Initialize module and dependencies

This commit is contained in:
dwrz
2026-02-13 14:59:42 +00:00
commit 0740968bca
390 changed files with 131652 additions and 0 deletions

16
vendor/golang.org/x/tools/go/ssa/TODO generated vendored Normal file
View File

@@ -0,0 +1,16 @@
-*- text -*-
SSA Generics to-do list
===========================
DOCUMENTATION:
- Read me for internals
TYPE PARAMETERIZED GENERIC FUNCTIONS:
- sanity.go updates.
- Check source functions going to generics.
- Tests, tests, tests...
USAGE:
- Back fill users for handling ssa.InstantiateGenerics being off.

113
vendor/golang.org/x/tools/go/ssa/block.go generated vendored Normal file
View File

@@ -0,0 +1,113 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
import "fmt"
// This file implements the BasicBlock type.
// addEdge adds a control-flow graph edge from from to to.
func addEdge(from, to *BasicBlock) {
from.Succs = append(from.Succs, to)
to.Preds = append(to.Preds, from)
}
// Parent returns the function that contains block b.
func (b *BasicBlock) Parent() *Function { return b.parent }
// String returns a human-readable label of this block.
// It is not guaranteed unique within the function.
func (b *BasicBlock) String() string {
return fmt.Sprintf("%d", b.Index)
}
// emit appends an instruction to the current basic block.
// If the instruction defines a Value, it is returned.
func (b *BasicBlock) emit(i Instruction) Value {
i.setBlock(b)
b.Instrs = append(b.Instrs, i)
v, _ := i.(Value)
return v
}
// predIndex returns the i such that b.Preds[i] == c or panics if
// there is none.
func (b *BasicBlock) predIndex(c *BasicBlock) int {
for i, pred := range b.Preds {
if pred == c {
return i
}
}
panic(fmt.Sprintf("no edge %s -> %s", c, b))
}
// hasPhi returns true if b.Instrs contains φ-nodes.
func (b *BasicBlock) hasPhi() bool {
_, ok := b.Instrs[0].(*Phi)
return ok
}
// phis returns the prefix of b.Instrs containing all the block's φ-nodes.
func (b *BasicBlock) phis() []Instruction {
for i, instr := range b.Instrs {
if _, ok := instr.(*Phi); !ok {
return b.Instrs[:i]
}
}
return nil // unreachable in well-formed blocks
}
// replacePred replaces all occurrences of p in b's predecessor list with q.
// Ordinarily there should be at most one.
func (b *BasicBlock) replacePred(p, q *BasicBlock) {
for i, pred := range b.Preds {
if pred == p {
b.Preds[i] = q
}
}
}
// replaceSucc replaces all occurrences of p in b's successor list with q.
// Ordinarily there should be at most one.
func (b *BasicBlock) replaceSucc(p, q *BasicBlock) {
for i, succ := range b.Succs {
if succ == p {
b.Succs[i] = q
}
}
}
// removePred removes all occurrences of p in b's
// predecessor list and φ-nodes.
// Ordinarily there should be at most one.
func (b *BasicBlock) removePred(p *BasicBlock) {
phis := b.phis()
// We must preserve edge order for φ-nodes.
j := 0
for i, pred := range b.Preds {
if pred != p {
b.Preds[j] = b.Preds[i]
// Strike out φ-edge too.
for _, instr := range phis {
phi := instr.(*Phi)
phi.Edges[j] = phi.Edges[i]
}
j++
}
}
// Nil out b.Preds[j:] and φ-edges[j:] to aid GC.
for i := j; i < len(b.Preds); i++ {
b.Preds[i] = nil
for _, instr := range phis {
instr.(*Phi).Edges[i] = nil
}
}
b.Preds = b.Preds[:j]
for _, instr := range phis {
phi := instr.(*Phi)
phi.Edges = phi.Edges[:j]
}
}

183
vendor/golang.org/x/tools/go/ssa/blockopt.go generated vendored Normal file
View File

@@ -0,0 +1,183 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// Simple block optimizations to simplify the control flow graph.
// TODO(adonovan): opt: instead of creating several "unreachable" blocks
// per function in the Builder, reuse a single one (e.g. at Blocks[1])
// to reduce garbage.
import (
"fmt"
"os"
)
// If true, perform sanity checking and show progress at each
// successive iteration of optimizeBlocks. Very verbose.
const debugBlockOpt = false
// markReachable sets Index=-1 for all blocks reachable from b.
func markReachable(b *BasicBlock) {
b.Index = -1
for _, succ := range b.Succs {
if succ.Index == 0 {
markReachable(succ)
}
}
}
// deleteUnreachableBlocks marks all reachable blocks of f and
// eliminates (nils) all others, including possibly cyclic subgraphs.
func deleteUnreachableBlocks(f *Function) {
const white, black = 0, -1
// We borrow b.Index temporarily as the mark bit.
for _, b := range f.Blocks {
b.Index = white
}
markReachable(f.Blocks[0])
if f.Recover != nil {
markReachable(f.Recover)
}
for i, b := range f.Blocks {
if b.Index == white {
for _, c := range b.Succs {
if c.Index == black {
c.removePred(b) // delete white->black edge
}
}
if debugBlockOpt {
fmt.Fprintln(os.Stderr, "unreachable", b)
}
f.Blocks[i] = nil // delete b
}
}
f.removeNilBlocks()
}
// jumpThreading attempts to apply simple jump-threading to block b,
// in which a->b->c become a->c if b is just a Jump.
// The result is true if the optimization was applied.
func jumpThreading(f *Function, b *BasicBlock) bool {
if b.Index == 0 {
return false // don't apply to entry block
}
if b.Instrs == nil {
return false
}
if _, ok := b.Instrs[0].(*Jump); !ok {
return false // not just a jump
}
c := b.Succs[0]
if c == b {
return false // don't apply to degenerate jump-to-self.
}
if c.hasPhi() {
return false // not sound without more effort
}
for j, a := range b.Preds {
a.replaceSucc(b, c)
// If a now has two edges to c, replace its degenerate If by Jump.
if len(a.Succs) == 2 && a.Succs[0] == c && a.Succs[1] == c {
jump := new(Jump)
jump.setBlock(a)
a.Instrs[len(a.Instrs)-1] = jump
a.Succs = a.Succs[:1]
c.removePred(b)
} else {
if j == 0 {
c.replacePred(b, a)
} else {
c.Preds = append(c.Preds, a)
}
}
if debugBlockOpt {
fmt.Fprintln(os.Stderr, "jumpThreading", a, b, c)
}
}
f.Blocks[b.Index] = nil // delete b
return true
}
// fuseBlocks attempts to apply the block fusion optimization to block
// a, in which a->b becomes ab if len(a.Succs)==len(b.Preds)==1.
// The result is true if the optimization was applied.
func fuseBlocks(f *Function, a *BasicBlock) bool {
if len(a.Succs) != 1 {
return false
}
b := a.Succs[0]
if len(b.Preds) != 1 {
return false
}
// Degenerate &&/|| ops may result in a straight-line CFG
// containing φ-nodes. (Ideally we'd replace such them with
// their sole operand but that requires Referrers, built later.)
if b.hasPhi() {
return false // not sound without further effort
}
// Eliminate jump at end of A, then copy all of B across.
a.Instrs = append(a.Instrs[:len(a.Instrs)-1], b.Instrs...)
for _, instr := range b.Instrs {
instr.setBlock(a)
}
// A inherits B's successors
a.Succs = append(a.succs2[:0], b.Succs...)
// Fix up Preds links of all successors of B.
for _, c := range b.Succs {
c.replacePred(b, a)
}
if debugBlockOpt {
fmt.Fprintln(os.Stderr, "fuseBlocks", a, b)
}
f.Blocks[b.Index] = nil // delete b
return true
}
// optimizeBlocks() performs some simple block optimizations on a
// completed function: dead block elimination, block fusion, jump
// threading.
func optimizeBlocks(f *Function) {
deleteUnreachableBlocks(f)
// Loop until no further progress.
changed := true
for changed {
changed = false
if debugBlockOpt {
f.WriteTo(os.Stderr)
mustSanityCheck(f, nil)
}
for _, b := range f.Blocks {
// f.Blocks will temporarily contain nils to indicate
// deleted blocks; we remove them at the end.
if b == nil {
continue
}
// Fuse blocks. b->c becomes bc.
if fuseBlocks(f, b) {
changed = true
}
// a->b->c becomes a->c if b contains only a Jump.
if jumpThreading(f, b) {
changed = true
continue // (b was disconnected)
}
}
}
f.removeNilBlocks()
}

3292
vendor/golang.org/x/tools/go/ssa/builder.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

193
vendor/golang.org/x/tools/go/ssa/const.go generated vendored Normal file
View File

@@ -0,0 +1,193 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// This file defines the Const SSA value type.
import (
"fmt"
"go/constant"
"go/token"
"go/types"
"strconv"
"golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/internal/typesinternal"
)
// NewConst returns a new constant of the specified value and type.
// val must be valid according to the specification of Const.Value.
func NewConst(val constant.Value, typ types.Type) *Const {
if val == nil {
switch soleTypeKind(typ) {
case types.IsBoolean:
val = constant.MakeBool(false)
case types.IsInteger:
val = constant.MakeInt64(0)
case types.IsString:
val = constant.MakeString("")
}
}
return &Const{typ, val}
}
// soleTypeKind returns a BasicInfo for which constant.Value can
// represent all zero values for the types in the type set.
//
// types.IsBoolean for false is a representative.
// types.IsInteger for 0
// types.IsString for ""
// 0 otherwise.
func soleTypeKind(typ types.Type) types.BasicInfo {
// State records the set of possible zero values (false, 0, "").
// Candidates (perhaps all) are eliminated during the type-set
// iteration, which executes at least once.
state := types.IsBoolean | types.IsInteger | types.IsString
underIs(typ, func(ut types.Type) bool {
var c types.BasicInfo
if t, ok := ut.(*types.Basic); ok {
c = t.Info()
}
if c&types.IsNumeric != 0 { // int/float/complex
c = types.IsInteger
}
state = state & c
return state != 0
})
return state
}
// intConst returns an 'int' constant that evaluates to i.
// (i is an int64 in case the host is narrower than the target.)
func intConst(i int64) *Const {
return NewConst(constant.MakeInt64(i), tInt)
}
// stringConst returns a 'string' constant that evaluates to s.
func stringConst(s string) *Const {
return NewConst(constant.MakeString(s), tString)
}
// zeroConst returns a new "zero" constant of the specified type.
func zeroConst(t types.Type) *Const {
return NewConst(nil, t)
}
func (c *Const) RelString(from *types.Package) string {
var s string
if c.Value == nil {
s, _ = typesinternal.ZeroString(c.typ, types.RelativeTo(from))
} else if c.Value.Kind() == constant.String {
s = constant.StringVal(c.Value)
const max = 20
// TODO(adonovan): don't cut a rune in half.
if len(s) > max {
s = s[:max-3] + "..." // abbreviate
}
s = strconv.Quote(s)
} else {
s = c.Value.String()
}
return s + ":" + relType(c.Type(), from)
}
func (c *Const) Name() string {
return c.RelString(nil)
}
func (c *Const) String() string {
return c.Name()
}
func (c *Const) Type() types.Type {
return c.typ
}
func (c *Const) Referrers() *[]Instruction {
return nil
}
func (c *Const) Parent() *Function { return nil }
func (c *Const) Pos() token.Pos {
return token.NoPos
}
// IsNil returns true if this constant is a nil value of
// a nillable reference type (pointer, slice, channel, map, or function),
// a basic interface type, or
// a type parameter all of whose possible instantiations are themselves nillable.
func (c *Const) IsNil() bool {
return c.Value == nil && nillable(c.typ)
}
// nillable reports whether *new(T) == nil is legal for type T.
func nillable(t types.Type) bool {
if typeparams.IsTypeParam(t) {
return underIs(t, func(u types.Type) bool {
// empty type set (u==nil) => any underlying types => not nillable
return u != nil && nillable(u)
})
}
switch t.Underlying().(type) {
case *types.Pointer, *types.Slice, *types.Chan, *types.Map, *types.Signature:
return true
case *types.Interface:
return true // basic interface.
default:
return false
}
}
// TODO(adonovan): move everything below into golang.org/x/tools/go/ssa/interp.
// Int64 returns the numeric value of this constant truncated to fit
// a signed 64-bit integer.
func (c *Const) Int64() int64 {
switch x := constant.ToInt(c.Value); x.Kind() {
case constant.Int:
if i, ok := constant.Int64Val(x); ok {
return i
}
return 0
case constant.Float:
f, _ := constant.Float64Val(x)
return int64(f)
}
panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
}
// Uint64 returns the numeric value of this constant truncated to fit
// an unsigned 64-bit integer.
func (c *Const) Uint64() uint64 {
switch x := constant.ToInt(c.Value); x.Kind() {
case constant.Int:
if u, ok := constant.Uint64Val(x); ok {
return u
}
return 0
case constant.Float:
f, _ := constant.Float64Val(x)
return uint64(f)
}
panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
}
// Float64 returns the numeric value of this constant truncated to fit
// a float64.
func (c *Const) Float64() float64 {
x := constant.ToFloat(c.Value) // (c.Value == nil) => x.Kind() == Unknown
f, _ := constant.Float64Val(x)
return f
}
// Complex128 returns the complex value of this constant truncated to
// fit a complex128.
func (c *Const) Complex128() complex128 {
x := constant.ToComplex(c.Value) // (c.Value == nil) => x.Kind() == Unknown
re, _ := constant.Float64Val(constant.Real(x))
im, _ := constant.Float64Val(constant.Imag(x))
return complex(re, im)
}

325
vendor/golang.org/x/tools/go/ssa/create.go generated vendored Normal file
View File

@@ -0,0 +1,325 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// This file implements the CREATE phase of SSA construction.
// See builder.go for explanation.
import (
"fmt"
"go/ast"
"go/token"
"go/types"
"os"
"sync"
"golang.org/x/tools/internal/versions"
)
// NewProgram returns a new SSA Program.
//
// mode controls diagnostics and checking during SSA construction.
//
// To construct an SSA program:
//
// - Call NewProgram to create an empty Program.
// - Call CreatePackage providing typed syntax for each package
// you want to build, and call it with types but not
// syntax for each of those package's direct dependencies.
// - Call [Package.Build] on each syntax package you wish to build,
// or [Program.Build] to build all of them.
//
// See the Example tests for simple examples.
func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
return &Program{
Fset: fset,
imported: make(map[string]*Package),
packages: make(map[*types.Package]*Package),
mode: mode,
canon: newCanonizer(),
ctxt: types.NewContext(),
}
}
// memberFromObject populates package pkg with a member for the
// typechecker object obj.
//
// For objects from Go source code, syntax is the associated syntax
// tree (for funcs and vars only) and goversion defines the
// appropriate interpretation; they will be used during the build
// phase.
func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node, goversion string) {
name := obj.Name()
switch obj := obj.(type) {
case *types.Builtin:
if pkg.Pkg != types.Unsafe {
panic("unexpected builtin object: " + obj.String())
}
case *types.TypeName:
if name != "_" {
pkg.Members[name] = &Type{
object: obj,
pkg: pkg,
}
}
case *types.Const:
c := &NamedConst{
object: obj,
Value: NewConst(obj.Val(), obj.Type()),
pkg: pkg,
}
pkg.objects[obj] = c
if name != "_" {
pkg.Members[name] = c
}
case *types.Var:
g := &Global{
Pkg: pkg,
name: name,
object: obj,
typ: types.NewPointer(obj.Type()), // address
pos: obj.Pos(),
}
pkg.objects[obj] = g
if name != "_" {
pkg.Members[name] = g
}
case *types.Func:
sig := obj.Type().(*types.Signature)
if sig.Recv() == nil && name == "init" {
pkg.ninit++
name = fmt.Sprintf("init#%d", pkg.ninit)
}
fn := createFunction(pkg.Prog, obj, name, syntax, pkg.info, goversion)
fn.Pkg = pkg
pkg.created = append(pkg.created, fn)
pkg.objects[obj] = fn
if name != "_" && sig.Recv() == nil {
pkg.Members[name] = fn // package-level function
}
default: // (incl. *types.Package)
panic("unexpected Object type: " + obj.String())
}
}
// createFunction creates a function or method. It supports both
// CreatePackage (with or without syntax) and the on-demand creation
// of methods in non-created packages based on their types.Func.
func createFunction(prog *Program, obj *types.Func, name string, syntax ast.Node, info *types.Info, goversion string) *Function {
sig := obj.Type().(*types.Signature)
// Collect type parameters.
var tparams *types.TypeParamList
if rtparams := sig.RecvTypeParams(); rtparams.Len() > 0 {
tparams = rtparams // method of generic type
} else if sigparams := sig.TypeParams(); sigparams.Len() > 0 {
tparams = sigparams // generic function
}
/* declared function/method (from syntax or export data) */
fn := &Function{
name: name,
object: obj,
Signature: sig,
build: (*builder).buildFromSyntax,
syntax: syntax,
info: info,
goversion: goversion,
pos: obj.Pos(),
Pkg: nil, // may be set by caller
Prog: prog,
typeparams: tparams,
}
if fn.syntax == nil {
fn.Synthetic = "from type information"
fn.build = (*builder).buildParamsOnly
}
if tparams.Len() > 0 {
fn.generic = new(generic)
}
return fn
}
// membersFromDecl populates package pkg with members for each
// typechecker object (var, func, const or type) associated with the
// specified decl.
func membersFromDecl(pkg *Package, decl ast.Decl, goversion string) {
switch decl := decl.(type) {
case *ast.GenDecl: // import, const, type or var
switch decl.Tok {
case token.CONST:
for _, spec := range decl.Specs {
for _, id := range spec.(*ast.ValueSpec).Names {
memberFromObject(pkg, pkg.info.Defs[id], nil, "")
}
}
case token.VAR:
for _, spec := range decl.Specs {
for _, rhs := range spec.(*ast.ValueSpec).Values {
pkg.initVersion[rhs] = goversion
}
for _, id := range spec.(*ast.ValueSpec).Names {
memberFromObject(pkg, pkg.info.Defs[id], spec, goversion)
}
}
case token.TYPE:
for _, spec := range decl.Specs {
id := spec.(*ast.TypeSpec).Name
memberFromObject(pkg, pkg.info.Defs[id], nil, "")
}
}
case *ast.FuncDecl:
id := decl.Name
memberFromObject(pkg, pkg.info.Defs[id], decl, goversion)
}
}
// CreatePackage creates and returns an SSA Package from the
// specified type-checked, error-free file ASTs, and populates its
// Members mapping.
//
// importable determines whether this package should be returned by a
// subsequent call to ImportedPackage(pkg.Path()).
//
// The real work of building SSA form for each function is not done
// until a subsequent call to Package.Build.
func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package {
if pkg == nil {
panic("nil pkg") // otherwise pkg.Scope below returns types.Universe!
}
p := &Package{
Prog: prog,
Members: make(map[string]Member),
objects: make(map[types.Object]Member),
Pkg: pkg,
syntax: info != nil,
// transient values (cleared after Package.Build)
info: info,
files: files,
initVersion: make(map[ast.Expr]string),
}
/* synthesized package initializer */
p.init = &Function{
name: "init",
Signature: new(types.Signature),
Synthetic: "package initializer",
Pkg: p,
Prog: prog,
build: (*builder).buildPackageInit,
info: p.info,
goversion: "", // See Package.build for details.
}
p.Members[p.init.name] = p.init
p.created = append(p.created, p.init)
// Allocate all package members: vars, funcs, consts and types.
if len(files) > 0 {
// Go source package.
for _, file := range files {
goversion := versions.Lang(versions.FileVersion(p.info, file))
for _, decl := range file.Decls {
membersFromDecl(p, decl, goversion)
}
}
} else {
// GC-compiled binary package (or "unsafe")
// No code.
// No position information.
scope := p.Pkg.Scope()
for _, name := range scope.Names() {
obj := scope.Lookup(name)
memberFromObject(p, obj, nil, "")
if obj, ok := obj.(*types.TypeName); ok {
// No Unalias: aliases should not duplicate methods.
if named, ok := obj.Type().(*types.Named); ok {
for i, n := 0, named.NumMethods(); i < n; i++ {
memberFromObject(p, named.Method(i), nil, "")
}
}
}
}
}
if prog.mode&BareInits == 0 {
// Add initializer guard variable.
initguard := &Global{
Pkg: p,
name: "init$guard",
typ: types.NewPointer(tBool),
}
p.Members[initguard.Name()] = initguard
}
if prog.mode&GlobalDebug != 0 {
p.SetDebugMode(true)
}
if prog.mode&PrintPackages != 0 {
printMu.Lock()
p.WriteTo(os.Stdout)
printMu.Unlock()
}
if importable {
prog.imported[p.Pkg.Path()] = p
}
prog.packages[p.Pkg] = p
return p
}
// printMu serializes printing of Packages/Functions to stdout.
var printMu sync.Mutex
// AllPackages returns a new slice containing all packages created by
// prog.CreatePackage in unspecified order.
func (prog *Program) AllPackages() []*Package {
pkgs := make([]*Package, 0, len(prog.packages))
for _, pkg := range prog.packages {
pkgs = append(pkgs, pkg)
}
return pkgs
}
// ImportedPackage returns the importable Package whose PkgPath
// is path, or nil if no such Package has been created.
//
// A parameter to CreatePackage determines whether a package should be
// considered importable. For example, no import declaration can resolve
// to the ad-hoc main package created by 'go build foo.go'.
//
// TODO(adonovan): rethink this function and the "importable" concept;
// most packages are importable. This function assumes that all
// types.Package.Path values are unique within the ssa.Program, which is
// false---yet this function remains very convenient.
// Clients should use (*Program).Package instead where possible.
// SSA doesn't really need a string-keyed map of packages.
//
// Furthermore, the graph of packages may contain multiple variants
// (e.g. "p" vs "p as compiled for q.test"), and each has a different
// view of its dependencies.
func (prog *Program) ImportedPackage(path string) *Package {
return prog.imported[path]
}
// SetNoReturn sets the predicate used when building the ssa.Program
// prog that reports whether a given function cannot return.
// This may be used to prune spurious control flow edges
// after (e.g.) log.Fatal, improving the precision of analyses.
//
// A typical implementation is the [ctrlflow.CFGs.NoReturn] method from
// [golang.org/x/tools/go/analysis/passes/ctrlflow].
func (prog *Program) SetNoReturn(noReturn func(*types.Func) bool) {
prog.noReturn = noReturn
}

122
vendor/golang.org/x/tools/go/ssa/doc.go generated vendored Normal file
View File

@@ -0,0 +1,122 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package ssa defines a representation of the elements of Go programs
// (packages, types, functions, variables and constants) using a
// static single-assignment (SSA) form intermediate representation
// (IR) for the bodies of functions.
//
// For an introduction to SSA form, see
// http://en.wikipedia.org/wiki/Static_single_assignment_form.
// This page provides a broader reading list:
// http://www.dcs.gla.ac.uk/~jsinger/ssa.html.
//
// The level of abstraction of the SSA form is intentionally close to
// the source language to facilitate construction of source analysis
// tools. It is not intended for machine code generation.
//
// All looping, branching and switching constructs are replaced with
// unstructured control flow. Higher-level control flow constructs
// such as multi-way branch can be reconstructed as needed; see
// [golang.org/x/tools/go/ssa/ssautil.Switches] for an example.
//
// The simplest way to create the SSA representation of a package is
// to load typed syntax trees using [golang.org/x/tools/go/packages], then
// invoke the [golang.org/x/tools/go/ssa/ssautil.Packages] helper function.
// (See the package-level Examples named LoadPackages and LoadWholeProgram.)
// The resulting [ssa.Program] contains all the packages and their
// members, but SSA code is not created for function bodies until a
// subsequent call to [Package.Build] or [Program.Build].
//
// The builder initially builds a naive SSA form in which all local
// variables are addresses of stack locations with explicit loads and
// stores. Registerisation of eligible locals and φ-node insertion
// using dominance and dataflow are then performed as a second pass
// called "lifting" to improve the accuracy and performance of
// subsequent analyses; this pass can be skipped by setting the
// NaiveForm builder flag.
//
// The primary interfaces of this package are:
//
// - [Member]: a named member of a Go package.
// - [Value]: an expression that yields a value.
// - [Instruction]: a statement that consumes values and performs computation.
// - [Node]: a [Value] or [Instruction] (emphasizing its membership in the SSA value graph)
//
// A computation that yields a result implements both the [Value] and
// [Instruction] interfaces. The following table shows for each
// concrete type which of these interfaces it implements.
//
// Value? Instruction? Member?
// *Alloc ✔ ✔
// *BinOp ✔ ✔
// *Builtin ✔
// *Call ✔ ✔
// *ChangeInterface ✔ ✔
// *ChangeType ✔ ✔
// *Const ✔
// *Convert ✔ ✔
// *DebugRef ✔
// *Defer ✔
// *Extract ✔ ✔
// *Field ✔ ✔
// *FieldAddr ✔ ✔
// *FreeVar ✔
// *Function ✔ ✔ (func)
// *Global ✔ ✔ (var)
// *Go ✔
// *If ✔
// *Index ✔ ✔
// *IndexAddr ✔ ✔
// *Jump ✔
// *Lookup ✔ ✔
// *MakeChan ✔ ✔
// *MakeClosure ✔ ✔
// *MakeInterface ✔ ✔
// *MakeMap ✔ ✔
// *MakeSlice ✔ ✔
// *MapUpdate ✔
// *MultiConvert ✔ ✔
// *NamedConst ✔ (const)
// *Next ✔ ✔
// *Panic ✔
// *Parameter ✔
// *Phi ✔ ✔
// *Range ✔ ✔
// *Return ✔
// *RunDefers ✔
// *Select ✔ ✔
// *Send ✔
// *Slice ✔ ✔
// *SliceToArrayPointer ✔ ✔
// *Store ✔
// *Type ✔ (type)
// *TypeAssert ✔ ✔
// *UnOp ✔ ✔
//
// Other key types in this package include: [Program], [Package], [Function]
// and [BasicBlock].
//
// The program representation constructed by this package is fully
// resolved internally, i.e. it does not rely on the names of Values,
// Packages, Functions, Types or BasicBlocks for the correct
// interpretation of the program. Only the identities of objects and
// the topology of the SSA and type graphs are semantically
// significant. (There is one exception: [types.Id] values, which identify field
// and method names, contain strings.) Avoidance of name-based
// operations simplifies the implementation of subsequent passes and
// can make them very efficient. Many objects are nonetheless named
// to aid in debugging, but it is not essential that the names be
// either accurate or unambiguous. The public API exposes a number of
// name-based maps for client convenience.
//
// The [golang.org/x/tools/go/ssa/ssautil] package provides various
// helper functions, for example to simplify loading a Go program into
// SSA form.
//
// TODO(adonovan): write a how-to document for all the various cases
// of trying to determine corresponding elements across the four
// domains of source locations, ast.Nodes, types.Objects,
// ssa.Values/Instructions.
package ssa // import "golang.org/x/tools/go/ssa"

342
vendor/golang.org/x/tools/go/ssa/dom.go generated vendored Normal file
View File

@@ -0,0 +1,342 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// This file defines algorithms related to dominance.
// Dominator tree construction ----------------------------------------
//
// We use the algorithm described in Lengauer & Tarjan. 1979. A fast
// algorithm for finding dominators in a flowgraph.
// http://doi.acm.org/10.1145/357062.357071
//
// We also apply the optimizations to SLT described in Georgiadis et
// al, Finding Dominators in Practice, JGAA 2006,
// http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf
// to avoid the need for buckets of size > 1.
import (
"bytes"
"fmt"
"math/big"
"os"
"slices"
"sort"
)
// Idom returns the block that immediately dominates b:
// its parent in the dominator tree, if any.
// Neither the entry node (b.Index==0) nor recover node
// (b==b.Parent().Recover()) have a parent.
func (b *BasicBlock) Idom() *BasicBlock { return b.dom.idom }
// Dominees returns the list of blocks that b immediately dominates:
// its children in the dominator tree.
func (b *BasicBlock) Dominees() []*BasicBlock { return b.dom.children }
// Dominates reports whether b dominates c.
func (b *BasicBlock) Dominates(c *BasicBlock) bool {
return b.dom.pre <= c.dom.pre && c.dom.post <= b.dom.post
}
// DomPreorder returns a new slice containing the blocks of f
// in a preorder traversal of the dominator tree.
func (f *Function) DomPreorder() []*BasicBlock {
slice := slices.Clone(f.Blocks)
sort.Slice(slice, func(i, j int) bool {
return slice[i].dom.pre < slice[j].dom.pre
})
return slice
}
// DomPostorder returns a new slice containing the blocks of f
// in a postorder traversal of the dominator tree.
// (This is not the same as a postdominance order.)
func (f *Function) DomPostorder() []*BasicBlock {
slice := slices.Clone(f.Blocks)
sort.Slice(slice, func(i, j int) bool {
return slice[i].dom.post < slice[j].dom.post
})
return slice
}
// domInfo contains a BasicBlock's dominance information.
type domInfo struct {
idom *BasicBlock // immediate dominator (parent in domtree)
children []*BasicBlock // nodes immediately dominated by this one
pre, post int32 // pre- and post-order numbering within domtree
}
// ltState holds the working state for Lengauer-Tarjan algorithm
// (during which domInfo.pre is repurposed for CFG DFS preorder number).
type ltState struct {
// Each slice is indexed by b.Index.
sdom []*BasicBlock // b's semidominator
parent []*BasicBlock // b's parent in DFS traversal of CFG
ancestor []*BasicBlock // b's ancestor with least sdom
}
// dfs implements the depth-first search part of the LT algorithm.
func (lt *ltState) dfs(v *BasicBlock, i int32, preorder []*BasicBlock) int32 {
preorder[i] = v
v.dom.pre = i // For now: DFS preorder of spanning tree of CFG
i++
lt.sdom[v.Index] = v
lt.link(nil, v)
for _, w := range v.Succs {
if lt.sdom[w.Index] == nil {
lt.parent[w.Index] = v
i = lt.dfs(w, i, preorder)
}
}
return i
}
// eval implements the EVAL part of the LT algorithm.
func (lt *ltState) eval(v *BasicBlock) *BasicBlock {
// TODO(adonovan): opt: do path compression per simple LT.
u := v
for ; lt.ancestor[v.Index] != nil; v = lt.ancestor[v.Index] {
if lt.sdom[v.Index].dom.pre < lt.sdom[u.Index].dom.pre {
u = v
}
}
return u
}
// link implements the LINK part of the LT algorithm.
func (lt *ltState) link(v, w *BasicBlock) {
lt.ancestor[w.Index] = v
}
// buildDomTree computes the dominator tree of f using the LT algorithm.
// Precondition: all blocks are reachable (e.g. optimizeBlocks has been run).
func buildDomTree(f *Function) {
// The step numbers refer to the original LT paper; the
// reordering is due to Georgiadis.
// Clear any previous domInfo.
for _, b := range f.Blocks {
b.dom = domInfo{}
}
n := len(f.Blocks)
// Allocate space for 5 contiguous [n]*BasicBlock arrays:
// sdom, parent, ancestor, preorder, buckets.
space := make([]*BasicBlock, 5*n)
lt := ltState{
sdom: space[0:n],
parent: space[n : 2*n],
ancestor: space[2*n : 3*n],
}
// Step 1. Number vertices by depth-first preorder.
preorder := space[3*n : 4*n]
root := f.Blocks[0]
prenum := lt.dfs(root, 0, preorder)
recover := f.Recover
if recover != nil {
lt.dfs(recover, prenum, preorder)
}
buckets := space[4*n : 5*n]
copy(buckets, preorder)
// In reverse preorder...
for i := int32(n) - 1; i > 0; i-- {
w := preorder[i]
// Step 3. Implicitly define the immediate dominator of each node.
for v := buckets[i]; v != w; v = buckets[v.dom.pre] {
u := lt.eval(v)
if lt.sdom[u.Index].dom.pre < i {
v.dom.idom = u
} else {
v.dom.idom = w
}
}
// Step 2. Compute the semidominators of all nodes.
lt.sdom[w.Index] = lt.parent[w.Index]
for _, v := range w.Preds {
u := lt.eval(v)
if lt.sdom[u.Index].dom.pre < lt.sdom[w.Index].dom.pre {
lt.sdom[w.Index] = lt.sdom[u.Index]
}
}
lt.link(lt.parent[w.Index], w)
if lt.parent[w.Index] == lt.sdom[w.Index] {
w.dom.idom = lt.parent[w.Index]
} else {
buckets[i] = buckets[lt.sdom[w.Index].dom.pre]
buckets[lt.sdom[w.Index].dom.pre] = w
}
}
// The final 'Step 3' is now outside the loop.
for v := buckets[0]; v != root; v = buckets[v.dom.pre] {
v.dom.idom = root
}
// Step 4. Explicitly define the immediate dominator of each
// node, in preorder.
for _, w := range preorder[1:] {
if w == root || w == recover {
w.dom.idom = nil
} else {
if w.dom.idom != lt.sdom[w.Index] {
w.dom.idom = w.dom.idom.dom.idom
}
// Calculate Children relation as inverse of Idom.
w.dom.idom.dom.children = append(w.dom.idom.dom.children, w)
}
}
pre, post := numberDomTree(root, 0, 0)
if recover != nil {
numberDomTree(recover, pre, post)
}
// printDomTreeDot(os.Stderr, f) // debugging
// printDomTreeText(os.Stderr, root, 0) // debugging
if f.Prog.mode&SanityCheckFunctions != 0 {
sanityCheckDomTree(f)
}
}
// numberDomTree sets the pre- and post-order numbers of a depth-first
// traversal of the dominator tree rooted at v. These are used to
// answer dominance queries in constant time.
func numberDomTree(v *BasicBlock, pre, post int32) (int32, int32) {
v.dom.pre = pre
pre++
for _, child := range v.dom.children {
pre, post = numberDomTree(child, pre, post)
}
v.dom.post = post
post++
return pre, post
}
// Testing utilities ----------------------------------------
// sanityCheckDomTree checks the correctness of the dominator tree
// computed by the LT algorithm by comparing against the dominance
// relation computed by a naive Kildall-style forward dataflow
// analysis (Algorithm 10.16 from the "Dragon" book).
func sanityCheckDomTree(f *Function) {
n := len(f.Blocks)
// D[i] is the set of blocks that dominate f.Blocks[i],
// represented as a bit-set of block indices.
D := make([]big.Int, n)
one := big.NewInt(1)
// all is the set of all blocks; constant.
var all big.Int
all.Set(one).Lsh(&all, uint(n)).Sub(&all, one)
// Initialization.
for i, b := range f.Blocks {
if i == 0 || b == f.Recover {
// A root is dominated only by itself.
D[i].SetBit(&D[0], 0, 1)
} else {
// All other blocks are (initially) dominated
// by every block.
D[i].Set(&all)
}
}
// Iteration until fixed point.
for changed := true; changed; {
changed = false
for i, b := range f.Blocks {
if i == 0 || b == f.Recover {
continue
}
// Compute intersection across predecessors.
var x big.Int
x.Set(&all)
for _, pred := range b.Preds {
x.And(&x, &D[pred.Index])
}
x.SetBit(&x, i, 1) // a block always dominates itself.
if D[i].Cmp(&x) != 0 {
D[i].Set(&x)
changed = true
}
}
}
// Check the entire relation. O(n^2).
// The Recover block (if any) must be treated specially so we skip it.
ok := true
for i := range n {
for j := range n {
b, c := f.Blocks[i], f.Blocks[j]
if c == f.Recover {
continue
}
actual := b.Dominates(c)
expected := D[j].Bit(i) == 1
if actual != expected {
fmt.Fprintf(os.Stderr, "dominates(%s, %s)==%t, want %t\n", b, c, actual, expected)
ok = false
}
}
}
preorder := f.DomPreorder()
for _, b := range f.Blocks {
if got := preorder[b.dom.pre]; got != b {
fmt.Fprintf(os.Stderr, "preorder[%d]==%s, want %s\n", b.dom.pre, got, b)
ok = false
}
}
if !ok {
panic("sanityCheckDomTree failed for " + f.String())
}
}
// Printing functions ----------------------------------------
// printDomTreeText prints the dominator tree as text, using indentation.
func printDomTreeText(buf *bytes.Buffer, v *BasicBlock, indent int) {
fmt.Fprintf(buf, "%*s%s\n", 4*indent, "", v)
for _, child := range v.dom.children {
printDomTreeText(buf, child, indent+1)
}
}
// printDomTreeDot prints the dominator tree of f in AT&T GraphViz
// (.dot) format.
// (unused; retained for debugging)
func printDomTreeDot(buf *bytes.Buffer, f *Function) {
fmt.Fprintln(buf, "//", f)
fmt.Fprintln(buf, "digraph domtree {")
for i, b := range f.Blocks {
v := b.dom
fmt.Fprintf(buf, "\tn%d [label=\"%s (%d, %d)\",shape=\"rectangle\"];\n", v.pre, b, v.pre, v.post)
// TODO(adonovan): improve appearance of edges
// belonging to both dominator tree and CFG.
// Dominator tree edge.
if i != 0 {
fmt.Fprintf(buf, "\tn%d -> n%d [style=\"solid\",weight=100];\n", v.idom.dom.pre, v.pre)
}
// CFG edges.
for _, pred := range b.Preds {
fmt.Fprintf(buf, "\tn%d -> n%d [style=\"dotted\",weight=0];\n", pred.dom.pre, v.pre)
}
}
fmt.Fprintln(buf, "}")
}

629
vendor/golang.org/x/tools/go/ssa/emit.go generated vendored Normal file
View File

@@ -0,0 +1,629 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// Helpers for emitting SSA instructions.
import (
"fmt"
"go/ast"
"go/token"
"go/types"
"golang.org/x/tools/internal/typeparams"
)
// emitAlloc emits to f a new Alloc instruction allocating a variable
// of type typ.
//
// The caller must set Alloc.Heap=true (for a heap-allocated variable)
// or add the Alloc to f.Locals (for a frame-allocated variable).
//
// During building, a variable in f.Locals may have its Heap flag
// set when it is discovered that its address is taken.
// These Allocs are removed from f.Locals at the end.
//
// The builder should generally call one of the emit{New,Local,LocalVar} wrappers instead.
func emitAlloc(f *Function, typ types.Type, pos token.Pos, comment string) *Alloc {
v := &Alloc{Comment: comment}
v.setType(types.NewPointer(typ))
v.setPos(pos)
f.emit(v)
return v
}
// emitNew emits to f a new Alloc instruction heap-allocating a
// variable of type typ. pos is the optional source location.
func emitNew(f *Function, typ types.Type, pos token.Pos, comment string) *Alloc {
alloc := emitAlloc(f, typ, pos, comment)
alloc.Heap = true
return alloc
}
// emitLocal creates a local var for (t, pos, comment) and
// emits an Alloc instruction for it.
//
// (Use this function or emitNew for synthetic variables;
// for source-level variables in the same function, use emitLocalVar.)
func emitLocal(f *Function, t types.Type, pos token.Pos, comment string) *Alloc {
local := emitAlloc(f, t, pos, comment)
f.Locals = append(f.Locals, local)
return local
}
// emitLocalVar creates a local var for v and emits an Alloc instruction for it.
// Subsequent calls to f.lookup(v) return it.
// It applies the appropriate generic instantiation to the type.
func emitLocalVar(f *Function, v *types.Var) *Alloc {
alloc := emitLocal(f, f.typ(v.Type()), v.Pos(), v.Name())
f.vars[v] = alloc
return alloc
}
// emitLoad emits to f an instruction to load the address addr into a
// new temporary, and returns the value so defined.
func emitLoad(f *Function, addr Value) *UnOp {
v := &UnOp{Op: token.MUL, X: addr}
v.setType(typeparams.MustDeref(addr.Type()))
f.emit(v)
return v
}
// emitDebugRef emits to f a DebugRef pseudo-instruction associating
// expression e with value v.
func emitDebugRef(f *Function, e ast.Expr, v Value, isAddr bool) {
if !f.debugInfo() {
return // debugging not enabled
}
if v == nil || e == nil {
panic("nil")
}
var obj types.Object
e = ast.Unparen(e)
if id, ok := e.(*ast.Ident); ok {
if isBlankIdent(id) {
return
}
obj = f.objectOf(id)
switch obj.(type) {
case *types.Nil, *types.Const, *types.Builtin:
return
}
}
f.emit(&DebugRef{
X: v,
Expr: e,
IsAddr: isAddr,
object: obj,
})
}
// emitArith emits to f code to compute the binary operation op(x, y)
// where op is an eager shift, logical or arithmetic operation.
// (Use emitCompare() for comparisons and Builder.logicalBinop() for
// non-eager operations.)
func emitArith(f *Function, op token.Token, x, y Value, t types.Type, pos token.Pos) Value {
switch op {
case token.SHL, token.SHR:
x = emitConv(f, x, t)
// y may be signed or an 'untyped' constant.
// There is a runtime panic if y is signed and <0. Instead of inserting a check for y<0
// and converting to an unsigned value (like the compiler) leave y as is.
if isUntyped(y.Type().Underlying()) {
// Untyped conversion:
// Spec https://go.dev/ref/spec#Operators:
// The right operand in a shift expression must have integer type or be an untyped constant
// representable by a value of type uint.
y = emitConv(f, y, types.Typ[types.Uint])
}
case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
x = emitConv(f, x, t)
y = emitConv(f, y, t)
default:
panic("illegal op in emitArith: " + op.String())
}
v := &BinOp{
Op: op,
X: x,
Y: y,
}
v.setPos(pos)
v.setType(t)
return f.emit(v)
}
// emitCompare emits to f code compute the boolean result of
// comparison 'x op y'.
func emitCompare(f *Function, op token.Token, x, y Value, pos token.Pos) Value {
xt := x.Type().Underlying()
yt := y.Type().Underlying()
// Special case to optimise a tagless SwitchStmt so that
// these are equivalent
// switch { case e: ...}
// switch true { case e: ... }
// if e==true { ... }
// even in the case when e's type is an interface.
// TODO(adonovan): opt: generalise to x==true, false!=y, etc.
if x == vTrue && op == token.EQL {
if yt, ok := yt.(*types.Basic); ok && yt.Info()&types.IsBoolean != 0 {
return y
}
}
if types.Identical(xt, yt) {
// no conversion necessary
} else if isNonTypeParamInterface(x.Type()) {
y = emitConv(f, y, x.Type())
} else if isNonTypeParamInterface(y.Type()) {
x = emitConv(f, x, y.Type())
} else if _, ok := x.(*Const); ok {
x = emitConv(f, x, y.Type())
} else if _, ok := y.(*Const); ok {
y = emitConv(f, y, x.Type())
} else {
// other cases, e.g. channels. No-op.
}
v := &BinOp{
Op: op,
X: x,
Y: y,
}
v.setPos(pos)
v.setType(tBool)
return f.emit(v)
}
// isValuePreserving returns true if a conversion from ut_src to
// ut_dst is value-preserving, i.e. just a change of type.
// Precondition: neither argument is a named or alias type.
func isValuePreserving(ut_src, ut_dst types.Type) bool {
// Identical underlying types?
if types.IdenticalIgnoreTags(ut_dst, ut_src) {
return true
}
switch ut_dst.(type) {
case *types.Chan:
// Conversion between channel types?
_, ok := ut_src.(*types.Chan)
return ok
case *types.Pointer:
// Conversion between pointers with identical base types?
_, ok := ut_src.(*types.Pointer)
return ok
}
return false
}
// emitConv emits to f code to convert Value val to exactly type typ,
// and returns the converted value. Implicit conversions are required
// by language assignability rules in assignments, parameter passing,
// etc.
func emitConv(f *Function, val Value, typ types.Type) Value {
t_src := val.Type()
// Identical types? Conversion is a no-op.
if types.Identical(t_src, typ) {
return val
}
ut_dst := typ.Underlying()
ut_src := t_src.Underlying()
// Conversion to, or construction of a value of, an interface type?
if isNonTypeParamInterface(typ) {
// Interface name change?
if isValuePreserving(ut_src, ut_dst) {
c := &ChangeType{X: val}
c.setType(typ)
return f.emit(c)
}
// Assignment from one interface type to another?
if isNonTypeParamInterface(t_src) {
c := &ChangeInterface{X: val}
c.setType(typ)
return f.emit(c)
}
// Untyped nil constant? Return interface-typed nil constant.
if ut_src == tUntypedNil {
return zeroConst(typ)
}
// Convert (non-nil) "untyped" literals to their default type.
if t, ok := ut_src.(*types.Basic); ok && t.Info()&types.IsUntyped != 0 {
val = emitConv(f, val, types.Default(ut_src))
}
// Record the types of operands to MakeInterface, if
// non-parameterized, as they are the set of runtime types.
t := val.Type()
if f.typeparams.Len() == 0 || !f.Prog.isParameterized(t) {
addMakeInterfaceType(f.Prog, t)
}
mi := &MakeInterface{X: val}
mi.setType(typ)
return f.emit(mi)
}
// conversionCase describes an instruction pattern that maybe emitted to
// model d <- s for d in dst_terms and s in src_terms.
// Multiple conversions can match the same pattern.
type conversionCase uint8
const (
changeType conversionCase = 1 << iota
sliceToArray
sliceToArrayPtr
sliceTo0Array
sliceTo0ArrayPtr
convert
)
// classify the conversion case of a source type us to a destination type ud.
// us and ud are underlying types (not *Named or *Alias)
classify := func(us, ud types.Type) conversionCase {
// Just a change of type, but not value or representation?
if isValuePreserving(us, ud) {
return changeType
}
// Conversion from slice to array or slice to array pointer?
if slice, ok := us.(*types.Slice); ok {
var arr *types.Array
var ptr bool
// Conversion from slice to array pointer?
switch d := ud.(type) {
case *types.Array:
arr = d
case *types.Pointer:
arr, _ = d.Elem().Underlying().(*types.Array)
ptr = true
}
if arr != nil && types.Identical(slice.Elem(), arr.Elem()) {
if arr.Len() == 0 {
if ptr {
return sliceTo0ArrayPtr
} else {
return sliceTo0Array
}
}
if ptr {
return sliceToArrayPtr
} else {
return sliceToArray
}
}
}
// The only remaining case in well-typed code is a representation-
// changing conversion of basic types (possibly with []byte/[]rune).
if !isBasic(us) && !isBasic(ud) {
panic(fmt.Sprintf("in %s: cannot convert term %s (%s [within %s]) to type %s [within %s]", f, val, val.Type(), us, typ, ud))
}
return convert
}
var classifications conversionCase
underIs(ut_src, func(us types.Type) bool {
return underIs(ut_dst, func(ud types.Type) bool {
if us != nil && ud != nil {
classifications |= classify(us, ud)
}
return classifications != 0
})
})
if classifications == 0 {
panic(fmt.Sprintf("in %s: cannot convert %s (%s) to %s", f, val, val.Type(), typ))
}
// Conversion of a compile-time constant value?
if c, ok := val.(*Const); ok {
// Conversion to a basic type?
if isBasic(ut_dst) {
// Conversion of a compile-time constant to
// another constant type results in a new
// constant of the destination type and
// (initially) the same abstract value.
// We don't truncate the value yet.
return NewConst(c.Value, typ)
}
// Can we always convert from zero value without panicking?
const mayPanic = sliceToArray | sliceToArrayPtr
if c.Value == nil && classifications&mayPanic == 0 {
return NewConst(nil, typ)
}
// We're converting from constant to non-constant type,
// e.g. string -> []byte/[]rune.
}
switch classifications {
case changeType: // representation-preserving change
c := &ChangeType{X: val}
c.setType(typ)
return f.emit(c)
case sliceToArrayPtr, sliceTo0ArrayPtr: // slice to array pointer
c := &SliceToArrayPointer{X: val}
c.setType(typ)
return f.emit(c)
case sliceToArray: // slice to arrays (not zero-length)
ptype := types.NewPointer(typ)
p := &SliceToArrayPointer{X: val}
p.setType(ptype)
x := f.emit(p)
unOp := &UnOp{Op: token.MUL, X: x}
unOp.setType(typ)
return f.emit(unOp)
case sliceTo0Array: // slice to zero-length arrays (constant)
return zeroConst(typ)
case convert: // representation-changing conversion
c := &Convert{X: val}
c.setType(typ)
return f.emit(c)
default: // The conversion represents a cross product.
c := &MultiConvert{X: val, from: t_src, to: typ}
c.setType(typ)
return f.emit(c)
}
}
// emitTypeCoercion emits to f code to coerce the type of a
// Value v to exactly type typ, and returns the coerced value.
//
// Requires that coercing v.Typ() to typ is a value preserving change.
//
// Currently used only when v.Type() is a type instance of typ or vice versa.
// A type v is a type instance of a type t if there exists a
// type parameter substitution σ s.t. σ(v) == t. Example:
//
// σ(func(T) T) == func(int) int for σ == [T ↦ int]
//
// This happens in instantiation wrappers for conversion
// from an instantiation to a parameterized type (and vice versa)
// with σ substituting f.typeparams by f.typeargs.
func emitTypeCoercion(f *Function, v Value, typ types.Type) Value {
if types.Identical(v.Type(), typ) {
return v // no coercion needed
}
// TODO(taking): for instances should we record which side is the instance?
c := &ChangeType{
X: v,
}
c.setType(typ)
f.emit(c)
return c
}
// emitStore emits to f an instruction to store value val at location
// addr, applying implicit conversions as required by assignability rules.
func emitStore(f *Function, addr, val Value, pos token.Pos) *Store {
typ := typeparams.MustDeref(addr.Type())
s := &Store{
Addr: addr,
Val: emitConv(f, val, typ),
pos: pos,
}
f.emit(s)
return s
}
// emitJump emits to f a jump to target, and updates the control-flow graph.
// Postcondition: f.currentBlock is nil.
func emitJump(f *Function, target *BasicBlock) {
b := f.currentBlock
b.emit(new(Jump))
addEdge(b, target)
f.currentBlock = nil
}
// emitIf emits to f a conditional jump to tblock or fblock based on
// cond, and updates the control-flow graph.
// Postcondition: f.currentBlock is nil.
func emitIf(f *Function, cond Value, tblock, fblock *BasicBlock) {
b := f.currentBlock
b.emit(&If{Cond: cond})
addEdge(b, tblock)
addEdge(b, fblock)
f.currentBlock = nil
}
// emitExtract emits to f an instruction to extract the index'th
// component of tuple. It returns the extracted value.
func emitExtract(f *Function, tuple Value, index int) Value {
e := &Extract{Tuple: tuple, Index: index}
e.setType(tuple.Type().(*types.Tuple).At(index).Type())
return f.emit(e)
}
// emitTypeAssert emits to f a type assertion value := x.(t) and
// returns the value. x.Type() must be an interface.
func emitTypeAssert(f *Function, x Value, t types.Type, pos token.Pos) Value {
a := &TypeAssert{X: x, AssertedType: t}
a.setPos(pos)
a.setType(t)
return f.emit(a)
}
// emitTypeTest emits to f a type test value,ok := x.(t) and returns
// a (value, ok) tuple. x.Type() must be an interface.
func emitTypeTest(f *Function, x Value, t types.Type, pos token.Pos) Value {
a := &TypeAssert{
X: x,
AssertedType: t,
CommaOk: true,
}
a.setPos(pos)
a.setType(types.NewTuple(
newVar("value", t),
varOk,
))
return f.emit(a)
}
// emitTailCall emits to f a function call in tail position. The
// caller is responsible for all fields of 'call' except its type.
// Intended for wrapper methods.
// Precondition: f does/will not use deferred procedure calls.
// Postcondition: f.currentBlock is nil.
func emitTailCall(f *Function, call *Call) {
tresults := f.Signature.Results()
nr := tresults.Len()
if nr == 1 {
call.typ = tresults.At(0).Type()
} else {
call.typ = tresults
}
tuple := emitCall(f, call)
var ret Return
switch nr {
case 0:
// no-op
case 1:
ret.Results = []Value{tuple}
default:
for i := range nr {
v := emitExtract(f, tuple, i)
// TODO(adonovan): in principle, this is required:
// v = emitConv(f, o.Type, f.Signature.Results[i].Type)
// but in practice emitTailCall is only used when
// the types exactly match.
ret.Results = append(ret.Results, v)
}
}
f.emit(&ret)
f.currentBlock = nil
}
// emitCall emits a call instruction. If the callee is "no return",
// it also emits a panic to eliminate infeasible CFG edges.
func emitCall(fn *Function, call *Call) Value {
res := fn.emit(call)
callee := call.Call.StaticCallee()
if callee != nil &&
callee.object != nil &&
fn.Prog.noReturn != nil &&
fn.Prog.noReturn(callee.object) {
// Call cannot return. Insert a panic after it.
fn.emit(&Panic{
X: emitConv(fn, vNoReturn, tEface),
pos: call.Pos(),
})
fn.currentBlock = fn.newBasicBlock("unreachable.noreturn")
}
return res
}
// emitImplicitSelections emits to f code to apply the sequence of
// implicit field selections specified by indices to base value v, and
// returns the selected value.
//
// If v is the address of a struct, the result will be the address of
// a field; if it is the value of a struct, the result will be the
// value of a field.
func emitImplicitSelections(f *Function, v Value, indices []int, pos token.Pos) Value {
for _, index := range indices {
if isPointerCore(v.Type()) {
fld := fieldOf(typeparams.MustDeref(v.Type()), index)
instr := &FieldAddr{
X: v,
Field: index,
}
instr.setPos(pos)
instr.setType(types.NewPointer(fld.Type()))
v = f.emit(instr)
// Load the field's value iff indirectly embedded.
if isPointerCore(fld.Type()) {
v = emitLoad(f, v)
}
} else {
fld := fieldOf(v.Type(), index)
instr := &Field{
X: v,
Field: index,
}
instr.setPos(pos)
instr.setType(fld.Type())
v = f.emit(instr)
}
}
return v
}
// emitFieldSelection emits to f code to select the index'th field of v.
//
// If wantAddr, the input must be a pointer-to-struct and the result
// will be the field's address; otherwise the result will be the
// field's value.
// Ident id is used for position and debug info.
func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.Ident) Value {
if isPointerCore(v.Type()) {
fld := fieldOf(typeparams.MustDeref(v.Type()), index)
instr := &FieldAddr{
X: v,
Field: index,
}
instr.setPos(id.Pos())
instr.setType(types.NewPointer(fld.Type()))
v = f.emit(instr)
// Load the field's value iff we don't want its address.
if !wantAddr {
v = emitLoad(f, v)
}
} else {
fld := fieldOf(v.Type(), index)
instr := &Field{
X: v,
Field: index,
}
instr.setPos(id.Pos())
instr.setType(fld.Type())
v = f.emit(instr)
}
emitDebugRef(f, id, v, wantAddr)
return v
}
// createRecoverBlock emits to f a block of code to return after a
// recovered panic, and sets f.Recover to it.
//
// If f's result parameters are named, the code loads and returns
// their current values, otherwise it returns the zero values of their
// type.
//
// Idempotent.
func createRecoverBlock(f *Function) {
if f.Recover != nil {
return // already created
}
saved := f.currentBlock
f.Recover = f.newBasicBlock("recover")
f.currentBlock = f.Recover
var results []Value
// Reload NRPs to form value tuple.
for _, nr := range f.results {
results = append(results, emitLoad(f, nr))
}
f.emit(&Return{Results: results})
f.currentBlock = saved
}

836
vendor/golang.org/x/tools/go/ssa/func.go generated vendored Normal file
View File

@@ -0,0 +1,836 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// This file implements the Function type.
import (
"bytes"
"fmt"
"go/ast"
"go/token"
"go/types"
"io"
"iter"
"os"
"strings"
"golang.org/x/tools/internal/typeparams"
)
// Like ObjectOf, but panics instead of returning nil.
// Only valid during f's create and build phases.
func (f *Function) objectOf(id *ast.Ident) types.Object {
if o := f.info.ObjectOf(id); o != nil {
return o
}
panic(fmt.Sprintf("no types.Object for ast.Ident %s @ %s",
id.Name, f.Prog.Fset.Position(id.Pos())))
}
// Like TypeOf, but panics instead of returning nil.
// Only valid during f's create and build phases.
func (f *Function) typeOf(e ast.Expr) types.Type {
if T := f.info.TypeOf(e); T != nil {
return f.typ(T)
}
panic(fmt.Sprintf("no type for %T @ %s", e, f.Prog.Fset.Position(e.Pos())))
}
// typ is the locally instantiated type of T.
// If f is not an instantiation, then f.typ(T)==T.
func (f *Function) typ(T types.Type) types.Type {
return f.subst.typ(T)
}
// If id is an Instance, returns info.Instances[id].Type.
// Otherwise returns f.typeOf(id).
func (f *Function) instanceType(id *ast.Ident) types.Type {
if t, ok := f.info.Instances[id]; ok {
return t.Type
}
return f.typeOf(id)
}
// selection returns a *selection corresponding to f.info.Selections[selector]
// with potential updates for type substitution.
func (f *Function) selection(selector *ast.SelectorExpr) *selection {
sel := f.info.Selections[selector]
if sel == nil {
return nil
}
switch sel.Kind() {
case types.MethodExpr, types.MethodVal:
if recv := f.typ(sel.Recv()); recv != sel.Recv() {
// recv changed during type substitution.
pkg := f.declaredPackage().Pkg
obj, index, indirect := types.LookupFieldOrMethod(recv, true, pkg, sel.Obj().Name())
// sig replaces sel.Type(). See (types.Selection).Typ() for details.
sig := obj.Type().(*types.Signature)
sig = changeRecv(sig, newVar(sig.Recv().Name(), recv))
if sel.Kind() == types.MethodExpr {
sig = recvAsFirstArg(sig)
}
return &selection{
kind: sel.Kind(),
recv: recv,
typ: sig,
obj: obj,
index: index,
indirect: indirect,
}
}
}
return toSelection(sel)
}
// Destinations associated with unlabelled for/switch/select stmts.
// We push/pop one of these as we enter/leave each construct and for
// each BranchStmt we scan for the innermost target of the right type.
type targets struct {
tail *targets // rest of stack
_break *BasicBlock
_continue *BasicBlock
_fallthrough *BasicBlock
}
// Destinations associated with a labelled block.
// We populate these as labels are encountered in forward gotos or
// labelled statements.
// Forward gotos are resolved once it is known which statement they
// are associated with inside the Function.
type lblock struct {
label *types.Label // Label targeted by the blocks.
resolved bool // _goto block encountered (back jump or resolved fwd jump)
_goto *BasicBlock
_break *BasicBlock
_continue *BasicBlock
}
// label returns the symbol denoted by a label identifier.
//
// label should be a non-blank identifier (label.Name != "_").
func (f *Function) label(label *ast.Ident) *types.Label {
return f.objectOf(label).(*types.Label)
}
// lblockOf returns the branch target associated with the
// specified label, creating it if needed.
func (f *Function) lblockOf(label *types.Label) *lblock {
lb := f.lblocks[label]
if lb == nil {
lb = &lblock{
label: label,
_goto: f.newBasicBlock(label.Name()),
}
if f.lblocks == nil {
f.lblocks = make(map[*types.Label]*lblock)
}
f.lblocks[label] = lb
}
return lb
}
// labelledBlock searches f for the block of the specified label.
//
// If f is a yield function, it additionally searches ancestor Functions
// corresponding to enclosing range-over-func statements within the
// same source function, so the returned block may belong to a different Function.
func labelledBlock(f *Function, label *types.Label, tok token.Token) *BasicBlock {
if lb := f.lblocks[label]; lb != nil {
var block *BasicBlock
switch tok {
case token.BREAK:
block = lb._break
case token.CONTINUE:
block = lb._continue
case token.GOTO:
block = lb._goto
}
if block != nil {
return block
}
}
// Search ancestors if this is a yield function.
if f.jump != nil {
return labelledBlock(f.parent, label, tok)
}
return nil
}
// targetedBlock looks for the nearest block in f.targets
// (and f's ancestors) that matches tok's type, and returns
// the block and function it was found in.
func targetedBlock(f *Function, tok token.Token) *BasicBlock {
if f == nil {
return nil
}
for t := f.targets; t != nil; t = t.tail {
var block *BasicBlock
switch tok {
case token.BREAK:
block = t._break
case token.CONTINUE:
block = t._continue
case token.FALLTHROUGH:
block = t._fallthrough
}
if block != nil {
return block
}
}
// Search f's ancestors (in case f is a yield function).
return targetedBlock(f.parent, tok)
}
// instrs returns an iterator that returns each reachable instruction of the SSA function.
func (f *Function) instrs() iter.Seq[Instruction] {
return func(yield func(i Instruction) bool) {
for _, block := range f.Blocks {
for _, instr := range block.Instrs {
if !yield(instr) {
return
}
}
}
}
}
// addResultVar adds a result for a variable v to f.results and v to f.returnVars.
func (f *Function) addResultVar(v *types.Var) {
result := emitLocalVar(f, v)
f.results = append(f.results, result)
f.returnVars = append(f.returnVars, v)
}
// addParamVar adds a parameter to f.Params.
func (f *Function) addParamVar(v *types.Var) *Parameter {
name := v.Name()
if name == "" {
name = fmt.Sprintf("arg%d", len(f.Params))
}
param := &Parameter{
name: name,
object: v,
typ: f.typ(v.Type()),
parent: f,
}
f.Params = append(f.Params, param)
return param
}
// addSpilledParam declares a parameter that is pre-spilled to the
// stack; the function body will load/store the spilled location.
// Subsequent lifting will eliminate spills where possible.
func (f *Function) addSpilledParam(obj *types.Var) {
param := f.addParamVar(obj)
spill := emitLocalVar(f, obj)
f.emit(&Store{Addr: spill, Val: param})
}
// startBody initializes the function prior to generating SSA code for its body.
// Precondition: f.Type() already set.
func (f *Function) startBody() {
f.currentBlock = f.newBasicBlock("entry")
f.vars = make(map[*types.Var]Value) // needed for some synthetics, e.g. init
}
// createSyntacticParams populates f.Params and generates code (spills
// and named result locals) for all the parameters declared in the
// syntax. In addition it populates the f.objects mapping.
//
// Preconditions:
// f.startBody() was called. f.info != nil.
// Postcondition:
// len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0)
func (f *Function) createSyntacticParams(recv *ast.FieldList, functype *ast.FuncType) {
// Receiver (at most one inner iteration).
if recv != nil {
for _, field := range recv.List {
for _, n := range field.Names {
f.addSpilledParam(identVar(f, n))
}
// Anonymous receiver? No need to spill.
if field.Names == nil {
f.addParamVar(f.Signature.Recv())
}
}
}
// Parameters.
if functype.Params != nil {
n := len(f.Params) // 1 if has recv, 0 otherwise
for _, field := range functype.Params.List {
for _, n := range field.Names {
f.addSpilledParam(identVar(f, n))
}
// Anonymous parameter? No need to spill.
if field.Names == nil {
f.addParamVar(f.Signature.Params().At(len(f.Params) - n))
}
}
}
// Results.
if functype.Results != nil {
for _, field := range functype.Results.List {
// Implicit "var" decl of locals for named results.
for _, n := range field.Names {
v := identVar(f, n)
f.addResultVar(v)
}
// Implicit "var" decl of local for an unnamed result.
if field.Names == nil {
v := f.Signature.Results().At(len(f.results))
f.addResultVar(v)
}
}
}
}
// createDeferStack initializes fn.deferstack to local variable
// initialized to a ssa:deferstack() call.
func (fn *Function) createDeferStack() {
// Each syntactic function makes a call to ssa:deferstack,
// which is spilled to a local. Unused ones are later removed.
fn.deferstack = newVar("defer$stack", tDeferStack)
call := &Call{Call: CallCommon{Value: vDeferStack}}
call.setType(tDeferStack)
deferstack := fn.emit(call)
spill := emitLocalVar(fn, fn.deferstack)
emitStore(fn, spill, deferstack, token.NoPos)
}
type setNumable interface {
setNum(int)
}
// numberRegisters assigns numbers to all SSA registers
// (value-defining Instructions) in f, to aid debugging.
// (Non-Instruction Values are named at construction.)
func numberRegisters(f *Function) {
v := 0
for _, b := range f.Blocks {
for _, instr := range b.Instrs {
switch instr.(type) {
case Value:
instr.(setNumable).setNum(v)
v++
}
}
}
}
// buildReferrers populates the def/use information in all non-nil
// Value.Referrers slice.
// Precondition: all such slices are initially empty.
func buildReferrers(f *Function) {
var rands []*Value
for _, b := range f.Blocks {
for _, instr := range b.Instrs {
rands = instr.Operands(rands[:0]) // recycle storage
for _, rand := range rands {
if r := *rand; r != nil {
if ref := r.Referrers(); ref != nil {
*ref = append(*ref, instr)
}
}
}
}
}
}
// finishBody() finalizes the contents of the function after SSA code generation of its body.
//
// The function is not done being built until done() is called.
func (f *Function) finishBody() {
f.currentBlock = nil
f.lblocks = nil
f.returnVars = nil
f.jump = nil
f.source = nil
f.exits = nil
// Remove from f.Locals any Allocs that escape to the heap.
j := 0
for _, l := range f.Locals {
if !l.Heap {
f.Locals[j] = l
j++
}
}
// Nil out f.Locals[j:] to aid GC.
for i := j; i < len(f.Locals); i++ {
f.Locals[i] = nil
}
f.Locals = f.Locals[:j]
optimizeBlocks(f)
buildReferrers(f)
buildDomTree(f)
if f.Prog.mode&NaiveForm == 0 {
// For debugging pre-state of lifting pass:
// numberRegisters(f)
// f.WriteTo(os.Stderr)
lift(f)
}
// clear remaining builder state
f.results = nil // (used by lifting)
f.deferstack = nil // (used by lifting)
f.vars = nil // (used by lifting)
// clear out other function state (keep consistent with buildParamsOnly)
f.subst = nil
numberRegisters(f) // uses f.namedRegisters
}
// done marks the building of f's SSA body complete,
// along with any nested functions, and optionally prints them.
func (f *Function) done() {
assert(f.parent == nil, "done called on an anonymous function")
var visit func(*Function)
visit = func(f *Function) {
for _, anon := range f.AnonFuncs {
visit(anon) // anon is done building before f.
}
f.uniq = 0 // done with uniq
f.build = nil // function is built
if f.Prog.mode&PrintFunctions != 0 {
printMu.Lock()
f.WriteTo(os.Stdout)
printMu.Unlock()
}
if f.Prog.mode&SanityCheckFunctions != 0 {
mustSanityCheck(f, nil)
}
}
visit(f)
}
// removeNilBlocks eliminates nils from f.Blocks and updates each
// BasicBlock.Index. Use this after any pass that may delete blocks.
func (f *Function) removeNilBlocks() {
j := 0
for _, b := range f.Blocks {
if b != nil {
b.Index = j
f.Blocks[j] = b
j++
}
}
// Nil out f.Blocks[j:] to aid GC.
for i := j; i < len(f.Blocks); i++ {
f.Blocks[i] = nil
}
f.Blocks = f.Blocks[:j]
}
// SetDebugMode sets the debug mode for package pkg. If true, all its
// functions will include full debug info. This greatly increases the
// size of the instruction stream, and causes Functions to depend upon
// the ASTs, potentially keeping them live in memory for longer.
func (pkg *Package) SetDebugMode(debug bool) {
pkg.debug = debug
}
// debugInfo reports whether debug info is wanted for this function.
func (f *Function) debugInfo() bool {
// debug info for instantiations follows the debug info of their origin.
p := f.declaredPackage()
return p != nil && p.debug
}
// lookup returns the address of the named variable identified by obj
// that is local to function f or one of its enclosing functions.
// If escaping, the reference comes from a potentially escaping pointer
// expression and the referent must be heap-allocated.
// We assume the referent is a *Alloc or *Phi.
// (The only Phis at this stage are those created directly by go1.22 "for" loops.)
func (f *Function) lookup(obj *types.Var, escaping bool) Value {
if v, ok := f.vars[obj]; ok {
if escaping {
switch v := v.(type) {
case *Alloc:
v.Heap = true
case *Phi:
for _, edge := range v.Edges {
if alloc, ok := edge.(*Alloc); ok {
alloc.Heap = true
}
}
}
}
return v // function-local var (address)
}
// Definition must be in an enclosing function;
// plumb it through intervening closures.
if f.parent == nil {
panic("no ssa.Value for " + obj.String())
}
outer := f.parent.lookup(obj, true) // escaping
v := &FreeVar{
name: obj.Name(),
typ: outer.Type(),
pos: outer.Pos(),
outer: outer,
parent: f,
}
f.vars[obj] = v
f.FreeVars = append(f.FreeVars, v)
return v
}
// emit emits the specified instruction to function f.
func (f *Function) emit(instr Instruction) Value {
return f.currentBlock.emit(instr)
}
// RelString returns the full name of this function, qualified by
// package name, receiver type, etc.
//
// The specific formatting rules are not guaranteed and may change.
//
// Examples:
//
// "math.IsNaN" // a package-level function
// "(*bytes.Buffer).Bytes" // a declared method or a wrapper
// "(*bytes.Buffer).Bytes$thunk" // thunk (func wrapping method; receiver is param 0)
// "(*bytes.Buffer).Bytes$bound" // bound (func wrapping method; receiver supplied by closure)
// "main.main$1" // an anonymous function in main
// "main.init#1" // a declared init function
// "main.init" // the synthesized package initializer
//
// When these functions are referred to from within the same package
// (i.e. from == f.Pkg.Object), they are rendered without the package path.
// For example: "IsNaN", "(*Buffer).Bytes", etc.
//
// All non-synthetic functions have distinct package-qualified names.
// (But two methods may have the same name "(T).f" if one is a synthetic
// wrapper promoting a non-exported method "f" from another package; in
// that case, the strings are equal but the identifiers "f" are distinct.)
func (f *Function) RelString(from *types.Package) string {
// Anonymous?
if f.parent != nil {
// An anonymous function's Name() looks like "parentName$1",
// but its String() should include the type/package/etc.
parent := f.parent.RelString(from)
for i, anon := range f.parent.AnonFuncs {
if anon == f {
return fmt.Sprintf("%s$%d", parent, 1+i)
}
}
return f.name // should never happen
}
// Method (declared or wrapper)?
if recv := f.Signature.Recv(); recv != nil {
return f.relMethod(from, recv.Type())
}
// Thunk?
if f.method != nil {
return f.relMethod(from, f.method.recv)
}
// Bound?
if len(f.FreeVars) == 1 && strings.HasSuffix(f.name, "$bound") {
return f.relMethod(from, f.FreeVars[0].Type())
}
// Package-level function?
// Prefix with package name for cross-package references only.
if p := f.relPkg(); p != nil && p != from {
return fmt.Sprintf("%s.%s", p.Path(), f.name)
}
// Unknown.
return f.name
}
func (f *Function) relMethod(from *types.Package, recv types.Type) string {
return fmt.Sprintf("(%s).%s", relType(recv, from), f.name)
}
// writeSignature writes to buf the signature sig in declaration syntax.
func writeSignature(buf *bytes.Buffer, from *types.Package, name string, sig *types.Signature) {
buf.WriteString("func ")
if recv := sig.Recv(); recv != nil {
buf.WriteString("(")
if name := recv.Name(); name != "" {
buf.WriteString(name)
buf.WriteString(" ")
}
types.WriteType(buf, recv.Type(), types.RelativeTo(from))
buf.WriteString(") ")
}
buf.WriteString(name)
types.WriteSignature(buf, sig, types.RelativeTo(from))
}
// declaredPackage returns the package fn is declared in or nil if the
// function is not declared in a package.
func (fn *Function) declaredPackage() *Package {
switch {
case fn.Pkg != nil:
return fn.Pkg // non-generic function (does that follow??)
case fn.topLevelOrigin != nil:
return fn.topLevelOrigin.Pkg // instance of a named generic function
case fn.parent != nil:
return fn.parent.declaredPackage() // instance of an anonymous [generic] function
default:
return nil // function is not declared in a package, e.g. a wrapper.
}
}
// relPkg returns types.Package fn is printed in relationship to.
func (fn *Function) relPkg() *types.Package {
if p := fn.declaredPackage(); p != nil {
return p.Pkg
}
return nil
}
var _ io.WriterTo = (*Function)(nil) // *Function implements io.Writer
func (f *Function) WriteTo(w io.Writer) (int64, error) {
var buf bytes.Buffer
WriteFunction(&buf, f)
n, err := w.Write(buf.Bytes())
return int64(n), err
}
// WriteFunction writes to buf a human-readable "disassembly" of f.
func WriteFunction(buf *bytes.Buffer, f *Function) {
fmt.Fprintf(buf, "# Name: %s\n", f.String())
if f.Pkg != nil {
fmt.Fprintf(buf, "# Package: %s\n", f.Pkg.Pkg.Path())
}
if syn := f.Synthetic; syn != "" {
fmt.Fprintln(buf, "# Synthetic:", syn)
}
if pos := f.Pos(); pos.IsValid() {
fmt.Fprintf(buf, "# Location: %s\n", f.Prog.Fset.Position(pos))
}
if f.parent != nil {
fmt.Fprintf(buf, "# Parent: %s\n", f.parent.Name())
}
if f.Recover != nil {
fmt.Fprintf(buf, "# Recover: %s\n", f.Recover)
}
from := f.relPkg()
if f.FreeVars != nil {
buf.WriteString("# Free variables:\n")
for i, fv := range f.FreeVars {
fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, fv.Name(), relType(fv.Type(), from))
}
}
if len(f.Locals) > 0 {
buf.WriteString("# Locals:\n")
for i, l := range f.Locals {
fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, l.Name(), relType(typeparams.MustDeref(l.Type()), from))
}
}
writeSignature(buf, from, f.Name(), f.Signature)
buf.WriteString(":\n")
if f.Blocks == nil {
buf.WriteString("\t(external)\n")
}
// NB. column calculations are confused by non-ASCII
// characters and assume 8-space tabs.
const punchcard = 80 // for old time's sake.
const tabwidth = 8
for _, b := range f.Blocks {
if b == nil {
// Corrupt CFG.
fmt.Fprintf(buf, ".nil:\n")
continue
}
n, _ := fmt.Fprintf(buf, "%d:", b.Index)
// (|predecessors|, |successors|, immediate dominator)
bmsg := fmt.Sprintf("%s P:%d S:%d", b.Comment, len(b.Preds), len(b.Succs))
if b.Idom() != nil {
bmsg = fmt.Sprintf("%s idom:%d", bmsg, b.Idom().Index)
}
fmt.Fprintf(buf, "%*s%s\n", punchcard-1-n-len(bmsg), "", bmsg)
if false { // CFG debugging
fmt.Fprintf(buf, "\t# CFG: %s --> %s --> %s\n", b.Preds, b, b.Succs)
}
for _, instr := range b.Instrs {
buf.WriteString("\t")
switch v := instr.(type) {
case Value:
l := punchcard - tabwidth
// Left-align the instruction.
if name := v.Name(); name != "" {
n, _ := fmt.Fprintf(buf, "%s = ", name)
l -= n
}
n, _ := buf.WriteString(instr.String())
l -= n
// Right-align the type if there's space.
if t := v.Type(); t != nil {
buf.WriteByte(' ')
ts := relType(t, from)
l -= len(ts) + len(" ") // (spaces before and after type)
if l > 0 {
fmt.Fprintf(buf, "%*s", l, "")
}
buf.WriteString(ts)
}
case nil:
// Be robust against bad transforms.
buf.WriteString("<deleted>")
default:
buf.WriteString(instr.String())
}
// -mode=S: show line numbers
if f.Prog.mode&LogSource != 0 {
if pos := instr.Pos(); pos.IsValid() {
fmt.Fprintf(buf, " L%d", f.Prog.Fset.Position(pos).Line)
}
}
buf.WriteString("\n")
}
}
fmt.Fprintf(buf, "\n")
}
// newBasicBlock adds to f a new basic block and returns it. It does
// not automatically become the current block for subsequent calls to emit.
// comment is an optional string for more readable debugging output.
func (f *Function) newBasicBlock(comment string) *BasicBlock {
b := &BasicBlock{
Index: len(f.Blocks),
Comment: comment,
parent: f,
}
b.Succs = b.succs2[:0]
f.Blocks = append(f.Blocks, b)
return b
}
// NewFunction returns a new synthetic Function instance belonging to
// prog, with its name and signature fields set as specified.
//
// The caller is responsible for initializing the remaining fields of
// the function object, e.g. Pkg, Params, Blocks.
//
// It is practically impossible for clients to construct well-formed
// SSA functions/packages/programs directly, so we assume this is the
// job of the Builder alone. NewFunction exists to provide clients a
// little flexibility. For example, analysis tools may wish to
// construct fake Functions for the root of the callgraph, a fake
// "reflect" package, etc.
//
// TODO(adonovan): think harder about the API here.
func (prog *Program) NewFunction(name string, sig *types.Signature, provenance string) *Function {
return &Function{Prog: prog, name: name, Signature: sig, Synthetic: provenance}
}
// Syntax returns the function's syntax (*ast.Func{Decl,Lit})
// if it was produced from syntax or an *ast.RangeStmt if
// it is a range-over-func yield function.
func (f *Function) Syntax() ast.Node { return f.syntax }
// identVar returns the variable defined by id.
func identVar(fn *Function, id *ast.Ident) *types.Var {
return fn.info.Defs[id].(*types.Var)
}
// unique returns a unique positive int within the source tree of f.
// The source tree of f includes all of f's ancestors by parent and all
// of the AnonFuncs contained within these.
func unique(f *Function) int64 {
f.uniq++
return f.uniq
}
// exit is a change of control flow going from a range-over-func
// yield function to an ancestor function caused by a break, continue,
// goto, or return statement.
//
// There are 3 types of exits:
// * return from the source function (from ReturnStmt),
// * jump to a block (from break and continue statements [labelled/unlabelled]),
// * go to a label (from goto statements).
//
// As the builder does one pass over the ast, it is unclear whether
// a forward goto statement will leave a range-over-func body.
// The function being exited to is unresolved until the end
// of building the range-over-func body.
type exit struct {
id int64 // unique value for exit within from and to
from *Function // the function the exit starts from
to *Function // the function being exited to (nil if unresolved)
pos token.Pos
block *BasicBlock // basic block within to being jumped to.
label *types.Label // forward label being jumped to via goto.
// block == nil && label == nil => return
}
// storeVar emits to function f code to store a value v to a *types.Var x.
func storeVar(f *Function, x *types.Var, v Value, pos token.Pos) {
emitStore(f, f.lookup(x, true), v, pos)
}
// labelExit creates a new exit to a yield fn to exit the function using a label.
func labelExit(fn *Function, label *types.Label, pos token.Pos) *exit {
e := &exit{
id: unique(fn),
from: fn,
to: nil,
pos: pos,
label: label,
}
fn.exits = append(fn.exits, e)
return e
}
// blockExit creates a new exit to a yield fn that jumps to a basic block.
func blockExit(fn *Function, block *BasicBlock, pos token.Pos) *exit {
e := &exit{
id: unique(fn),
from: fn,
to: block.parent,
pos: pos,
block: block,
}
fn.exits = append(fn.exits, e)
return e
}
// returnExit creates a new exit to a yield fn that returns the source function.
func returnExit(fn *Function, pos token.Pos) *exit {
e := &exit{
id: unique(fn),
from: fn,
to: fn.source,
pos: pos,
}
fn.exits = append(fn.exits, e)
return e
}

127
vendor/golang.org/x/tools/go/ssa/instantiate.go generated vendored Normal file
View File

@@ -0,0 +1,127 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
import (
"fmt"
"go/types"
"slices"
"sync"
)
// A generic records information about a generic origin function,
// including a cache of existing instantiations.
type generic struct {
instancesMu sync.Mutex
instances map[*typeList]*Function // canonical type arguments to an instance.
}
// instance returns a Function that is the instantiation of generic
// origin function fn with the type arguments targs.
//
// Any created instance is added to cr.
//
// Acquires fn.generic.instancesMu.
func (fn *Function) instance(targs []types.Type, b *builder) *Function {
key := fn.Prog.canon.List(targs)
gen := fn.generic
gen.instancesMu.Lock()
defer gen.instancesMu.Unlock()
inst, ok := gen.instances[key]
if !ok {
inst = createInstance(fn, targs)
inst.buildshared = b.shared()
b.enqueue(inst)
if gen.instances == nil {
gen.instances = make(map[*typeList]*Function)
}
gen.instances[key] = inst
} else {
b.waitForSharedFunction(inst)
}
return inst
}
// createInstance returns the instantiation of generic function fn using targs.
//
// Requires fn.generic.instancesMu.
func createInstance(fn *Function, targs []types.Type) *Function {
prog := fn.Prog
// Compute signature.
var sig *types.Signature
var obj *types.Func
if recv := fn.Signature.Recv(); recv != nil {
// method
obj = prog.canon.instantiateMethod(fn.object, targs, prog.ctxt)
sig = obj.Type().(*types.Signature)
} else {
// function
instSig, err := types.Instantiate(prog.ctxt, fn.Signature, targs, false)
if err != nil {
panic(err)
}
instance, ok := instSig.(*types.Signature)
if !ok {
panic("Instantiate of a Signature returned a non-signature")
}
obj = fn.object // instantiation does not exist yet
sig = prog.canon.Type(instance).(*types.Signature)
}
// Choose strategy (instance or wrapper).
var (
synthetic string
subst *subster
build buildFunc
)
if prog.mode&InstantiateGenerics != 0 && !prog.isParameterized(targs...) {
synthetic = fmt.Sprintf("instance of %s", fn.Name())
if fn.syntax != nil {
subst = makeSubster(prog.ctxt, obj, fn.typeparams, targs)
build = (*builder).buildFromSyntax
} else {
build = (*builder).buildParamsOnly
}
} else {
synthetic = fmt.Sprintf("instantiation wrapper of %s", fn.Name())
build = (*builder).buildInstantiationWrapper
}
/* generic instance or instantiation wrapper */
return &Function{
name: fmt.Sprintf("%s%s", fn.Name(), targs), // may not be unique
object: obj,
Signature: sig,
Synthetic: synthetic,
syntax: fn.syntax, // \
info: fn.info, // } empty for non-created packages
goversion: fn.goversion, // /
build: build,
topLevelOrigin: fn,
pos: obj.Pos(),
Pkg: nil,
Prog: fn.Prog,
typeparams: fn.typeparams, // share with origin
typeargs: targs,
subst: subst,
}
}
// isParameterized reports whether any of the specified types contains
// a free type parameter. It is safe to call concurrently.
func (prog *Program) isParameterized(ts ...types.Type) bool {
prog.hasParamsMu.Lock()
defer prog.hasParamsMu.Unlock()
// TODO(adonovan): profile. If this operation is expensive,
// handle the most common but shallow cases such as T, pkg.T,
// *T without consulting the cache under the lock.
return slices.ContainsFunc(ts, prog.hasParams.Has)
}

671
vendor/golang.org/x/tools/go/ssa/lift.go generated vendored Normal file
View File

@@ -0,0 +1,671 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// This file defines the lifting pass which tries to "lift" Alloc
// cells (new/local variables) into SSA registers, replacing loads
// with the dominating stored value, eliminating loads and stores, and
// inserting φ-nodes as needed.
// Cited papers and resources:
//
// Ron Cytron et al. 1991. Efficiently computing SSA form...
// http://doi.acm.org/10.1145/115372.115320
//
// Cooper, Harvey, Kennedy. 2001. A Simple, Fast Dominance Algorithm.
// Software Practice and Experience 2001, 4:1-10.
// http://www.hipersoft.rice.edu/grads/publications/dom14.pdf
//
// Daniel Berlin, llvmdev mailing list, 2012.
// http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-January/046638.html
// (Be sure to expand the whole thread.)
// TODO(adonovan): opt: there are many optimizations worth evaluating, and
// the conventional wisdom for SSA construction is that a simple
// algorithm well engineered often beats those of better asymptotic
// complexity on all but the most egregious inputs.
//
// Danny Berlin suggests that the Cooper et al. algorithm for
// computing the dominance frontier is superior to Cytron et al.
// Furthermore he recommends that rather than computing the DF for the
// whole function then renaming all alloc cells, it may be cheaper to
// compute the DF for each alloc cell separately and throw it away.
//
// Consider exploiting liveness information to avoid creating dead
// φ-nodes which we then immediately remove.
//
// Also see many other "TODO: opt" suggestions in the code.
import (
"fmt"
"go/token"
"math/big"
"os"
"slices"
"golang.org/x/tools/internal/typeparams"
)
// If true, show diagnostic information at each step of lifting.
// Very verbose.
const debugLifting = false
// domFrontier maps each block to the set of blocks in its dominance
// frontier. The outer slice is conceptually a map keyed by
// Block.Index. The inner slice is conceptually a set, possibly
// containing duplicates.
//
// TODO(adonovan): opt: measure impact of dups; consider a packed bit
// representation, e.g. big.Int, and bitwise parallel operations for
// the union step in the Children loop.
//
// domFrontier's methods mutate the slice's elements but not its
// length, so their receivers needn't be pointers.
type domFrontier [][]*BasicBlock
func (df domFrontier) add(u, v *BasicBlock) {
p := &df[u.Index]
*p = append(*p, v)
}
// build builds the dominance frontier df for the dominator (sub)tree
// rooted at u, using the Cytron et al. algorithm.
//
// TODO(adonovan): opt: consider Berlin approach, computing pruned SSA
// by pruning the entire IDF computation, rather than merely pruning
// the DF -> IDF step.
func (df domFrontier) build(u *BasicBlock) {
// Encounter each node u in postorder of dom tree.
for _, child := range u.dom.children {
df.build(child)
}
for _, vb := range u.Succs {
if v := vb.dom; v.idom != u {
df.add(u, vb)
}
}
for _, w := range u.dom.children {
for _, vb := range df[w.Index] {
// TODO(adonovan): opt: use word-parallel bitwise union.
if v := vb.dom; v.idom != u {
df.add(u, vb)
}
}
}
}
func buildDomFrontier(fn *Function) domFrontier {
df := make(domFrontier, len(fn.Blocks))
df.build(fn.Blocks[0])
if fn.Recover != nil {
df.build(fn.Recover)
}
return df
}
func removeInstr(refs []Instruction, instr Instruction) []Instruction {
return slices.DeleteFunc(refs, func(i Instruction) bool { return i == instr })
}
// lift replaces local and new Allocs accessed only with
// load/store by SSA registers, inserting φ-nodes where necessary.
// The result is a program in classical pruned SSA form.
//
// Preconditions:
// - fn has no dead blocks (blockopt has run).
// - Def/use info (Operands and Referrers) is up-to-date.
// - The dominator tree is up-to-date.
func lift(fn *Function) {
// TODO(adonovan): opt: lots of little optimizations may be
// worthwhile here, especially if they cause us to avoid
// buildDomFrontier. For example:
//
// - Alloc never loaded? Eliminate.
// - Alloc never stored? Replace all loads with a zero constant.
// - Alloc stored once? Replace loads with dominating store;
// don't forget that an Alloc is itself an effective store
// of zero.
// - Alloc used only within a single block?
// Use degenerate algorithm avoiding φ-nodes.
// - Consider synergy with scalar replacement of aggregates (SRA).
// e.g. *(&x.f) where x is an Alloc.
// Perhaps we'd get better results if we generated this as x.f
// i.e. Field(x, .f) instead of Load(FieldIndex(x, .f)).
// Unclear.
//
// But we will start with the simplest correct code.
df := buildDomFrontier(fn)
if debugLifting {
title := false
for i, blocks := range df {
if blocks != nil {
if !title {
fmt.Fprintf(os.Stderr, "Dominance frontier of %s:\n", fn)
title = true
}
fmt.Fprintf(os.Stderr, "\t%s: %s\n", fn.Blocks[i], blocks)
}
}
}
newPhis := make(newPhiMap)
// During this pass we will replace some BasicBlock.Instrs
// (allocs, loads and stores) with nil, keeping a count in
// BasicBlock.gaps. At the end we will reset Instrs to the
// concatenation of all non-dead newPhis and non-nil Instrs
// for the block, reusing the original array if space permits.
// While we're here, we also eliminate 'rundefers'
// instructions and ssa:deferstack() in functions that contain no
// 'defer' instructions. For now, we also eliminate
// 's = ssa:deferstack()' calls if s doesn't escape, replacing s
// with nil in Defer{DeferStack: s}. This has the same meaning,
// but allows eliminating the intrinsic function `ssa:deferstack()`
// (unless it is needed due to range-over-func instances). This gives
// ssa users more time to support range-over-func.
usesDefer := false
deferstackAlloc, deferstackCall := deferstackPreamble(fn)
eliminateDeferStack := deferstackAlloc != nil && !deferstackAlloc.Heap
// A counter used to generate ~unique ids for Phi nodes, as an
// aid to debugging. We use large numbers to make them highly
// visible. All nodes are renumbered later.
fresh := 1000
// Determine which allocs we can lift and number them densely.
// The renaming phase uses this numbering for compact maps.
numAllocs := 0
for _, b := range fn.Blocks {
b.gaps = 0
b.rundefers = 0
for _, instr := range b.Instrs {
switch instr := instr.(type) {
case *Alloc:
index := -1
if liftAlloc(df, instr, newPhis, &fresh) {
index = numAllocs
numAllocs++
}
instr.index = index
case *Defer:
usesDefer = true
if eliminateDeferStack {
// Clear DeferStack and remove references to loads
if instr.DeferStack != nil {
if refs := instr.DeferStack.Referrers(); refs != nil {
*refs = removeInstr(*refs, instr)
}
instr.DeferStack = nil
}
}
case *RunDefers:
b.rundefers++
}
}
}
// renaming maps an alloc (keyed by index) to its replacement
// value. Initially the renaming contains nil, signifying the
// zero constant of the appropriate type; we construct the
// Const lazily at most once on each path through the domtree.
// TODO(adonovan): opt: cache per-function not per subtree.
renaming := make([]Value, numAllocs)
// Renaming.
rename(fn.Blocks[0], renaming, newPhis)
// Eliminate dead φ-nodes.
removeDeadPhis(fn.Blocks, newPhis)
// Eliminate ssa:deferstack() call.
if eliminateDeferStack {
b := deferstackCall.block
for i, instr := range b.Instrs {
if instr == deferstackCall {
b.Instrs[i] = nil
b.gaps++
break
}
}
}
// Prepend remaining live φ-nodes to each block.
for _, b := range fn.Blocks {
nps := newPhis[b]
j := len(nps)
rundefersToKill := b.rundefers
if usesDefer {
rundefersToKill = 0
}
if j+b.gaps+rundefersToKill == 0 {
continue // fast path: no new phis or gaps
}
// Compact nps + non-nil Instrs into a new slice.
// TODO(adonovan): opt: compact in situ (rightwards)
// if Instrs has sufficient space or slack.
dst := make([]Instruction, len(b.Instrs)+j-b.gaps-rundefersToKill)
for i, np := range nps {
dst[i] = np.phi
}
for _, instr := range b.Instrs {
if instr == nil {
continue
}
if !usesDefer {
if _, ok := instr.(*RunDefers); ok {
continue
}
}
dst[j] = instr
j++
}
b.Instrs = dst
}
// Remove any fn.Locals that were lifted.
j := 0
for _, l := range fn.Locals {
if l.index < 0 {
fn.Locals[j] = l
j++
}
}
// Nil out fn.Locals[j:] to aid GC.
for i := j; i < len(fn.Locals); i++ {
fn.Locals[i] = nil
}
fn.Locals = fn.Locals[:j]
}
// removeDeadPhis removes φ-nodes not transitively needed by a
// non-Phi, non-DebugRef instruction.
func removeDeadPhis(blocks []*BasicBlock, newPhis newPhiMap) {
// First pass: find the set of "live" φ-nodes: those reachable
// from some non-Phi instruction.
//
// We compute reachability in reverse, starting from each φ,
// rather than forwards, starting from each live non-Phi
// instruction, because this way visits much less of the
// Value graph.
livePhis := make(map[*Phi]bool)
for _, npList := range newPhis {
for _, np := range npList {
phi := np.phi
if !livePhis[phi] && phiHasDirectReferrer(phi) {
markLivePhi(livePhis, phi)
}
}
}
// Existing φ-nodes due to && and || operators
// are all considered live (see Go issue 19622).
for _, b := range blocks {
for _, phi := range b.phis() {
markLivePhi(livePhis, phi.(*Phi))
}
}
// Second pass: eliminate unused phis from newPhis.
for block, npList := range newPhis {
j := 0
for _, np := range npList {
if livePhis[np.phi] {
npList[j] = np
j++
} else {
// discard it, first removing it from referrers
for _, val := range np.phi.Edges {
if refs := val.Referrers(); refs != nil {
*refs = removeInstr(*refs, np.phi)
}
}
np.phi.block = nil
}
}
newPhis[block] = npList[:j]
}
}
// markLivePhi marks phi, and all φ-nodes transitively reachable via
// its Operands, live.
func markLivePhi(livePhis map[*Phi]bool, phi *Phi) {
livePhis[phi] = true
for _, rand := range phi.Operands(nil) {
if q, ok := (*rand).(*Phi); ok {
if !livePhis[q] {
markLivePhi(livePhis, q)
}
}
}
}
// phiHasDirectReferrer reports whether phi is directly referred to by
// a non-Phi instruction. Such instructions are the
// roots of the liveness traversal.
func phiHasDirectReferrer(phi *Phi) bool {
for _, instr := range *phi.Referrers() {
if _, ok := instr.(*Phi); !ok {
return true
}
}
return false
}
type blockSet struct{ big.Int } // (inherit methods from Int)
// add adds b to the set and returns true if the set changed.
func (s *blockSet) add(b *BasicBlock) bool {
i := b.Index
if s.Bit(i) != 0 {
return false
}
s.SetBit(&s.Int, i, 1)
return true
}
// take removes an arbitrary element from a set s and
// returns its index, or returns -1 if empty.
func (s *blockSet) take() int {
l := s.BitLen()
for i := range l {
if s.Bit(i) == 1 {
s.SetBit(&s.Int, i, 0)
return i
}
}
return -1
}
// newPhi is a pair of a newly introduced φ-node and the lifted Alloc
// it replaces.
type newPhi struct {
phi *Phi
alloc *Alloc
}
// newPhiMap records for each basic block, the set of newPhis that
// must be prepended to the block.
type newPhiMap map[*BasicBlock][]newPhi
// liftAlloc determines whether alloc can be lifted into registers,
// and if so, it populates newPhis with all the φ-nodes it may require
// and returns true.
//
// fresh is a source of fresh ids for phi nodes.
func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap, fresh *int) bool {
// Don't lift result values in functions that defer
// calls that may recover from panic.
if fn := alloc.Parent(); fn.Recover != nil {
if slices.Contains(fn.results, alloc) {
return false
}
}
// Compute defblocks, the set of blocks containing a
// definition of the alloc cell.
var defblocks blockSet
for _, instr := range *alloc.Referrers() {
// Bail out if we discover the alloc is not liftable;
// the only operations permitted to use the alloc are
// loads/stores into the cell, and DebugRef.
switch instr := instr.(type) {
case *Store:
if instr.Val == alloc {
return false // address used as value
}
if instr.Addr != alloc {
panic("Alloc.Referrers is inconsistent")
}
defblocks.add(instr.Block())
case *UnOp:
if instr.Op != token.MUL {
return false // not a load
}
if instr.X != alloc {
panic("Alloc.Referrers is inconsistent")
}
case *DebugRef:
// ok
default:
return false // some other instruction
}
}
// The Alloc itself counts as a (zero) definition of the cell.
defblocks.add(alloc.Block())
if debugLifting {
fmt.Fprintln(os.Stderr, "\tlifting ", alloc, alloc.Name())
}
fn := alloc.Parent()
// Φ-insertion.
//
// What follows is the body of the main loop of the insert-φ
// function described by Cytron et al, but instead of using
// counter tricks, we just reset the 'hasAlready' and 'work'
// sets each iteration. These are bitmaps so it's pretty cheap.
//
// TODO(adonovan): opt: recycle slice storage for W,
// hasAlready, defBlocks across liftAlloc calls.
var hasAlready blockSet
// Initialize W and work to defblocks.
var work blockSet = defblocks // blocks seen
var W blockSet // blocks to do
W.Set(&defblocks.Int)
// Traverse iterated dominance frontier, inserting φ-nodes.
for i := W.take(); i != -1; i = W.take() {
u := fn.Blocks[i]
for _, v := range df[u.Index] {
if hasAlready.add(v) {
// Create φ-node.
// It will be prepended to v.Instrs later, if needed.
phi := &Phi{
Edges: make([]Value, len(v.Preds)),
Comment: alloc.Comment,
}
// This is merely a debugging aid:
phi.setNum(*fresh)
*fresh++
phi.pos = alloc.Pos()
phi.setType(typeparams.MustDeref(alloc.Type()))
phi.block = v
if debugLifting {
fmt.Fprintf(os.Stderr, "\tplace %s = %s at block %s\n", phi.Name(), phi, v)
}
newPhis[v] = append(newPhis[v], newPhi{phi, alloc})
if work.add(v) {
W.add(v)
}
}
}
}
return true
}
// replaceAll replaces all intraprocedural uses of x with y,
// updating x.Referrers and y.Referrers.
// Precondition: x.Referrers() != nil, i.e. x must be local to some function.
func replaceAll(x, y Value) {
var rands []*Value
pxrefs := x.Referrers()
pyrefs := y.Referrers()
for _, instr := range *pxrefs {
rands = instr.Operands(rands[:0]) // recycle storage
for _, rand := range rands {
if *rand != nil {
if *rand == x {
*rand = y
}
}
}
if pyrefs != nil {
*pyrefs = append(*pyrefs, instr) // dups ok
}
}
*pxrefs = nil // x is now unreferenced
}
// renamed returns the value to which alloc is being renamed,
// constructing it lazily if it's the implicit zero initialization.
func renamed(renaming []Value, alloc *Alloc) Value {
v := renaming[alloc.index]
if v == nil {
v = zeroConst(typeparams.MustDeref(alloc.Type()))
renaming[alloc.index] = v
}
return v
}
// rename implements the (Cytron et al) SSA renaming algorithm, a
// preorder traversal of the dominator tree replacing all loads of
// Alloc cells with the value stored to that cell by the dominating
// store instruction. For lifting, we need only consider loads,
// stores and φ-nodes.
//
// renaming is a map from *Alloc (keyed by index number) to its
// dominating stored value; newPhis[x] is the set of new φ-nodes to be
// prepended to block x.
func rename(u *BasicBlock, renaming []Value, newPhis newPhiMap) {
// Each φ-node becomes the new name for its associated Alloc.
for _, np := range newPhis[u] {
phi := np.phi
alloc := np.alloc
renaming[alloc.index] = phi
}
// Rename loads and stores of allocs.
for i, instr := range u.Instrs {
switch instr := instr.(type) {
case *Alloc:
if instr.index >= 0 { // store of zero to Alloc cell
// Replace dominated loads by the zero value.
renaming[instr.index] = nil
if debugLifting {
fmt.Fprintf(os.Stderr, "\tkill alloc %s\n", instr)
}
// Delete the Alloc.
u.Instrs[i] = nil
u.gaps++
}
case *Store:
if alloc, ok := instr.Addr.(*Alloc); ok && alloc.index >= 0 { // store to Alloc cell
// Replace dominated loads by the stored value.
renaming[alloc.index] = instr.Val
if debugLifting {
fmt.Fprintf(os.Stderr, "\tkill store %s; new value: %s\n",
instr, instr.Val.Name())
}
// Remove the store from the referrer list of the stored value.
if refs := instr.Val.Referrers(); refs != nil {
*refs = removeInstr(*refs, instr)
}
// Delete the Store.
u.Instrs[i] = nil
u.gaps++
}
case *UnOp:
if instr.Op == token.MUL {
if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // load of Alloc cell
newval := renamed(renaming, alloc)
if debugLifting {
fmt.Fprintf(os.Stderr, "\tupdate load %s = %s with %s\n",
instr.Name(), instr, newval.Name())
}
// Replace all references to
// the loaded value by the
// dominating stored value.
replaceAll(instr, newval)
// Delete the Load.
u.Instrs[i] = nil
u.gaps++
}
}
case *DebugRef:
if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // ref of Alloc cell
if instr.IsAddr {
instr.X = renamed(renaming, alloc)
instr.IsAddr = false
// Add DebugRef to instr.X's referrers.
if refs := instr.X.Referrers(); refs != nil {
*refs = append(*refs, instr)
}
} else {
// A source expression denotes the address
// of an Alloc that was optimized away.
instr.X = nil
// Delete the DebugRef.
u.Instrs[i] = nil
u.gaps++
}
}
}
}
// For each φ-node in a CFG successor, rename the edge.
for _, v := range u.Succs {
phis := newPhis[v]
if len(phis) == 0 {
continue
}
i := v.predIndex(u)
for _, np := range phis {
phi := np.phi
alloc := np.alloc
newval := renamed(renaming, alloc)
if debugLifting {
fmt.Fprintf(os.Stderr, "\tsetphi %s edge %s -> %s (#%d) (alloc=%s) := %s\n",
phi.Name(), u, v, i, alloc.Name(), newval.Name())
}
phi.Edges[i] = newval
if prefs := newval.Referrers(); prefs != nil {
*prefs = append(*prefs, phi)
}
}
}
// Continue depth-first recursion over domtree, pushing a
// fresh copy of the renaming map for each subtree.
for i, v := range u.dom.children {
r := renaming
if i < len(u.dom.children)-1 {
// On all but the final iteration, we must make
// a copy to avoid destructive update.
r = make([]Value, len(renaming))
copy(r, renaming)
}
rename(v, r, newPhis)
}
}
// deferstackPreamble returns the *Alloc and ssa:deferstack() call for fn.deferstack.
func deferstackPreamble(fn *Function) (*Alloc, *Call) {
if alloc, _ := fn.vars[fn.deferstack].(*Alloc); alloc != nil {
for _, ref := range *alloc.Referrers() {
if ref, _ := ref.(*Store); ref != nil && ref.Addr == alloc {
if call, _ := ref.Val.(*Call); call != nil {
return alloc, call
}
}
}
}
return nil, nil
}

155
vendor/golang.org/x/tools/go/ssa/lvalue.go generated vendored Normal file
View File

@@ -0,0 +1,155 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// lvalues are the union of addressable expressions and map-index
// expressions.
import (
"go/ast"
"go/token"
"go/types"
"golang.org/x/tools/internal/typeparams"
)
// An lvalue represents an assignable location that may appear on the
// left-hand side of an assignment. This is a generalization of a
// pointer to permit updates to elements of maps.
type lvalue interface {
store(fn *Function, v Value) // stores v into the location
load(fn *Function) Value // loads the contents of the location
address(fn *Function) Value // address of the location
typ() types.Type // returns the type of the location
}
// An address is an lvalue represented by a true pointer.
type address struct {
addr Value // must have a pointer core type.
pos token.Pos // source position
expr ast.Expr // source syntax of the value (not address) [debug mode]
}
func (a *address) load(fn *Function) Value {
load := emitLoad(fn, a.addr)
load.pos = a.pos
return load
}
func (a *address) store(fn *Function, v Value) {
store := emitStore(fn, a.addr, v, a.pos)
if a.expr != nil {
// store.Val is v, converted for assignability.
emitDebugRef(fn, a.expr, store.Val, false)
}
}
func (a *address) address(fn *Function) Value {
if a.expr != nil {
emitDebugRef(fn, a.expr, a.addr, true)
}
return a.addr
}
func (a *address) typ() types.Type {
return typeparams.MustDeref(a.addr.Type())
}
// An element is an lvalue represented by m[k], the location of an
// element of a map. These locations are not addressable
// since pointers cannot be formed from them, but they do support
// load() and store().
type element struct {
m, k Value // map
t types.Type // map element type
pos token.Pos // source position of colon ({k:v}) or lbrack (m[k]=v)
}
func (e *element) load(fn *Function) Value {
l := &Lookup{
X: e.m,
Index: e.k,
}
l.setPos(e.pos)
l.setType(e.t)
return fn.emit(l)
}
func (e *element) store(fn *Function, v Value) {
up := &MapUpdate{
Map: e.m,
Key: e.k,
Value: emitConv(fn, v, e.t),
}
up.pos = e.pos
fn.emit(up)
}
func (e *element) address(fn *Function) Value {
panic("map elements are not addressable")
}
func (e *element) typ() types.Type {
return e.t
}
// A lazyAddress is an lvalue whose address is the result of an instruction.
// These work like an *address except a new address.address() Value
// is created on each load, store and address call.
// A lazyAddress can be used to control when a side effect (nil pointer
// dereference, index out of bounds) of using a location happens.
type lazyAddress struct {
addr func(fn *Function) Value // emit to fn the computation of the address
t types.Type // type of the location
pos token.Pos // source position
expr ast.Expr // source syntax of the value (not address) [debug mode]
}
func (l *lazyAddress) load(fn *Function) Value {
load := emitLoad(fn, l.addr(fn))
load.pos = l.pos
return load
}
func (l *lazyAddress) store(fn *Function, v Value) {
store := emitStore(fn, l.addr(fn), v, l.pos)
if l.expr != nil {
// store.Val is v, converted for assignability.
emitDebugRef(fn, l.expr, store.Val, false)
}
}
func (l *lazyAddress) address(fn *Function) Value {
addr := l.addr(fn)
if l.expr != nil {
emitDebugRef(fn, l.expr, addr, true)
}
return addr
}
func (l *lazyAddress) typ() types.Type { return l.t }
// A blank is a dummy variable whose name is "_".
// It is not reified: loads are illegal and stores are ignored.
type blank struct{}
func (bl blank) load(fn *Function) Value {
panic("blank.load is illegal")
}
func (bl blank) store(fn *Function, v Value) {
// no-op
}
func (bl blank) address(fn *Function) Value {
panic("blank var is not addressable")
}
func (bl blank) typ() types.Type {
// This should be the type of the blank Ident; the typechecker
// doesn't provide this yet, but fortunately, we don't need it
// yet either.
panic("blank.typ is unimplemented")
}

180
vendor/golang.org/x/tools/go/ssa/methods.go generated vendored Normal file
View File

@@ -0,0 +1,180 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// This file defines utilities for population of method sets.
import (
"fmt"
"go/types"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/typesinternal"
)
// MethodValue returns the Function implementing method sel, building
// wrapper methods on demand. It returns nil if sel denotes an
// interface or generic method.
//
// Precondition: sel.Kind() == MethodVal.
//
// Thread-safe.
//
// Acquires prog.methodsMu.
func (prog *Program) MethodValue(sel *types.Selection) *Function {
if sel.Kind() != types.MethodVal {
panic(fmt.Sprintf("MethodValue(%s) kind != MethodVal", sel))
}
T := sel.Recv()
if types.IsInterface(T) {
return nil // interface method or type parameter
}
if prog.isParameterized(T) {
return nil // generic method
}
if prog.mode&LogSource != 0 {
defer logStack("MethodValue %s %v", T, sel)()
}
var b builder
m := func() *Function {
prog.methodsMu.Lock()
defer prog.methodsMu.Unlock()
// Get or create SSA method set.
mset, ok := prog.methodSets.At(T).(*methodSet)
if !ok {
mset = &methodSet{mapping: make(map[string]*Function)}
prog.methodSets.Set(T, mset)
}
// Get or create SSA method.
id := sel.Obj().Id()
fn, ok := mset.mapping[id]
if !ok {
obj := sel.Obj().(*types.Func)
needsPromotion := len(sel.Index()) > 1
needsIndirection := !isPointer(recvType(obj)) && isPointer(T)
if needsPromotion || needsIndirection {
fn = createWrapper(prog, toSelection(sel))
fn.buildshared = b.shared()
b.enqueue(fn)
} else {
fn = prog.objectMethod(obj, &b)
}
if fn.Signature.Recv() == nil {
panic(fn)
}
mset.mapping[id] = fn
} else {
b.waitForSharedFunction(fn)
}
return fn
}()
b.iterate()
return m
}
// objectMethod returns the Function for a given method symbol.
// The symbol may be an instance of a generic function. It need not
// belong to an existing SSA package created by a call to
// prog.CreatePackage.
//
// objectMethod panics if the function is not a method.
//
// Acquires prog.objectMethodsMu.
func (prog *Program) objectMethod(obj *types.Func, b *builder) *Function {
sig := obj.Type().(*types.Signature)
if sig.Recv() == nil {
panic("not a method: " + obj.String())
}
// Belongs to a created package?
if fn := prog.FuncValue(obj); fn != nil {
return fn
}
// Instantiation of generic?
if originObj := obj.Origin(); originObj != obj {
origin := prog.objectMethod(originObj, b)
assert(origin.typeparams.Len() > 0, "origin is not generic")
targs := receiverTypeArgs(obj)
return origin.instance(targs, b)
}
// Consult/update cache of methods created from types.Func.
prog.objectMethodsMu.Lock()
defer prog.objectMethodsMu.Unlock()
fn, ok := prog.objectMethods[obj]
if !ok {
fn = createFunction(prog, obj, obj.Name(), nil, nil, "")
fn.Synthetic = "from type information (on demand)"
fn.buildshared = b.shared()
b.enqueue(fn)
if prog.objectMethods == nil {
prog.objectMethods = make(map[*types.Func]*Function)
}
prog.objectMethods[obj] = fn
} else {
b.waitForSharedFunction(fn)
}
return fn
}
// LookupMethod returns the implementation of the method of type T
// identified by (pkg, name). It returns nil if the method exists but
// is an interface method or generic method, and panics if T has no such method.
func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function {
sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name)
if sel == nil {
panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name)))
}
return prog.MethodValue(sel)
}
// methodSet contains the (concrete) methods of a concrete type (non-interface, non-parameterized).
type methodSet struct {
mapping map[string]*Function // populated lazily
}
// RuntimeTypes returns a new unordered slice containing all types in
// the program for which a runtime type is required.
//
// A runtime type is required for any non-parameterized, non-interface
// type that is converted to an interface, or for any type (including
// interface types) derivable from one through reflection.
//
// The methods of such types may be reachable through reflection or
// interface calls even if they are never called directly.
//
// Thread-safe.
//
// Acquires prog.makeInterfaceTypesMu.
func (prog *Program) RuntimeTypes() []types.Type {
prog.makeInterfaceTypesMu.Lock()
defer prog.makeInterfaceTypesMu.Unlock()
// Compute the derived types on demand, since many SSA clients
// never call RuntimeTypes, and those that do typically call
// it once (often within ssautil.AllFunctions, which will
// eventually not use it; see Go issue #69291.) This
// eliminates the need to eagerly compute all the element
// types during SSA building.
var runtimeTypes []types.Type
add := func(t types.Type) { runtimeTypes = append(runtimeTypes, t) }
var set typeutil.Map // for de-duping identical types
for t := range prog.makeInterfaceTypes {
typesinternal.ForEachElement(&set, &prog.MethodSets, t, add)
}
return runtimeTypes
}

111
vendor/golang.org/x/tools/go/ssa/mode.go generated vendored Normal file
View File

@@ -0,0 +1,111 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// This file defines the BuilderMode type and its command-line flag.
import (
"bytes"
"fmt"
)
// BuilderMode is a bitmask of options for diagnostics and checking.
//
// *BuilderMode satisfies the flag.Value interface. Example:
//
// var mode = ssa.BuilderMode(0)
// func init() { flag.Var(&mode, "build", ssa.BuilderModeDoc) }
type BuilderMode uint
const (
PrintPackages BuilderMode = 1 << iota // Print package inventory to stdout
PrintFunctions // Print function SSA code to stdout
LogSource // Log source locations as SSA builder progresses
SanityCheckFunctions // Perform sanity checking of function bodies
NaiveForm // Build naïve SSA form: don't replace local loads/stores with registers
BuildSerially // Build packages serially, not in parallel.
GlobalDebug // Enable debug info for all packages
BareInits // Build init functions without guards or calls to dependent inits
InstantiateGenerics // Instantiate generics functions (monomorphize) while building
)
const BuilderModeDoc = `Options controlling the SSA builder.
The value is a sequence of zero or more of these letters:
C perform sanity [C]hecking of the SSA form.
D include [D]ebug info for every function.
P print [P]ackage inventory.
F print [F]unction SSA code.
S log [S]ource locations as SSA builder progresses.
L build distinct packages seria[L]ly instead of in parallel.
N build [N]aive SSA form: don't replace local loads/stores with registers.
I build bare [I]nit functions: no init guards or calls to dependent inits.
G instantiate [G]eneric function bodies via monomorphization
`
func (m BuilderMode) String() string {
var buf bytes.Buffer
if m&GlobalDebug != 0 {
buf.WriteByte('D')
}
if m&PrintPackages != 0 {
buf.WriteByte('P')
}
if m&PrintFunctions != 0 {
buf.WriteByte('F')
}
if m&LogSource != 0 {
buf.WriteByte('S')
}
if m&SanityCheckFunctions != 0 {
buf.WriteByte('C')
}
if m&NaiveForm != 0 {
buf.WriteByte('N')
}
if m&BuildSerially != 0 {
buf.WriteByte('L')
}
if m&BareInits != 0 {
buf.WriteByte('I')
}
if m&InstantiateGenerics != 0 {
buf.WriteByte('G')
}
return buf.String()
}
// Set parses the flag characters in s and updates *m.
func (m *BuilderMode) Set(s string) error {
var mode BuilderMode
for _, c := range s {
switch c {
case 'D':
mode |= GlobalDebug
case 'P':
mode |= PrintPackages
case 'F':
mode |= PrintFunctions
case 'S':
mode |= LogSource | BuildSerially
case 'C':
mode |= SanityCheckFunctions
case 'N':
mode |= NaiveForm
case 'L':
mode |= BuildSerially
case 'I':
mode |= BareInits
case 'G':
mode |= InstantiateGenerics
default:
return fmt.Errorf("unknown BuilderMode option: %q", c)
}
}
*m = mode
return nil
}
// Get returns m.
func (m BuilderMode) Get() any { return m }

462
vendor/golang.org/x/tools/go/ssa/print.go generated vendored Normal file
View File

@@ -0,0 +1,462 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// This file implements the String() methods for all Value and
// Instruction types.
import (
"bytes"
"fmt"
"go/types"
"io"
"reflect"
"sort"
"strings"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/typeparams"
)
// relName returns the name of v relative to i.
// In most cases, this is identical to v.Name(), but references to
// Functions (including methods) and Globals use RelString and
// all types are displayed with relType, so that only cross-package
// references are package-qualified.
func relName(v Value, i Instruction) string {
var from *types.Package
if i != nil {
from = i.Parent().relPkg()
}
switch v := v.(type) {
case Member: // *Function or *Global
return v.RelString(from)
case *Const:
return v.RelString(from)
}
return v.Name()
}
func relType(t types.Type, from *types.Package) string {
return types.TypeString(t, types.RelativeTo(from))
}
func relTerm(term *types.Term, from *types.Package) string {
s := relType(term.Type(), from)
if term.Tilde() {
return "~" + s
}
return s
}
func relString(m Member, from *types.Package) string {
// NB: not all globals have an Object (e.g. init$guard),
// so use Package().Object not Object.Package().
if pkg := m.Package().Pkg; pkg != nil && pkg != from {
return fmt.Sprintf("%s.%s", pkg.Path(), m.Name())
}
return m.Name()
}
// Value.String()
//
// This method is provided only for debugging.
// It never appears in disassembly, which uses Value.Name().
func (v *Parameter) String() string {
from := v.Parent().relPkg()
return fmt.Sprintf("parameter %s : %s", v.Name(), relType(v.Type(), from))
}
func (v *FreeVar) String() string {
from := v.Parent().relPkg()
return fmt.Sprintf("freevar %s : %s", v.Name(), relType(v.Type(), from))
}
func (v *Builtin) String() string {
return fmt.Sprintf("builtin %s", v.Name())
}
// Instruction.String()
func (v *Alloc) String() string {
op := "local"
if v.Heap {
op = "new"
}
from := v.Parent().relPkg()
return fmt.Sprintf("%s %s (%s)", op, relType(typeparams.MustDeref(v.Type()), from), v.Comment)
}
func (v *Phi) String() string {
var b bytes.Buffer
b.WriteString("phi [")
for i, edge := range v.Edges {
if i > 0 {
b.WriteString(", ")
}
// Be robust against malformed CFG.
if v.block == nil {
b.WriteString("??")
continue
}
block := -1
if i < len(v.block.Preds) {
block = v.block.Preds[i].Index
}
fmt.Fprintf(&b, "%d: ", block)
edgeVal := "<nil>" // be robust
if edge != nil {
edgeVal = relName(edge, v)
}
b.WriteString(edgeVal)
}
b.WriteString("]")
if v.Comment != "" {
b.WriteString(" #")
b.WriteString(v.Comment)
}
return b.String()
}
func printCall(v *CallCommon, prefix string, instr Instruction) string {
var b bytes.Buffer
b.WriteString(prefix)
if !v.IsInvoke() {
b.WriteString(relName(v.Value, instr))
} else {
fmt.Fprintf(&b, "invoke %s.%s", relName(v.Value, instr), v.Method.Name())
}
b.WriteString("(")
for i, arg := range v.Args {
if i > 0 {
b.WriteString(", ")
}
b.WriteString(relName(arg, instr))
}
if v.Signature().Variadic() {
b.WriteString("...")
}
b.WriteString(")")
return b.String()
}
func (c *CallCommon) String() string {
return printCall(c, "", nil)
}
func (v *Call) String() string {
return printCall(&v.Call, "", v)
}
func (v *BinOp) String() string {
return fmt.Sprintf("%s %s %s", relName(v.X, v), v.Op.String(), relName(v.Y, v))
}
func (v *UnOp) String() string {
return fmt.Sprintf("%s%s%s", v.Op, relName(v.X, v), commaOk(v.CommaOk))
}
func printConv(prefix string, v, x Value) string {
from := v.Parent().relPkg()
return fmt.Sprintf("%s %s <- %s (%s)",
prefix,
relType(v.Type(), from),
relType(x.Type(), from),
relName(x, v.(Instruction)))
}
func (v *ChangeType) String() string { return printConv("changetype", v, v.X) }
func (v *Convert) String() string { return printConv("convert", v, v.X) }
func (v *ChangeInterface) String() string { return printConv("change interface", v, v.X) }
func (v *SliceToArrayPointer) String() string { return printConv("slice to array pointer", v, v.X) }
func (v *MakeInterface) String() string { return printConv("make", v, v.X) }
func (v *MultiConvert) String() string {
from := v.Parent().relPkg()
var b strings.Builder
b.WriteString(printConv("multiconvert", v, v.X))
b.WriteString(" [")
for i, s := range termListOf(v.from) {
for j, d := range termListOf(v.to) {
if i != 0 || j != 0 {
b.WriteString(" | ")
}
fmt.Fprintf(&b, "%s <- %s", relTerm(d, from), relTerm(s, from))
}
}
b.WriteString("]")
return b.String()
}
func (v *MakeClosure) String() string {
var b bytes.Buffer
fmt.Fprintf(&b, "make closure %s", relName(v.Fn, v))
if v.Bindings != nil {
b.WriteString(" [")
for i, c := range v.Bindings {
if i > 0 {
b.WriteString(", ")
}
b.WriteString(relName(c, v))
}
b.WriteString("]")
}
return b.String()
}
func (v *MakeSlice) String() string {
from := v.Parent().relPkg()
return fmt.Sprintf("make %s %s %s",
relType(v.Type(), from),
relName(v.Len, v),
relName(v.Cap, v))
}
func (v *Slice) String() string {
var b bytes.Buffer
b.WriteString("slice ")
b.WriteString(relName(v.X, v))
b.WriteString("[")
if v.Low != nil {
b.WriteString(relName(v.Low, v))
}
b.WriteString(":")
if v.High != nil {
b.WriteString(relName(v.High, v))
}
if v.Max != nil {
b.WriteString(":")
b.WriteString(relName(v.Max, v))
}
b.WriteString("]")
return b.String()
}
func (v *MakeMap) String() string {
res := ""
if v.Reserve != nil {
res = relName(v.Reserve, v)
}
from := v.Parent().relPkg()
return fmt.Sprintf("make %s %s", relType(v.Type(), from), res)
}
func (v *MakeChan) String() string {
from := v.Parent().relPkg()
return fmt.Sprintf("make %s %s", relType(v.Type(), from), relName(v.Size, v))
}
func (v *FieldAddr) String() string {
// Be robust against a bad index.
name := "?"
if fld := fieldOf(typeparams.MustDeref(v.X.Type()), v.Field); fld != nil {
name = fld.Name()
}
return fmt.Sprintf("&%s.%s [#%d]", relName(v.X, v), name, v.Field)
}
func (v *Field) String() string {
// Be robust against a bad index.
name := "?"
if fld := fieldOf(v.X.Type(), v.Field); fld != nil {
name = fld.Name()
}
return fmt.Sprintf("%s.%s [#%d]", relName(v.X, v), name, v.Field)
}
func (v *IndexAddr) String() string {
return fmt.Sprintf("&%s[%s]", relName(v.X, v), relName(v.Index, v))
}
func (v *Index) String() string {
return fmt.Sprintf("%s[%s]", relName(v.X, v), relName(v.Index, v))
}
func (v *Lookup) String() string {
return fmt.Sprintf("%s[%s]%s", relName(v.X, v), relName(v.Index, v), commaOk(v.CommaOk))
}
func (v *Range) String() string {
return "range " + relName(v.X, v)
}
func (v *Next) String() string {
return "next " + relName(v.Iter, v)
}
func (v *TypeAssert) String() string {
from := v.Parent().relPkg()
return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), relType(v.AssertedType, from))
}
func (v *Extract) String() string {
return fmt.Sprintf("extract %s #%d", relName(v.Tuple, v), v.Index)
}
func (s *Jump) String() string {
// Be robust against malformed CFG.
block := -1
if s.block != nil && len(s.block.Succs) == 1 {
block = s.block.Succs[0].Index
}
return fmt.Sprintf("jump %d", block)
}
func (s *If) String() string {
// Be robust against malformed CFG.
tblock, fblock := -1, -1
if s.block != nil && len(s.block.Succs) == 2 {
tblock = s.block.Succs[0].Index
fblock = s.block.Succs[1].Index
}
return fmt.Sprintf("if %s goto %d else %d", relName(s.Cond, s), tblock, fblock)
}
func (s *Go) String() string {
return printCall(&s.Call, "go ", s)
}
func (s *Panic) String() string {
return "panic " + relName(s.X, s)
}
func (s *Return) String() string {
var b bytes.Buffer
b.WriteString("return")
for i, r := range s.Results {
if i == 0 {
b.WriteString(" ")
} else {
b.WriteString(", ")
}
b.WriteString(relName(r, s))
}
return b.String()
}
func (*RunDefers) String() string {
return "rundefers"
}
func (s *Send) String() string {
return fmt.Sprintf("send %s <- %s", relName(s.Chan, s), relName(s.X, s))
}
func (s *Defer) String() string {
prefix := "defer "
if s.DeferStack != nil {
prefix += "[" + relName(s.DeferStack, s) + "] "
}
c := printCall(&s.Call, prefix, s)
return c
}
func (s *Select) String() string {
var b bytes.Buffer
for i, st := range s.States {
if i > 0 {
b.WriteString(", ")
}
if st.Dir == types.RecvOnly {
b.WriteString("<-")
b.WriteString(relName(st.Chan, s))
} else {
b.WriteString(relName(st.Chan, s))
b.WriteString("<-")
b.WriteString(relName(st.Send, s))
}
}
non := ""
if !s.Blocking {
non = "non"
}
return fmt.Sprintf("select %sblocking [%s]", non, b.String())
}
func (s *Store) String() string {
return fmt.Sprintf("*%s = %s", relName(s.Addr, s), relName(s.Val, s))
}
func (s *MapUpdate) String() string {
return fmt.Sprintf("%s[%s] = %s", relName(s.Map, s), relName(s.Key, s), relName(s.Value, s))
}
func (s *DebugRef) String() string {
p := s.Parent().Prog.Fset.Position(s.Pos())
var descr any
if s.object != nil {
descr = s.object // e.g. "var x int"
} else {
descr = reflect.TypeOf(s.Expr) // e.g. "*ast.CallExpr"
}
var addr string
if s.IsAddr {
addr = "address of "
}
return fmt.Sprintf("; %s%s @ %d:%d is %s", addr, descr, p.Line, p.Column, s.X.Name())
}
func (p *Package) String() string {
return "package " + p.Pkg.Path()
}
var _ io.WriterTo = (*Package)(nil) // *Package implements io.Writer
func (p *Package) WriteTo(w io.Writer) (int64, error) {
var buf bytes.Buffer
WritePackage(&buf, p)
n, err := w.Write(buf.Bytes())
return int64(n), err
}
// WritePackage writes to buf a human-readable summary of p.
func WritePackage(buf *bytes.Buffer, p *Package) {
fmt.Fprintf(buf, "%s:\n", p)
var names []string
maxname := 0
for name := range p.Members {
if l := len(name); l > maxname {
maxname = l
}
names = append(names, name)
}
from := p.Pkg
sort.Strings(names)
for _, name := range names {
switch mem := p.Members[name].(type) {
case *NamedConst:
fmt.Fprintf(buf, " const %-*s %s = %s\n",
maxname, name, mem.Name(), mem.Value.RelString(from))
case *Function:
fmt.Fprintf(buf, " func %-*s %s\n",
maxname, name, relType(mem.Type(), from))
case *Type:
fmt.Fprintf(buf, " type %-*s %s\n",
maxname, name, relType(mem.Type().Underlying(), from))
for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) {
fmt.Fprintf(buf, " %s\n", types.SelectionString(meth, types.RelativeTo(from)))
}
case *Global:
fmt.Fprintf(buf, " var %-*s %s\n",
maxname, name, relType(typeparams.MustDeref(mem.Type()), from))
}
}
fmt.Fprintf(buf, "\n")
}
func commaOk(x bool) string {
if x {
return ",ok"
}
return ""
}

637
vendor/golang.org/x/tools/go/ssa/sanity.go generated vendored Normal file
View File

@@ -0,0 +1,637 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// An optional pass for sanity-checking invariants of the SSA representation.
// Currently it checks CFG invariants but little at the instruction level.
import (
"bytes"
"fmt"
"go/ast"
"go/types"
"io"
"os"
"slices"
"strings"
)
type sanity struct {
reporter io.Writer
fn *Function
block *BasicBlock
instrs map[Instruction]unit
insane bool
}
// sanityCheck performs integrity checking of the SSA representation
// of the function fn (which must have been "built") and returns true
// if it was valid. Diagnostics are written to reporter if non-nil,
// os.Stderr otherwise. Some diagnostics are only warnings and do not
// imply a negative result.
//
// Sanity-checking is intended to facilitate the debugging of code
// transformation passes.
func sanityCheck(fn *Function, reporter io.Writer) bool {
if reporter == nil {
reporter = os.Stderr
}
return (&sanity{reporter: reporter}).checkFunction(fn)
}
// mustSanityCheck is like sanityCheck but panics instead of returning
// a negative result.
func mustSanityCheck(fn *Function, reporter io.Writer) {
if !sanityCheck(fn, reporter) {
fn.WriteTo(os.Stderr)
panic("SanityCheck failed")
}
}
func (s *sanity) diagnostic(prefix, format string, args ...any) {
fmt.Fprintf(s.reporter, "%s: function %s", prefix, s.fn)
if s.block != nil {
fmt.Fprintf(s.reporter, ", block %s", s.block)
}
io.WriteString(s.reporter, ": ")
fmt.Fprintf(s.reporter, format, args...)
io.WriteString(s.reporter, "\n")
}
func (s *sanity) errorf(format string, args ...any) {
s.insane = true
s.diagnostic("Error", format, args...)
}
func (s *sanity) warnf(format string, args ...any) {
s.diagnostic("Warning", format, args...)
}
// findDuplicate returns an arbitrary basic block that appeared more
// than once in blocks, or nil if all were unique.
func findDuplicate(blocks []*BasicBlock) *BasicBlock {
if len(blocks) < 2 {
return nil
}
if blocks[0] == blocks[1] {
return blocks[0]
}
// Slow path:
m := make(map[*BasicBlock]bool)
for _, b := range blocks {
if m[b] {
return b
}
m[b] = true
}
return nil
}
func (s *sanity) checkInstr(idx int, instr Instruction) {
switch instr := instr.(type) {
case *If, *Jump, *Return, *Panic:
s.errorf("control flow instruction not at end of block")
case *Phi:
if idx == 0 {
// It suffices to apply this check to just the first phi node.
if dup := findDuplicate(s.block.Preds); dup != nil {
s.errorf("phi node in block with duplicate predecessor %s", dup)
}
} else {
prev := s.block.Instrs[idx-1]
if _, ok := prev.(*Phi); !ok {
s.errorf("Phi instruction follows a non-Phi: %T", prev)
}
}
if ne, np := len(instr.Edges), len(s.block.Preds); ne != np {
s.errorf("phi node has %d edges but %d predecessors", ne, np)
} else {
for i, e := range instr.Edges {
if e == nil {
s.errorf("phi node '%s' has no value for edge #%d from %s", instr.Comment, i, s.block.Preds[i])
} else if !types.Identical(instr.typ, e.Type()) {
s.errorf("phi node '%s' has a different type (%s) for edge #%d from %s (%s)",
instr.Comment, instr.Type(), i, s.block.Preds[i], e.Type())
}
}
}
case *Alloc:
if !instr.Heap {
found := slices.Contains(s.fn.Locals, instr)
if !found {
s.errorf("local alloc %s = %s does not appear in Function.Locals", instr.Name(), instr)
}
}
case *BinOp:
case *Call:
if common := instr.Call; common.IsInvoke() {
if !types.IsInterface(common.Value.Type()) {
s.errorf("invoke on %s (%s) which is not an interface type (or type param)", common.Value, common.Value.Type())
}
}
case *ChangeInterface:
case *ChangeType:
case *SliceToArrayPointer:
case *Convert:
if from := instr.X.Type(); !isBasicConvTypes(from) {
if to := instr.Type(); !isBasicConvTypes(to) {
s.errorf("convert %s -> %s: at least one type must be basic (or all basic, []byte, or []rune)", from, to)
}
}
case *MultiConvert:
case *Defer:
case *Extract:
case *Field:
case *FieldAddr:
case *Go:
case *Index:
case *IndexAddr:
case *Lookup:
case *MakeChan:
case *MakeClosure:
numFree := len(instr.Fn.(*Function).FreeVars)
numBind := len(instr.Bindings)
if numFree != numBind {
s.errorf("MakeClosure has %d Bindings for function %s with %d free vars",
numBind, instr.Fn, numFree)
}
if recv := instr.Type().(*types.Signature).Recv(); recv != nil {
s.errorf("MakeClosure's type includes receiver %s", recv.Type())
}
case *MakeInterface:
case *MakeMap:
case *MakeSlice:
case *MapUpdate:
case *Next:
case *Range:
case *RunDefers:
case *Select:
case *Send:
case *Slice:
case *Store:
case *TypeAssert:
case *UnOp:
case *DebugRef:
// TODO(adonovan): implement checks.
default:
panic(fmt.Sprintf("Unknown instruction type: %T", instr))
}
if call, ok := instr.(CallInstruction); ok {
if call.Common().Signature() == nil {
s.errorf("nil signature: %s", call)
}
}
// Check that value-defining instructions have valid types
// and a valid referrer list.
if v, ok := instr.(Value); ok {
t := v.Type()
if t == nil {
s.errorf("no type: %s = %s", v.Name(), v)
} else if t == tRangeIter || t == tDeferStack {
// not a proper type; ignore.
} else if b, ok := t.Underlying().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {
s.errorf("instruction has 'untyped' result: %s = %s : %s", v.Name(), v, t)
}
s.checkReferrerList(v)
}
// Untyped constants are legal as instruction Operands(),
// for example:
// _ = "foo"[0]
// or:
// if wordsize==64 {...}
// All other non-Instruction Values can be found via their
// enclosing Function or Package.
}
func (s *sanity) checkFinalInstr(instr Instruction) {
switch instr := instr.(type) {
case *If:
if nsuccs := len(s.block.Succs); nsuccs != 2 {
s.errorf("If-terminated block has %d successors; expected 2", nsuccs)
return
}
if s.block.Succs[0] == s.block.Succs[1] {
s.errorf("If-instruction has same True, False target blocks: %s", s.block.Succs[0])
return
}
case *Jump:
if nsuccs := len(s.block.Succs); nsuccs != 1 {
s.errorf("Jump-terminated block has %d successors; expected 1", nsuccs)
return
}
case *Return:
if nsuccs := len(s.block.Succs); nsuccs != 0 {
s.errorf("Return-terminated block has %d successors; expected none", nsuccs)
return
}
if na, nf := len(instr.Results), s.fn.Signature.Results().Len(); nf != na {
s.errorf("%d-ary return in %d-ary function", na, nf)
}
case *Panic:
if nsuccs := len(s.block.Succs); nsuccs != 0 {
s.errorf("Panic-terminated block has %d successors; expected none", nsuccs)
return
}
default:
s.errorf("non-control flow instruction at end of block")
}
}
func (s *sanity) checkBlock(b *BasicBlock, index int) {
s.block = b
if b.Index != index {
s.errorf("block has incorrect Index %d", b.Index)
}
if b.parent != s.fn {
s.errorf("block has incorrect parent %s", b.parent)
}
// Check all blocks are reachable.
// (The entry block is always implicitly reachable,
// as is the Recover block, if any.)
if (index > 0 && b != b.parent.Recover) && len(b.Preds) == 0 {
s.warnf("unreachable block")
if b.Instrs == nil {
// Since this block is about to be pruned,
// tolerating transient problems in it
// simplifies other optimizations.
return
}
}
// Check predecessor and successor relations are dual,
// and that all blocks in CFG belong to same function.
for _, a := range b.Preds {
found := slices.Contains(a.Succs, b)
if !found {
s.errorf("expected successor edge in predecessor %s; found only: %s", a, a.Succs)
}
if a.parent != s.fn {
s.errorf("predecessor %s belongs to different function %s", a, a.parent)
}
}
for _, c := range b.Succs {
found := slices.Contains(c.Preds, b)
if !found {
s.errorf("expected predecessor edge in successor %s; found only: %s", c, c.Preds)
}
if c.parent != s.fn {
s.errorf("successor %s belongs to different function %s", c, c.parent)
}
}
// Check each instruction is sane.
n := len(b.Instrs)
if n == 0 {
s.errorf("basic block contains no instructions")
}
var rands [10]*Value // reuse storage
for j, instr := range b.Instrs {
if instr == nil {
s.errorf("nil instruction at index %d", j)
continue
}
if b2 := instr.Block(); b2 == nil {
s.errorf("nil Block() for instruction at index %d", j)
continue
} else if b2 != b {
s.errorf("wrong Block() (%s) for instruction at index %d ", b2, j)
continue
}
if j < n-1 {
s.checkInstr(j, instr)
} else {
s.checkFinalInstr(instr)
}
// Check Instruction.Operands.
operands:
for i, op := range instr.Operands(rands[:0]) {
if op == nil {
s.errorf("nil operand pointer %d of %s", i, instr)
continue
}
val := *op
if val == nil {
continue // a nil operand is ok
}
// Check that "untyped" types only appear on constant operands.
if _, ok := (*op).(*Const); !ok {
if basic, ok := (*op).Type().Underlying().(*types.Basic); ok {
if basic.Info()&types.IsUntyped != 0 {
s.errorf("operand #%d of %s is untyped: %s", i, instr, basic)
}
}
}
// Check that Operands that are also Instructions belong to same function.
// TODO(adonovan): also check their block dominates block b.
if val, ok := val.(Instruction); ok {
if val.Block() == nil {
s.errorf("operand %d of %s is an instruction (%s) that belongs to no block", i, instr, val)
} else if val.Parent() != s.fn {
s.errorf("operand %d of %s is an instruction (%s) from function %s", i, instr, val, val.Parent())
}
}
// Check that each function-local operand of
// instr refers back to instr. (NB: quadratic)
switch val := val.(type) {
case *Const, *Global, *Builtin:
continue // not local
case *Function:
if val.parent == nil {
continue // only anon functions are local
}
}
// TODO(adonovan): check val.Parent() != nil <=> val.Referrers() is defined.
if refs := val.Referrers(); refs != nil {
for _, ref := range *refs {
if ref == instr {
continue operands
}
}
s.errorf("operand %d of %s (%s) does not refer to us", i, instr, val)
} else {
s.errorf("operand %d of %s (%s) has no referrers", i, instr, val)
}
}
}
}
func (s *sanity) checkReferrerList(v Value) {
refs := v.Referrers()
if refs == nil {
s.errorf("%s has missing referrer list", v.Name())
return
}
for i, ref := range *refs {
if _, ok := s.instrs[ref]; !ok {
s.errorf("%s.Referrers()[%d] = %s is not an instruction belonging to this function", v.Name(), i, ref)
}
}
}
func (s *sanity) checkFunctionParams() {
signature := s.fn.Signature
params := s.fn.Params
// startSigParams is the start of signature.Params() within params.
startSigParams := 0
if signature.Recv() != nil {
startSigParams = 1
}
if startSigParams+signature.Params().Len() != len(params) {
s.errorf("function has %d parameters in signature but has %d after building",
startSigParams+signature.Params().Len(), len(params))
return
}
for i, param := range params {
var sigType types.Type
si := i - startSigParams
if si < 0 {
sigType = signature.Recv().Type()
} else {
sigType = signature.Params().At(si).Type()
}
if !types.Identical(sigType, param.Type()) {
s.errorf("expect type %s in signature but got type %s in param %d", param.Type(), sigType, i)
}
}
}
// checkTransientFields checks whether all transient fields of Function are cleared.
func (s *sanity) checkTransientFields() {
fn := s.fn
if fn.build != nil {
s.errorf("function transient field 'build' is not nil")
}
if fn.currentBlock != nil {
s.errorf("function transient field 'currentBlock' is not nil")
}
if fn.vars != nil {
s.errorf("function transient field 'vars' is not nil")
}
if fn.results != nil {
s.errorf("function transient field 'results' is not nil")
}
if fn.returnVars != nil {
s.errorf("function transient field 'returnVars' is not nil")
}
if fn.targets != nil {
s.errorf("function transient field 'targets' is not nil")
}
if fn.lblocks != nil {
s.errorf("function transient field 'lblocks' is not nil")
}
if fn.subst != nil {
s.errorf("function transient field 'subst' is not nil")
}
if fn.jump != nil {
s.errorf("function transient field 'jump' is not nil")
}
if fn.deferstack != nil {
s.errorf("function transient field 'deferstack' is not nil")
}
if fn.source != nil {
s.errorf("function transient field 'source' is not nil")
}
if fn.exits != nil {
s.errorf("function transient field 'exits' is not nil")
}
if fn.uniq != 0 {
s.errorf("function transient field 'uniq' is not zero")
}
}
func (s *sanity) checkFunction(fn *Function) bool {
s.fn = fn
s.checkFunctionParams()
s.checkTransientFields()
// TODO(taking): Sanity check origin, typeparams, and typeargs.
if fn.Prog == nil {
s.errorf("nil Prog")
}
var buf bytes.Buffer
_ = fn.String() // must not crash
_ = fn.RelString(fn.relPkg()) // must not crash
WriteFunction(&buf, fn) // must not crash
// All functions have a package, except delegates (which are
// shared across packages, or duplicated as weak symbols in a
// separate-compilation model), and error.Error.
if fn.Pkg == nil {
if strings.HasPrefix(fn.Synthetic, "from type information (on demand)") ||
strings.HasPrefix(fn.Synthetic, "wrapper ") ||
strings.HasPrefix(fn.Synthetic, "bound ") ||
strings.HasPrefix(fn.Synthetic, "thunk ") ||
strings.HasSuffix(fn.name, "Error") ||
strings.HasPrefix(fn.Synthetic, "instance ") ||
strings.HasPrefix(fn.Synthetic, "instantiation ") ||
(fn.parent != nil && len(fn.typeargs) > 0) /* anon fun in instance */ {
// ok
} else {
s.errorf("nil Pkg")
}
}
if src, syn := fn.Synthetic == "", fn.Syntax() != nil; src != syn {
if len(fn.typeargs) > 0 && fn.Prog.mode&InstantiateGenerics != 0 {
// ok (instantiation with InstantiateGenerics on)
} else if fn.topLevelOrigin != nil && len(fn.typeargs) > 0 {
// ok (we always have the syntax set for instantiation)
} else if _, rng := fn.syntax.(*ast.RangeStmt); rng && fn.Synthetic == "range-over-func yield" {
// ok (range-func-yields are both synthetic and keep syntax)
} else {
s.errorf("got fromSource=%t, hasSyntax=%t; want same values", src, syn)
}
}
// Build the set of valid referrers.
s.instrs = make(map[Instruction]unit)
// instrs are the instructions that are present in the function.
for instr := range fn.instrs() {
s.instrs[instr] = unit{}
}
// Check all Locals allocations appear in the function instruction.
for i, l := range fn.Locals {
if _, present := s.instrs[l]; !present {
s.warnf("function doesn't contain Local alloc %s", l.Name())
}
if l.Parent() != fn {
s.errorf("Local %s at index %d has wrong parent", l.Name(), i)
}
if l.Heap {
s.errorf("Local %s at index %d has Heap flag set", l.Name(), i)
}
}
for i, p := range fn.Params {
if p.Parent() != fn {
s.errorf("Param %s at index %d has wrong parent", p.Name(), i)
}
// Check common suffix of Signature and Params match type.
if sig := fn.Signature; sig != nil {
j := i - len(fn.Params) + sig.Params().Len() // index within sig.Params
if j < 0 {
continue
}
if !types.Identical(p.Type(), sig.Params().At(j).Type()) {
s.errorf("Param %s at index %d has wrong type (%s, versus %s in Signature)", p.Name(), i, p.Type(), sig.Params().At(j).Type())
}
}
s.checkReferrerList(p)
}
for i, fv := range fn.FreeVars {
if fv.Parent() != fn {
s.errorf("FreeVar %s at index %d has wrong parent", fv.Name(), i)
}
s.checkReferrerList(fv)
}
if fn.Blocks != nil && len(fn.Blocks) == 0 {
// Function _had_ blocks (so it's not external) but
// they were "optimized" away, even the entry block.
s.errorf("Blocks slice is non-nil but empty")
}
for i, b := range fn.Blocks {
if b == nil {
s.warnf("nil *BasicBlock at f.Blocks[%d]", i)
continue
}
s.checkBlock(b, i)
}
if fn.Recover != nil && fn.Blocks[fn.Recover.Index] != fn.Recover {
s.errorf("Recover block is not in Blocks slice")
}
s.block = nil
for i, anon := range fn.AnonFuncs {
if anon.Parent() != fn {
s.errorf("AnonFuncs[%d]=%s but %s.Parent()=%s", i, anon, anon, anon.Parent())
}
if i != int(anon.anonIdx) {
s.errorf("AnonFuncs[%d]=%s but %s.anonIdx=%d", i, anon, anon, anon.anonIdx)
}
}
s.fn = nil
return !s.insane
}
// sanityCheckPackage checks invariants of packages upon creation.
// It does not require that the package is built.
// Unlike sanityCheck (for functions), it just panics at the first error.
func sanityCheckPackage(pkg *Package) {
if pkg.Pkg == nil {
panic(fmt.Sprintf("Package %s has no Object", pkg))
}
if pkg.info != nil {
panic(fmt.Sprintf("package %s field 'info' is not cleared", pkg))
}
if pkg.files != nil {
panic(fmt.Sprintf("package %s field 'files' is not cleared", pkg))
}
if pkg.created != nil {
panic(fmt.Sprintf("package %s field 'created' is not cleared", pkg))
}
if pkg.initVersion != nil {
panic(fmt.Sprintf("package %s field 'initVersion' is not cleared", pkg))
}
_ = pkg.String() // must not crash
for name, mem := range pkg.Members {
if name != mem.Name() {
panic(fmt.Sprintf("%s: %T.Name() = %s, want %s",
pkg.Pkg.Path(), mem, mem.Name(), name))
}
obj := mem.Object()
if obj == nil {
// This check is sound because fields
// {Global,Function}.object have type
// types.Object. (If they were declared as
// *types.{Var,Func}, we'd have a non-empty
// interface containing a nil pointer.)
continue // not all members have typechecker objects
}
if obj.Name() != name {
if obj.Name() == "init" && strings.HasPrefix(mem.Name(), "init#") {
// Ok. The name of a declared init function varies between
// its types.Func ("init") and its ssa.Function ("init#%d").
} else {
panic(fmt.Sprintf("%s: %T.Object().Name() = %s, want %s",
pkg.Pkg.Path(), mem, obj.Name(), name))
}
}
if obj.Pos() != mem.Pos() {
panic(fmt.Sprintf("%s Pos=%d obj.Pos=%d", mem, mem.Pos(), obj.Pos()))
}
}
}

288
vendor/golang.org/x/tools/go/ssa/source.go generated vendored Normal file
View File

@@ -0,0 +1,288 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// This file defines utilities for working with source positions
// or source-level named entities ("objects").
// TODO(adonovan): test that {Value,Instruction}.Pos() positions match
// the originating syntax, as specified.
import (
"go/ast"
"go/token"
"go/types"
)
// EnclosingFunction returns the function that contains the syntax
// node denoted by path.
//
// Syntax associated with package-level variable specifications is
// enclosed by the package's init() function.
//
// Returns nil if not found; reasons might include:
// - the node is not enclosed by any function.
// - the node is within an anonymous function (FuncLit) and
// its SSA function has not been created yet
// (pkg.Build() has not yet been called).
func EnclosingFunction(pkg *Package, path []ast.Node) *Function {
// Start with package-level function...
fn := findEnclosingPackageLevelFunction(pkg, path)
if fn == nil {
return nil // not in any function
}
// ...then walk down the nested anonymous functions.
n := len(path)
outer:
for i := range path {
if lit, ok := path[n-1-i].(*ast.FuncLit); ok {
for _, anon := range fn.AnonFuncs {
if anon.Pos() == lit.Type.Func {
fn = anon
continue outer
}
}
// SSA function not found:
// - package not yet built, or maybe
// - builder skipped FuncLit in dead block
// (in principle; but currently the Builder
// generates even dead FuncLits).
return nil
}
}
return fn
}
// HasEnclosingFunction returns true if the AST node denoted by path
// is contained within the declaration of some function or
// package-level variable.
//
// Unlike EnclosingFunction, the behaviour of this function does not
// depend on whether SSA code for pkg has been built, so it can be
// used to quickly reject check inputs that will cause
// EnclosingFunction to fail, prior to SSA building.
func HasEnclosingFunction(pkg *Package, path []ast.Node) bool {
return findEnclosingPackageLevelFunction(pkg, path) != nil
}
// findEnclosingPackageLevelFunction returns the Function
// corresponding to the package-level function enclosing path.
func findEnclosingPackageLevelFunction(pkg *Package, path []ast.Node) *Function {
if n := len(path); n >= 2 { // [... {Gen,Func}Decl File]
switch decl := path[n-2].(type) {
case *ast.GenDecl:
if decl.Tok == token.VAR && n >= 3 {
// Package-level 'var' initializer.
return pkg.init
}
case *ast.FuncDecl:
if decl.Recv == nil && decl.Name.Name == "init" {
// Explicit init() function.
for _, b := range pkg.init.Blocks {
for _, instr := range b.Instrs {
if instr, ok := instr.(*Call); ok {
if callee, ok := instr.Call.Value.(*Function); ok && callee.Pkg == pkg && callee.Pos() == decl.Name.NamePos {
return callee
}
}
}
}
// Hack: return non-nil when SSA is not yet
// built so that HasEnclosingFunction works.
return pkg.init
}
// Declared function/method.
return findNamedFunc(pkg, decl.Name.NamePos)
}
}
return nil // not in any function
}
// findNamedFunc returns the named function whose FuncDecl.Ident is at
// position pos.
func findNamedFunc(pkg *Package, pos token.Pos) *Function {
// Look at all package members and method sets of named types.
// Not very efficient.
for _, mem := range pkg.Members {
switch mem := mem.(type) {
case *Function:
if mem.Pos() == pos {
return mem
}
case *Type:
mset := pkg.Prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
for i, n := 0, mset.Len(); i < n; i++ {
// Don't call Program.Method: avoid creating wrappers.
obj := mset.At(i).Obj().(*types.Func)
if obj.Pos() == pos {
// obj from MethodSet may not be the origin type.
m := obj.Origin()
return pkg.objects[m].(*Function)
}
}
}
}
return nil
}
// ValueForExpr returns the SSA Value that corresponds to non-constant
// expression e.
//
// It returns nil if no value was found, e.g.
// - the expression is not lexically contained within f;
// - f was not built with debug information; or
// - e is a constant expression. (For efficiency, no debug
// information is stored for constants. Use
// go/types.Info.Types[e].Value instead.)
// - e is a reference to nil or a built-in function.
// - the value was optimised away.
//
// If e is an addressable expression used in an lvalue context,
// value is the address denoted by e, and isAddr is true.
//
// The types of e (or &e, if isAddr) and the result are equal
// (modulo "untyped" bools resulting from comparisons).
//
// (Tip: to find the ssa.Value given a source position, use
// astutil.PathEnclosingInterval to locate the ast.Node, then
// EnclosingFunction to locate the Function, then ValueForExpr to find
// the ssa.Value.)
func (f *Function) ValueForExpr(e ast.Expr) (value Value, isAddr bool) {
if f.debugInfo() { // (opt)
e = ast.Unparen(e)
for _, b := range f.Blocks {
for _, instr := range b.Instrs {
if ref, ok := instr.(*DebugRef); ok {
if ref.Expr == e {
return ref.X, ref.IsAddr
}
}
}
}
}
return
}
// --- Lookup functions for source-level named entities (types.Objects) ---
// Package returns the SSA Package corresponding to the specified
// type-checker package. It returns nil if no such Package was
// created by a prior call to prog.CreatePackage.
func (prog *Program) Package(pkg *types.Package) *Package {
return prog.packages[pkg]
}
// packageLevelMember returns the package-level member corresponding
// to the specified symbol, which may be a package-level const
// (*NamedConst), var (*Global) or func/method (*Function) of some
// package in prog.
//
// It returns nil if the object belongs to a package that has not been
// created by prog.CreatePackage.
func (prog *Program) packageLevelMember(obj types.Object) Member {
if pkg, ok := prog.packages[obj.Pkg()]; ok {
return pkg.objects[obj]
}
return nil
}
// FuncValue returns the SSA function or (non-interface) method
// denoted by the specified func symbol. It returns nil if the symbol
// denotes an interface method, or belongs to a package that was not
// created by prog.CreatePackage.
func (prog *Program) FuncValue(obj *types.Func) *Function {
fn, _ := prog.packageLevelMember(obj).(*Function)
return fn
}
// ConstValue returns the SSA constant denoted by the specified const symbol.
func (prog *Program) ConstValue(obj *types.Const) *Const {
// TODO(adonovan): opt: share (don't reallocate)
// Consts for const objects and constant ast.Exprs.
// Universal constant? {true,false,nil}
if obj.Parent() == types.Universe {
return NewConst(obj.Val(), obj.Type())
}
// Package-level named constant?
if v := prog.packageLevelMember(obj); v != nil {
return v.(*NamedConst).Value
}
return NewConst(obj.Val(), obj.Type())
}
// VarValue returns the SSA Value that corresponds to a specific
// identifier denoting the specified var symbol.
//
// VarValue returns nil if a local variable was not found, perhaps
// because its package was not built, the debug information was not
// requested during SSA construction, or the value was optimized away.
//
// ref is the path to an ast.Ident (e.g. from PathEnclosingInterval),
// and that ident must resolve to obj.
//
// pkg is the package enclosing the reference. (A reference to a var
// always occurs within a function, so we need to know where to find it.)
//
// If the identifier is a field selector and its base expression is
// non-addressable, then VarValue returns the value of that field.
// For example:
//
// func f() struct {x int}
// f().x // VarValue(x) returns a *Field instruction of type int
//
// All other identifiers denote addressable locations (variables).
// For them, VarValue may return either the variable's address or its
// value, even when the expression is evaluated only for its value; the
// situation is reported by isAddr, the second component of the result.
//
// If !isAddr, the returned value is the one associated with the
// specific identifier. For example,
//
// var x int // VarValue(x) returns Const 0 here
// x = 1 // VarValue(x) returns Const 1 here
//
// It is not specified whether the value or the address is returned in
// any particular case, as it may depend upon optimizations performed
// during SSA code generation, such as registerization, constant
// folding, avoidance of materialization of subexpressions, etc.
func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (value Value, isAddr bool) {
// All references to a var are local to some function, possibly init.
fn := EnclosingFunction(pkg, ref)
if fn == nil {
return // e.g. def of struct field; SSA not built?
}
id := ref[0].(*ast.Ident)
// Defining ident of a parameter?
if id.Pos() == obj.Pos() {
for _, param := range fn.Params {
if param.Object() == obj {
return param, false
}
}
}
// Other ident?
for _, b := range fn.Blocks {
for _, instr := range b.Instrs {
if dr, ok := instr.(*DebugRef); ok {
if dr.Pos() == id.Pos() {
return dr.X, dr.IsAddr
}
}
}
}
// Defining ident of package-level var?
if v := prog.packageLevelMember(obj); v != nil {
return v.(*Global), true
}
return // e.g. debug info not requested, or var optimized away
}

1873
vendor/golang.org/x/tools/go/ssa/ssa.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

36
vendor/golang.org/x/tools/go/ssa/ssautil/deprecated.go generated vendored Normal file
View File

@@ -0,0 +1,36 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssautil
// This file contains deprecated public APIs.
// We discourage their use.
import (
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
)
// CreateProgram returns a new program in SSA form, given a program
// loaded from source. An SSA package is created for each transitively
// error-free package of lprog.
//
// Code for bodies of functions is not built until Build is called
// on the result.
//
// The mode parameter controls diagnostics and checking during SSA construction.
//
// Deprecated: Use [golang.org/x/tools/go/packages] and the [Packages]
// function instead; see ssa.Example_loadPackages.
func CreateProgram(lprog *loader.Program, mode ssa.BuilderMode) *ssa.Program {
prog := ssa.NewProgram(lprog.Fset, mode)
for _, info := range lprog.AllPackages {
if info.TransitivelyErrorFree {
prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable)
}
}
return prog
}

189
vendor/golang.org/x/tools/go/ssa/ssautil/load.go generated vendored Normal file
View File

@@ -0,0 +1,189 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssautil
// This file defines utility functions for constructing programs in SSA form.
import (
"go/ast"
"go/token"
"go/types"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/ssa"
)
// Packages creates an SSA program for a set of packages.
//
// The packages must have been loaded from source syntax using the
// [packages.Load] function in [packages.LoadSyntax] or
// [packages.LoadAllSyntax] mode.
//
// Packages creates an SSA package for each well-typed package in the
// initial list, plus all their dependencies. The resulting list of
// packages corresponds to the list of initial packages, and may contain
// a nil if SSA code could not be constructed for the corresponding initial
// package due to type errors.
//
// Code for bodies of functions is not built until [Program.Build] is
// called on the resulting Program. SSA code is constructed only for
// the initial packages with well-typed syntax trees.
//
// The mode parameter controls diagnostics and checking during SSA construction.
func Packages(initial []*packages.Package, mode ssa.BuilderMode) (*ssa.Program, []*ssa.Package) {
// TODO(adonovan): opt: this calls CreatePackage far more than
// necessary: for all dependencies, not just the (non-initial)
// direct dependencies of the initial packages.
//
// But can it reasonably be changed without breaking the
// spirit and/or letter of the law above? Clients may notice
// if we call CreatePackage less, as methods like
// Program.FuncValue will return nil. Or must we provide a new
// function (and perhaps deprecate this one)? Is it worth it?
//
// Tim King makes the interesting point that it would be
// possible to entirely alleviate the client from the burden
// of calling CreatePackage for non-syntax packages, if we
// were to treat vars and funcs lazily in the same way we now
// treat methods. (In essence, try to move away from the
// notion of ssa.Packages, and make the Program answer
// all reasonable questions about any types.Object.)
return doPackages(initial, mode, false)
}
// AllPackages creates an SSA program for a set of packages plus all
// their dependencies.
//
// The packages must have been loaded from source syntax using the
// [packages.Load] function in [packages.LoadAllSyntax] mode.
//
// AllPackages creates an SSA package for each well-typed package in the
// initial list, plus all their dependencies. The resulting list of
// packages corresponds to the list of initial packages, and may contain
// a nil if SSA code could not be constructed for the corresponding
// initial package due to type errors.
//
// Code for bodies of functions is not built until Build is called on
// the resulting Program. SSA code is constructed for all packages with
// well-typed syntax trees.
//
// The mode parameter controls diagnostics and checking during SSA construction.
func AllPackages(initial []*packages.Package, mode ssa.BuilderMode) (*ssa.Program, []*ssa.Package) {
return doPackages(initial, mode, true)
}
func doPackages(initial []*packages.Package, mode ssa.BuilderMode, deps bool) (*ssa.Program, []*ssa.Package) {
var fset *token.FileSet
if len(initial) > 0 {
fset = initial[0].Fset
}
prog := ssa.NewProgram(fset, mode)
isInitial := make(map[*packages.Package]bool, len(initial))
for _, p := range initial {
isInitial[p] = true
}
ssamap := make(map[*packages.Package]*ssa.Package)
packages.Visit(initial, nil, func(p *packages.Package) {
if p.Types != nil && !p.IllTyped {
var files []*ast.File
var info *types.Info
if deps || isInitial[p] {
files = p.Syntax
info = p.TypesInfo
}
ssamap[p] = prog.CreatePackage(p.Types, files, info, true)
}
})
var ssapkgs []*ssa.Package
for _, p := range initial {
ssapkgs = append(ssapkgs, ssamap[p]) // may be nil
}
return prog, ssapkgs
}
// BuildPackage builds an SSA program with SSA intermediate
// representation (IR) for all functions of a single package.
//
// It populates pkg by type-checking the specified file syntax trees. All
// dependencies are loaded using the importer specified by tc, which
// typically loads compiler export data; SSA code cannot be built for
// those packages. BuildPackage then constructs an [ssa.Program] with all
// dependency packages created, and builds and returns the SSA package
// corresponding to pkg.
//
// The caller must have set pkg.Path to the import path.
//
// The operation fails if there were any type-checking or import errors.
//
// See ../example_test.go for an example.
func BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, files []*ast.File, mode ssa.BuilderMode) (*ssa.Package, *types.Info, error) {
if fset == nil {
panic("no token.FileSet")
}
if pkg.Path() == "" {
panic("package has no import path")
}
info := &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
Implicits: make(map[ast.Node]types.Object),
Instances: make(map[*ast.Ident]types.Instance),
Scopes: make(map[ast.Node]*types.Scope),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
FileVersions: make(map[*ast.File]string),
}
if err := types.NewChecker(tc, fset, pkg, info).Files(files); err != nil {
return nil, nil, err
}
prog := ssa.NewProgram(fset, mode)
// Create SSA packages for all imports.
// Order is not significant.
created := make(map[*types.Package]bool)
var createAll func(pkgs []*types.Package)
createAll = func(pkgs []*types.Package) {
for _, p := range pkgs {
if !created[p] {
created[p] = true
prog.CreatePackage(p, nil, nil, true)
createAll(p.Imports())
}
}
}
createAll(pkg.Imports())
// TODO(adonovan): we could replace createAll with just:
//
// // Create SSA packages for all imports.
// for _, p := range pkg.Imports() {
// prog.CreatePackage(p, nil, nil, true)
// }
//
// (with minor changes to changes to ../builder_test.go as
// shown in CL 511715 PS 10.) But this would strictly violate
// the letter of the doc comment above, which says "all
// dependencies created".
//
// Tim makes the good point with some extra work we could
// remove the need for any CreatePackage calls except the
// ones with syntax (i.e. primary packages). Of course
// You wouldn't have ssa.Packages and Members for as
// many things but no-one really uses that anyway.
// I wish I had done this from the outset.
// Create and build the primary package.
ssapkg := prog.CreatePackage(pkg, files, info, false)
ssapkg.Build()
return ssapkg, info, nil
}

230
vendor/golang.org/x/tools/go/ssa/ssautil/switch.go generated vendored Normal file
View File

@@ -0,0 +1,230 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssautil
// This file implements discovery of switch and type-switch constructs
// from low-level control flow.
//
// Many techniques exist for compiling a high-level switch with
// constant cases to efficient machine code. The optimal choice will
// depend on the data type, the specific case values, the code in the
// body of each case, and the hardware.
// Some examples:
// - a lookup table (for a switch that maps constants to constants)
// - a computed goto
// - a binary tree
// - a perfect hash
// - a two-level switch (to partition constant strings by their first byte).
import (
"bytes"
"fmt"
"go/token"
"go/types"
"golang.org/x/tools/go/ssa"
)
// A ConstCase represents a single constant comparison.
// It is part of a Switch.
type ConstCase struct {
Block *ssa.BasicBlock // block performing the comparison
Body *ssa.BasicBlock // body of the case
Value *ssa.Const // case comparand
}
// A TypeCase represents a single type assertion.
// It is part of a Switch.
type TypeCase struct {
Block *ssa.BasicBlock // block performing the type assert
Body *ssa.BasicBlock // body of the case
Type types.Type // case type
Binding ssa.Value // value bound by this case
}
// A Switch is a logical high-level control flow operation
// (a multiway branch) discovered by analysis of a CFG containing
// only if/else chains. It is not part of the ssa.Instruction set.
//
// One of ConstCases and TypeCases has length >= 2;
// the other is nil.
//
// In a value switch, the list of cases may contain duplicate constants.
// A type switch may contain duplicate types, or types assignable
// to an interface type also in the list.
// TODO(adonovan): eliminate such duplicates.
type Switch struct {
Start *ssa.BasicBlock // block containing start of if/else chain
X ssa.Value // the switch operand
ConstCases []ConstCase // ordered list of constant comparisons
TypeCases []TypeCase // ordered list of type assertions
Default *ssa.BasicBlock // successor if all comparisons fail
}
func (sw *Switch) String() string {
// We represent each block by the String() of its
// first Instruction, e.g. "print(42:int)".
var buf bytes.Buffer
if sw.ConstCases != nil {
fmt.Fprintf(&buf, "switch %s {\n", sw.X.Name())
for _, c := range sw.ConstCases {
fmt.Fprintf(&buf, "case %s: %s\n", c.Value, c.Body.Instrs[0])
}
} else {
fmt.Fprintf(&buf, "switch %s.(type) {\n", sw.X.Name())
for _, c := range sw.TypeCases {
fmt.Fprintf(&buf, "case %s %s: %s\n",
c.Binding.Name(), c.Type, c.Body.Instrs[0])
}
}
if sw.Default != nil {
fmt.Fprintf(&buf, "default: %s\n", sw.Default.Instrs[0])
}
fmt.Fprintf(&buf, "}")
return buf.String()
}
// Switches examines the control-flow graph of fn and returns the
// set of inferred value and type switches. A value switch tests an
// ssa.Value for equality against two or more compile-time constant
// values. Switches involving link-time constants (addresses) are
// ignored. A type switch type-asserts an ssa.Value against two or
// more types.
//
// The switches are returned in dominance order.
//
// The resulting switches do not necessarily correspond to uses of the
// 'switch' keyword in the source: for example, a single source-level
// switch statement with non-constant cases may result in zero, one or
// many Switches, one per plural sequence of constant cases.
// Switches may even be inferred from if/else- or goto-based control flow.
// (In general, the control flow constructs of the source program
// cannot be faithfully reproduced from the SSA representation.)
func Switches(fn *ssa.Function) []Switch {
// Traverse the CFG in dominance order, so we don't
// enter an if/else-chain in the middle.
var switches []Switch
seen := make(map[*ssa.BasicBlock]bool) // TODO(adonovan): opt: use ssa.blockSet
for _, b := range fn.DomPreorder() {
if x, k := isComparisonBlock(b); x != nil {
// Block b starts a switch.
sw := Switch{Start: b, X: x}
valueSwitch(&sw, k, seen)
if len(sw.ConstCases) > 1 {
switches = append(switches, sw)
}
}
if y, x, T := isTypeAssertBlock(b); y != nil {
// Block b starts a type switch.
sw := Switch{Start: b, X: x}
typeSwitch(&sw, y, T, seen)
if len(sw.TypeCases) > 1 {
switches = append(switches, sw)
}
}
}
return switches
}
func valueSwitch(sw *Switch, k *ssa.Const, seen map[*ssa.BasicBlock]bool) {
b := sw.Start
x := sw.X
for x == sw.X {
if seen[b] {
break
}
seen[b] = true
sw.ConstCases = append(sw.ConstCases, ConstCase{
Block: b,
Body: b.Succs[0],
Value: k,
})
b = b.Succs[1]
if len(b.Instrs) > 2 {
// Block b contains not just 'if x == k',
// so it may have side effects that
// make it unsafe to elide.
break
}
if len(b.Preds) != 1 {
// Block b has multiple predecessors,
// so it cannot be treated as a case.
break
}
x, k = isComparisonBlock(b)
}
sw.Default = b
}
func typeSwitch(sw *Switch, y ssa.Value, T types.Type, seen map[*ssa.BasicBlock]bool) {
b := sw.Start
x := sw.X
for x == sw.X {
if seen[b] {
break
}
seen[b] = true
sw.TypeCases = append(sw.TypeCases, TypeCase{
Block: b,
Body: b.Succs[0],
Type: T,
Binding: y,
})
b = b.Succs[1]
if len(b.Instrs) > 4 {
// Block b contains not just
// {TypeAssert; Extract #0; Extract #1; If}
// so it may have side effects that
// make it unsafe to elide.
break
}
if len(b.Preds) != 1 {
// Block b has multiple predecessors,
// so it cannot be treated as a case.
break
}
y, x, T = isTypeAssertBlock(b)
}
sw.Default = b
}
// isComparisonBlock returns the operands (v, k) if a block ends with
// a comparison v==k, where k is a compile-time constant.
func isComparisonBlock(b *ssa.BasicBlock) (v ssa.Value, k *ssa.Const) {
if n := len(b.Instrs); n >= 2 {
if i, ok := b.Instrs[n-1].(*ssa.If); ok {
if binop, ok := i.Cond.(*ssa.BinOp); ok && binop.Block() == b && binop.Op == token.EQL {
if k, ok := binop.Y.(*ssa.Const); ok {
return binop.X, k
}
if k, ok := binop.X.(*ssa.Const); ok {
return binop.Y, k
}
}
}
}
return
}
// isTypeAssertBlock returns the operands (y, x, T) if a block ends with
// a type assertion "if y, ok := x.(T); ok {".
func isTypeAssertBlock(b *ssa.BasicBlock) (y, x ssa.Value, T types.Type) {
if n := len(b.Instrs); n >= 4 {
if i, ok := b.Instrs[n-1].(*ssa.If); ok {
if ext1, ok := i.Cond.(*ssa.Extract); ok && ext1.Block() == b && ext1.Index == 1 {
if ta, ok := ext1.Tuple.(*ssa.TypeAssert); ok && ta.Block() == b {
// hack: relies upon instruction ordering.
if ext0, ok := b.Instrs[n-3].(*ssa.Extract); ok {
return ext0, ta.X, ta.AssertedType
}
}
}
}
}
return
}

157
vendor/golang.org/x/tools/go/ssa/ssautil/visit.go generated vendored Normal file
View File

@@ -0,0 +1,157 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssautil // import "golang.org/x/tools/go/ssa/ssautil"
import (
"go/ast"
"go/types"
"golang.org/x/tools/go/ssa"
_ "unsafe" // for linkname hack
)
// This file defines utilities for visiting the SSA representation of
// a Program.
//
// TODO(adonovan): test coverage.
// AllFunctions finds and returns the set of functions potentially
// needed by program prog, as determined by a simple linker-style
// reachability algorithm starting from the members and method-sets of
// each package. The result may include anonymous functions and
// synthetic wrappers.
//
// Precondition: all packages are built.
//
// TODO(adonovan): this function is underspecified. It doesn't
// actually work like a linker, which computes reachability from main
// using something like go/callgraph/cha (without materializing the
// call graph). In fact, it treats all public functions and all
// methods of public non-parameterized types as roots, even though
// they may be unreachable--but only in packages created from syntax.
//
// I think we should deprecate AllFunctions function in favor of two
// clearly defined ones:
//
// 1. The first would efficiently compute CHA reachability from a set
// of main packages, making it suitable for a whole-program
// analysis context with InstantiateGenerics, in conjunction with
// Program.Build.
//
// 2. The second would return only the set of functions corresponding
// to source Func{Decl,Lit} syntax, like SrcFunctions in
// go/analysis/passes/buildssa; this is suitable for
// package-at-a-time (or handful of packages) context.
// ssa.Package could easily expose it as a field.
//
// We could add them unexported for now and use them via the linkname hack.
func AllFunctions(prog *ssa.Program) map[*ssa.Function]bool {
seen := make(map[*ssa.Function]bool)
var function func(fn *ssa.Function)
function = func(fn *ssa.Function) {
if !seen[fn] {
seen[fn] = true
var buf [10]*ssa.Value // avoid alloc in common case
for _, b := range fn.Blocks {
for _, instr := range b.Instrs {
for _, op := range instr.Operands(buf[:0]) {
if fn, ok := (*op).(*ssa.Function); ok {
function(fn)
}
}
}
}
}
}
// TODO(adonovan): opt: provide a way to share a builder
// across a sequence of MethodValue calls.
methodsOf := func(T types.Type) {
if !types.IsInterface(T) {
mset := prog.MethodSets.MethodSet(T)
for method := range mset.Methods() {
function(prog.MethodValue(method))
}
}
}
// Historically, Program.RuntimeTypes used to include the type
// of any exported member of a package loaded from syntax that
// has a non-parameterized type, plus all types
// reachable from that type using reflection, even though
// these runtime types may not be required for them.
//
// Rather than break existing programs that rely on
// AllFunctions visiting extra methods that are unreferenced
// by IR and unreachable via reflection, we moved the logic
// here, unprincipled though it is.
// (See doc comment for better ideas.)
//
// Nonetheless, after the move, we no longer visit every
// method of any type recursively reachable from T, only the
// methods of T and *T themselves, and we only apply this to
// named types T, and not to the type of every exported
// package member.
exportedTypeHack := func(t *ssa.Type) {
if isSyntactic(t.Package()) &&
ast.IsExported(t.Name()) &&
!types.IsInterface(t.Type()) {
// Consider only named types.
// (Ignore aliases and unsafe.Pointer.)
if named, ok := t.Type().(*types.Named); ok {
if named.TypeParams() == nil {
methodsOf(named) // T
methodsOf(types.NewPointer(named)) // *T
}
}
}
}
for _, pkg := range prog.AllPackages() {
for _, mem := range pkg.Members {
switch mem := mem.(type) {
case *ssa.Function:
// Visit all package-level declared functions.
function(mem)
case *ssa.Type:
exportedTypeHack(mem)
}
}
}
// Visit all methods of types for which runtime types were
// materialized, as they are reachable through reflection.
for _, T := range prog.RuntimeTypes() {
methodsOf(T)
}
return seen
}
// MainPackages returns the subset of the specified packages
// named "main" that define a main function.
// The result may include synthetic "testmain" packages.
func MainPackages(pkgs []*ssa.Package) []*ssa.Package {
var mains []*ssa.Package
for _, pkg := range pkgs {
if pkg.Pkg.Name() == "main" && pkg.Func("main") != nil {
mains = append(mains, pkg)
}
}
return mains
}
// TODO(adonovan): propose a principled API for this. One possibility
// is a new field, Package.SrcFunctions []*Function, which would
// contain the list of SrcFunctions described in point 2 of the
// AllFunctions doc comment, or nil if the package is not from syntax.
// But perhaps overloading nil vs empty slice is too subtle.
//
//go:linkname isSyntactic golang.org/x/tools/go/ssa.isSyntactic
func isSyntactic(pkg *ssa.Package) bool

567
vendor/golang.org/x/tools/go/ssa/subst.go generated vendored Normal file
View File

@@ -0,0 +1,567 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
import (
"go/types"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/aliases"
)
// subster defines a type substitution operation of a set of type parameters
// to type parameter free replacement types. Substitution is done within
// the context of a package-level function instantiation. *Named types
// declared in the function are unique to the instantiation.
//
// For example, given a parameterized function F
//
// func F[S, T any]() any {
// type X struct{ s S; next *X }
// var p *X
// return p
// }
//
// calling the instantiation F[string, int]() returns an interface
// value (*X[string,int], nil) where the underlying value of
// X[string,int] is a struct{s string; next *X[string,int]}.
//
// A nil *subster is a valid, empty substitution map. It always acts as
// the identity function. This allows for treating parameterized and
// non-parameterized functions identically while compiling to ssa.
//
// Not concurrency-safe.
//
// Note: Some may find it helpful to think through some of the most
// complex substitution cases using lambda calculus inspired notation.
// subst.typ() solves evaluating a type expression E
// within the body of a function Fn[m] with the type parameters m
// once we have applied the type arguments N.
// We can succinctly write this as a function application:
//
// ((λm. E) N)
//
// go/types does not provide this interface directly.
// So what subster provides is a type substitution operation
//
// E[m:=N]
type subster struct {
replacements map[*types.TypeParam]types.Type // values should contain no type params
cache map[types.Type]types.Type // cache of subst results
origin *types.Func // types.Objects declared within this origin function are unique within this context
ctxt *types.Context // speeds up repeated instantiations
uniqueness typeutil.Map // determines the uniqueness of the instantiations within the function
// TODO(taking): consider adding Pos
}
// Returns a subster that replaces tparams[i] with targs[i]. Uses ctxt as a cache.
// targs should not contain any types in tparams.
// fn is the generic function for which we are substituting.
func makeSubster(ctxt *types.Context, fn *types.Func, tparams *types.TypeParamList, targs []types.Type) *subster {
assert(tparams.Len() == len(targs), "makeSubster argument count must match")
subst := &subster{
replacements: make(map[*types.TypeParam]types.Type, tparams.Len()),
cache: make(map[types.Type]types.Type),
origin: fn.Origin(),
ctxt: ctxt,
}
for i := 0; i < tparams.Len(); i++ {
subst.replacements[tparams.At(i)] = targs[i]
}
return subst
}
// typ returns the type of t with the type parameter tparams[i] substituted
// for the type targs[i] where subst was created using tparams and targs.
func (subst *subster) typ(t types.Type) (res types.Type) {
if subst == nil {
return t // A nil subst is type preserving.
}
if r, ok := subst.cache[t]; ok {
return r
}
defer func() {
subst.cache[t] = res
}()
switch t := t.(type) {
case *types.TypeParam:
if r := subst.replacements[t]; r != nil {
return r
}
return t
case *types.Basic:
return t
case *types.Array:
if r := subst.typ(t.Elem()); r != t.Elem() {
return types.NewArray(r, t.Len())
}
return t
case *types.Slice:
if r := subst.typ(t.Elem()); r != t.Elem() {
return types.NewSlice(r)
}
return t
case *types.Pointer:
if r := subst.typ(t.Elem()); r != t.Elem() {
return types.NewPointer(r)
}
return t
case *types.Tuple:
return subst.tuple(t)
case *types.Struct:
return subst.struct_(t)
case *types.Map:
key := subst.typ(t.Key())
elem := subst.typ(t.Elem())
if key != t.Key() || elem != t.Elem() {
return types.NewMap(key, elem)
}
return t
case *types.Chan:
if elem := subst.typ(t.Elem()); elem != t.Elem() {
return types.NewChan(t.Dir(), elem)
}
return t
case *types.Signature:
return subst.signature(t)
case *types.Union:
return subst.union(t)
case *types.Interface:
return subst.interface_(t)
case *types.Alias:
return subst.alias(t)
case *types.Named:
return subst.named(t)
case *opaqueType:
return t // opaque types are never substituted
default:
panic("unreachable")
}
}
// types returns the result of {subst.typ(ts[i])}.
func (subst *subster) types(ts []types.Type) []types.Type {
res := make([]types.Type, len(ts))
for i := range ts {
res[i] = subst.typ(ts[i])
}
return res
}
func (subst *subster) tuple(t *types.Tuple) *types.Tuple {
if t != nil {
if vars := subst.varlist(t); vars != nil {
return types.NewTuple(vars...)
}
}
return t
}
type varlist interface {
At(i int) *types.Var
Len() int
}
// fieldlist is an adapter for structs for the varlist interface.
type fieldlist struct {
str *types.Struct
}
func (fl fieldlist) At(i int) *types.Var { return fl.str.Field(i) }
func (fl fieldlist) Len() int { return fl.str.NumFields() }
func (subst *subster) struct_(t *types.Struct) *types.Struct {
if t != nil {
if fields := subst.varlist(fieldlist{t}); fields != nil {
tags := make([]string, t.NumFields())
for i, n := 0, t.NumFields(); i < n; i++ {
tags[i] = t.Tag(i)
}
return types.NewStruct(fields, tags)
}
}
return t
}
// varlist returns subst(in[i]) or return nils if subst(v[i]) == v[i] for all i.
func (subst *subster) varlist(in varlist) []*types.Var {
var out []*types.Var // nil => no updates
for i, n := 0, in.Len(); i < n; i++ {
v := in.At(i)
w := subst.var_(v)
if v != w && out == nil {
out = make([]*types.Var, n)
for j := 0; j < i; j++ {
out[j] = in.At(j)
}
}
if out != nil {
out[i] = w
}
}
return out
}
func (subst *subster) var_(v *types.Var) *types.Var {
if v != nil {
if typ := subst.typ(v.Type()); typ != v.Type() {
if v.IsField() {
return types.NewField(v.Pos(), v.Pkg(), v.Name(), typ, v.Embedded())
}
return types.NewParam(v.Pos(), v.Pkg(), v.Name(), typ)
}
}
return v
}
func (subst *subster) union(u *types.Union) *types.Union {
var out []*types.Term // nil => no updates
for i, n := 0, u.Len(); i < n; i++ {
t := u.Term(i)
r := subst.typ(t.Type())
if r != t.Type() && out == nil {
out = make([]*types.Term, n)
for j := 0; j < i; j++ {
out[j] = u.Term(j)
}
}
if out != nil {
out[i] = types.NewTerm(t.Tilde(), r)
}
}
if out != nil {
return types.NewUnion(out)
}
return u
}
func (subst *subster) interface_(iface *types.Interface) *types.Interface {
if iface == nil {
return nil
}
// methods for the interface. Initially nil if there is no known change needed.
// Signatures for the method where recv is nil. NewInterfaceType fills in the receivers.
var methods []*types.Func
initMethods := func(n int) { // copy first n explicit methods
methods = make([]*types.Func, iface.NumExplicitMethods())
for i := range n {
f := iface.ExplicitMethod(i)
norecv := changeRecv(f.Type().(*types.Signature), nil)
methods[i] = types.NewFunc(f.Pos(), f.Pkg(), f.Name(), norecv)
}
}
for i := 0; i < iface.NumExplicitMethods(); i++ {
f := iface.ExplicitMethod(i)
// On interfaces, we need to cycle break on anonymous interface types
// being in a cycle with their signatures being in cycles with their receivers
// that do not go through a Named.
norecv := changeRecv(f.Type().(*types.Signature), nil)
sig := subst.typ(norecv)
if sig != norecv && methods == nil {
initMethods(i)
}
if methods != nil {
methods[i] = types.NewFunc(f.Pos(), f.Pkg(), f.Name(), sig.(*types.Signature))
}
}
var embeds []types.Type
initEmbeds := func(n int) { // copy first n embedded types
embeds = make([]types.Type, iface.NumEmbeddeds())
for i := range n {
embeds[i] = iface.EmbeddedType(i)
}
}
for i := 0; i < iface.NumEmbeddeds(); i++ {
e := iface.EmbeddedType(i)
r := subst.typ(e)
if e != r && embeds == nil {
initEmbeds(i)
}
if embeds != nil {
embeds[i] = r
}
}
if methods == nil && embeds == nil {
return iface
}
if methods == nil {
initMethods(iface.NumExplicitMethods())
}
if embeds == nil {
initEmbeds(iface.NumEmbeddeds())
}
return types.NewInterfaceType(methods, embeds).Complete()
}
func (subst *subster) alias(t *types.Alias) types.Type {
// See subster.named. This follows the same strategy.
tparams := aliases.TypeParams(t)
targs := aliases.TypeArgs(t)
tname := t.Obj()
torigin := aliases.Origin(t)
if !declaredWithin(tname, subst.origin) {
// t is declared outside of the function origin. So t is a package level type alias.
if targs.Len() == 0 {
// No type arguments so no instantiation needed.
return t
}
// Instantiate with the substituted type arguments.
newTArgs := subst.typelist(targs)
return subst.instantiate(torigin, newTArgs)
}
if targs.Len() == 0 {
// t is declared within the function origin and has no type arguments.
//
// Example: This corresponds to A or B in F, but not A[int]:
//
// func F[T any]() {
// type A[S any] = struct{t T, s S}
// type B = T
// var x A[int]
// ...
// }
//
// This is somewhat different than *Named as *Alias cannot be created recursively.
// Copy and substitute type params.
var newTParams []*types.TypeParam
for cur := range tparams.TypeParams() {
cobj := cur.Obj()
cname := types.NewTypeName(cobj.Pos(), cobj.Pkg(), cobj.Name(), nil)
ntp := types.NewTypeParam(cname, nil)
subst.cache[cur] = ntp // See the comment "Note: Subtle" in subster.named.
newTParams = append(newTParams, ntp)
}
// Substitute rhs.
rhs := subst.typ(aliases.Rhs(t))
// Create the fresh alias.
//
// Until 1.27, the result of aliases.NewAlias(...).Type() cannot guarantee it is a *types.Alias.
// However, as t is an *alias.Alias and t is well-typed, then aliases must have been enabled.
// Follow this decision, and always enable aliases here.
const enabled = true
obj := aliases.NewAlias(enabled, tname.Pos(), tname.Pkg(), tname.Name(), rhs, newTParams)
// Substitute into all of the constraints after they are created.
for i, ntp := range newTParams {
bound := tparams.At(i).Constraint()
ntp.SetConstraint(subst.typ(bound))
}
return obj.Type()
}
// t is declared within the function origin and has type arguments.
//
// Example: This corresponds to A[int] in F. Cases A and B are handled above.
// func F[T any]() {
// type A[S any] = struct{t T, s S}
// type B = T
// var x A[int]
// ...
// }
subOrigin := subst.typ(torigin)
subTArgs := subst.typelist(targs)
return subst.instantiate(subOrigin, subTArgs)
}
func (subst *subster) named(t *types.Named) types.Type {
// A Named type is a user defined type.
// Ignoring generics, Named types are canonical: they are identical if
// and only if they have the same defining symbol.
// Generics complicate things, both if the type definition itself is
// parameterized, and if the type is defined within the scope of a
// parameterized function. In this case, two named types are identical if
// and only if their identifying symbols are identical, and all type
// arguments bindings in scope of the named type definition (including the
// type parameters of the definition itself) are equivalent.
//
// Notably:
// 1. For type definition type T[P1 any] struct{}, T[A] and T[B] are identical
// only if A and B are identical.
// 2. Inside the generic func Fn[m any]() any { type T struct{}; return T{} },
// the result of Fn[A] and Fn[B] have identical type if and only if A and
// B are identical.
// 3. Both 1 and 2 could apply, such as in
// func F[m any]() any { type T[x any] struct{}; return T{} }
//
// A subster replaces type parameters within a function scope, and therefore must
// also replace free type parameters in the definitions of local types.
//
// Note: There are some detailed notes sprinkled throughout that borrow from
// lambda calculus notation. These contain some over simplifying math.
//
// LC: One way to think about subster is that it is a way of evaluating
// ((λm. E) N) as E[m:=N].
// Each Named type t has an object *TypeName within a scope S that binds an
// underlying type expression U. U can refer to symbols within S (+ S's ancestors).
// Let x = t.TypeParams() and A = t.TypeArgs().
// Each Named type t is then either:
// U where len(x) == 0 && len(A) == 0
// λx. U where len(x) != 0 && len(A) == 0
// ((λx. U) A) where len(x) == len(A)
// In each case, we will evaluate t[m:=N].
tparams := t.TypeParams() // x
targs := t.TypeArgs() // A
if !declaredWithin(t.Obj(), subst.origin) {
// t is declared outside of Fn[m].
//
// In this case, we can skip substituting t.Underlying().
// The underlying type cannot refer to the type parameters.
//
// LC: Let free(E) be the set of free type parameters in an expression E.
// Then whenever m ∉ free(E), then E = E[m:=N].
// t ∉ Scope(fn) so therefore m ∉ free(U) and m ∩ x = ∅.
if targs.Len() == 0 {
// t has no type arguments. So it does not need to be instantiated.
//
// This is the normal case in real Go code, where t is not parameterized,
// declared at some package scope, and m is a TypeParam from a parameterized
// function F[m] or method.
//
// LC: m ∉ free(A) lets us conclude m ∉ free(t). So t=t[m:=N].
return t
}
// t is declared outside of Fn[m] and has type arguments.
// The type arguments may contain type parameters m so
// substitute the type arguments, and instantiate the substituted
// type arguments.
//
// LC: Evaluate this as ((λx. U) A') where A' = A[m := N].
newTArgs := subst.typelist(targs)
return subst.instantiate(t.Origin(), newTArgs)
}
// t is declared within Fn[m].
if targs.Len() == 0 { // no type arguments?
assert(t == t.Origin(), "local parameterized type abstraction must be an origin type")
// t has no type arguments.
// The underlying type of t may contain the function's type parameters,
// replace these, and create a new type.
//
// Subtle: We short circuit substitution and use a newly created type in
// subst, i.e. cache[t]=fresh, to preemptively replace t with fresh
// in recursive types during traversal. This both breaks infinite cycles
// and allows for constructing types with the replacement applied in
// subst.typ(U).
//
// A new copy of the Named and Typename (and constraints) per function
// instantiation matches the semantics of Go, which treats all function
// instantiations F[N] as having distinct local types.
//
// LC: x.Len()=0 can be thought of as a special case of λx. U.
// LC: Evaluate (λx. U)[m:=N] as (λx'. U') where U'=U[x:=x',m:=N].
tname := t.Obj()
obj := types.NewTypeName(tname.Pos(), tname.Pkg(), tname.Name(), nil)
fresh := types.NewNamed(obj, nil, nil)
var newTParams []*types.TypeParam
for cur := range tparams.TypeParams() {
cobj := cur.Obj()
cname := types.NewTypeName(cobj.Pos(), cobj.Pkg(), cobj.Name(), nil)
ntp := types.NewTypeParam(cname, nil)
subst.cache[cur] = ntp
newTParams = append(newTParams, ntp)
}
fresh.SetTypeParams(newTParams)
subst.cache[t] = fresh
subst.cache[fresh] = fresh
fresh.SetUnderlying(subst.typ(t.Underlying()))
// Substitute into all of the constraints after they are created.
for i, ntp := range newTParams {
bound := tparams.At(i).Constraint()
ntp.SetConstraint(subst.typ(bound))
}
return fresh
}
// t is defined within Fn[m] and t has type arguments (an instantiation).
// We reduce this to the two cases above:
// (1) substitute the function's type parameters into t.Origin().
// (2) substitute t's type arguments A and instantiate the updated t.Origin() with these.
//
// LC: Evaluate ((λx. U) A)[m:=N] as (t' A') where t' = (λx. U)[m:=N] and A'=A [m:=N]
subOrigin := subst.typ(t.Origin())
subTArgs := subst.typelist(targs)
return subst.instantiate(subOrigin, subTArgs)
}
func (subst *subster) instantiate(orig types.Type, targs []types.Type) types.Type {
i, err := types.Instantiate(subst.ctxt, orig, targs, false)
assert(err == nil, "failed to Instantiate named (Named or Alias) type")
if c, _ := subst.uniqueness.At(i).(types.Type); c != nil {
return c.(types.Type)
}
subst.uniqueness.Set(i, i)
return i
}
func (subst *subster) typelist(l *types.TypeList) []types.Type {
res := make([]types.Type, l.Len())
for i := 0; i < l.Len(); i++ {
res[i] = subst.typ(l.At(i))
}
return res
}
func (subst *subster) signature(t *types.Signature) types.Type {
tparams := t.TypeParams()
// We are choosing not to support tparams.Len() > 0 until a need has been observed in practice.
//
// There are some known usages for types.Types coming from types.{Eval,CheckExpr}.
// To support tparams.Len() > 0, we just need to do the following [pseudocode]:
// targs := {subst.replacements[tparams[i]]]}; Instantiate(ctxt, t, targs, false)
assert(tparams.Len() == 0, "Substituting types.Signatures with generic functions are currently unsupported.")
// Either:
// (1)non-generic function.
// no type params to substitute
// (2)generic method and recv needs to be substituted.
// Receivers can be either:
// named
// pointer to named
// interface
// nil
// interface is the problematic case. We need to cycle break there!
recv := subst.var_(t.Recv())
params := subst.tuple(t.Params())
results := subst.tuple(t.Results())
if recv != t.Recv() || params != t.Params() || results != t.Results() {
return types.NewSignatureType(recv, nil, nil, params, results, t.Variadic())
}
return t
}

103
vendor/golang.org/x/tools/go/ssa/task.go generated vendored Normal file
View File

@@ -0,0 +1,103 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
import (
"sync/atomic"
)
// Each task has two states: it is initially "active",
// and transitions to "done".
//
// tasks form a directed graph. An edge from x to y (with y in x.edges)
// indicates that the task x waits on the task y to be done.
// Cycles are permitted.
//
// Calling x.wait() blocks the calling goroutine until task x,
// and all the tasks transitively reachable from x are done.
//
// The nil *task is always considered done.
type task struct {
done chan unit // close when the task is done.
edges map[*task]unit // set of predecessors of this task.
transitive atomic.Bool // true once it is known all predecessors are done.
}
func (x *task) isTransitivelyDone() bool { return x == nil || x.transitive.Load() }
// addEdge creates an edge from x to y, indicating that
// x.wait() will not return before y is done.
// All calls to x.addEdge(...) should happen before x.markDone().
func (x *task) addEdge(y *task) {
if x == y || y.isTransitivelyDone() {
return // no work remaining
}
// heuristic done check
select {
case <-x.done:
panic("cannot add an edge to a done task")
default:
}
if x.edges == nil {
x.edges = make(map[*task]unit)
}
x.edges[y] = unit{}
}
// markDone changes the task's state to markDone.
func (x *task) markDone() {
if x != nil {
close(x.done)
}
}
// wait blocks until x and all the tasks it can reach through edges are done.
func (x *task) wait() {
if x.isTransitivelyDone() {
return // already known to be done. Skip allocations.
}
// Use BFS to wait on u.done to be closed, for all u transitively
// reachable from x via edges.
//
// This work can be repeated by multiple workers doing wait().
//
// Note: Tarjan's SCC algorithm is able to mark SCCs as transitively done
// as soon as the SCC has been visited. This is theoretically faster, but is
// a more complex algorithm. Until we have evidence, we need the more complex
// algorithm, the simpler algorithm BFS is implemented.
//
// In Go 1.23, ssa/TestStdlib reaches <=3 *tasks per wait() in most schedules
// On some schedules, there is a cycle building net/http and internal/trace/testtrace
// due to slices functions.
work := []*task{x}
enqueued := map[*task]unit{x: {}}
for i := 0; i < len(work); i++ {
u := work[i]
if u.isTransitivelyDone() { // already transitively done
work[i] = nil
continue
}
<-u.done // wait for u to be marked done.
for v := range u.edges {
if _, ok := enqueued[v]; !ok {
enqueued[v] = unit{}
work = append(work, v)
}
}
}
// work is transitively closed over dependencies.
// u in work is done (or transitively done and skipped).
// u is transitively done.
for _, u := range work {
if u != nil {
x.transitive.Store(true)
}
}
}

179
vendor/golang.org/x/tools/go/ssa/typeset.go generated vendored Normal file
View File

@@ -0,0 +1,179 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
import (
"go/types"
"golang.org/x/tools/internal/typeparams"
)
// Utilities for dealing with type sets.
const debug = false
// typeset is an iterator over the (type/underlying type) pairs of the
// specific type terms of the type set implied by t.
// If t is a type parameter, the implied type set is the type set of t's constraint.
// In that case, if there are no specific terms, typeset calls yield with (nil, nil).
// If t is not a type parameter, the implied type set consists of just t.
// In any case, typeset is guaranteed to call yield at least once.
func typeset(typ types.Type, yield func(t, u types.Type) bool) {
switch typ := types.Unalias(typ).(type) {
case *types.TypeParam, *types.Interface:
terms := termListOf(typ)
if len(terms) == 0 {
yield(nil, nil)
return
}
for _, term := range terms {
u := types.Unalias(term.Type())
if !term.Tilde() {
u = u.Underlying()
}
if debug {
assert(types.Identical(u, u.Underlying()), "Unalias(x) == under(x) for ~x terms")
}
if !yield(term.Type(), u) {
break
}
}
return
default:
yield(typ, typ.Underlying())
}
}
// termListOf returns the type set of typ as a normalized term set. Returns an empty set on an error.
func termListOf(typ types.Type) []*types.Term {
terms, err := typeparams.NormalTerms(typ)
if err != nil {
return nil
}
return terms
}
// typeSetIsEmpty returns true if a typeset is empty.
func typeSetIsEmpty(typ types.Type) bool {
var empty bool
typeset(typ, func(t, _ types.Type) bool {
empty = t == nil
return false
})
return empty
}
// isBytestring returns true if T has the same terms as interface{[]byte | string}.
// These act like a core type for some operations: slice expressions, append and copy.
//
// See https://go.dev/ref/spec#Core_types for the details on bytestring.
func isBytestring(T types.Type) bool {
U := T.Underlying()
if _, ok := U.(*types.Interface); !ok {
return false
}
hasBytes, hasString := false, false
ok := underIs(U, func(t types.Type) bool {
switch {
case isString(t):
hasString = true
return true
case isByteSlice(t):
hasBytes = true
return true
default:
return false
}
})
return ok && hasBytes && hasString
}
// underIs calls f with the underlying types of the type terms
// of the type set of typ and reports whether all calls to f returned true.
// If there are no specific terms, underIs returns the result of f(nil).
func underIs(typ types.Type, f func(types.Type) bool) bool {
var ok bool
typeset(typ, func(t, u types.Type) bool {
ok = f(u)
return ok
})
return ok
}
// indexType returns the element type and index mode of a IndexExpr over a type.
// It returns an invalid mode if the type is not indexable; this should never occur in a well-typed program.
func indexType(typ types.Type) (types.Type, indexMode) {
switch U := typ.Underlying().(type) {
case *types.Array:
return U.Elem(), ixArrVar
case *types.Pointer:
if arr, ok := U.Elem().Underlying().(*types.Array); ok {
return arr.Elem(), ixVar
}
case *types.Slice:
return U.Elem(), ixVar
case *types.Map:
return U.Elem(), ixMap
case *types.Basic:
return tByte, ixValue // must be a string
case *types.Interface:
var elem types.Type
mode := ixInvalid
typeset(typ, func(t, _ types.Type) bool {
if t == nil {
return false // empty set
}
e, m := indexType(t)
if elem == nil {
elem, mode = e, m
}
if debug && !types.Identical(elem, e) { // if type checked, just a sanity check
mode = ixInvalid
return false
}
// Update the mode to the most constrained address type.
mode = mode.meet(m)
return mode != ixInvalid
})
return elem, mode
}
return nil, ixInvalid
}
// An indexMode specifies the (addressing) mode of an index operand.
//
// Addressing mode of an index operation is based on the set of
// underlying types.
// Hasse diagram of the indexMode meet semi-lattice:
//
// ixVar ixMap
// | |
// ixArrVar |
// | |
// ixValue |
// \ /
// ixInvalid
type indexMode byte
const (
ixInvalid indexMode = iota // index is invalid
ixValue // index is a computed value (not addressable)
ixArrVar // like ixVar, but index operand contains an array
ixVar // index is an addressable variable
ixMap // index is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment)
)
// meet is the address type that is constrained by both x and y.
func (x indexMode) meet(y indexMode) indexMode {
if (x == ixMap || y == ixMap) && x != y {
return ixInvalid
}
// Use int representation and return min.
if x < y {
return y
}
return x
}

413
vendor/golang.org/x/tools/go/ssa/util.go generated vendored Normal file
View File

@@ -0,0 +1,413 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// This file defines a number of miscellaneous utility functions.
import (
"fmt"
"go/ast"
"go/token"
"go/types"
"io"
"os"
"sync"
_ "unsafe" // for go:linkname hack
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/internal/typesinternal"
)
type unit struct{}
//// Sanity checking utilities
// assert panics with the message msg if p is false.
// Avoid combining with expensive string formatting.
func assert(p bool, msg string) {
if !p {
panic(msg)
}
}
//// AST utilities
// isBlankIdent returns true iff e is an Ident with name "_".
// They have no associated types.Object, and thus no type.
func isBlankIdent(e ast.Expr) bool {
id, ok := e.(*ast.Ident)
return ok && id.Name == "_"
}
//// Type utilities. Some of these belong in go/types.
// isNonTypeParamInterface reports whether t is an interface type but not a type parameter.
func isNonTypeParamInterface(t types.Type) bool {
return !typeparams.IsTypeParam(t) && types.IsInterface(t)
}
// isBasic reports whether t is a basic type.
// t is assumed to be an Underlying type (not Named or Alias).
func isBasic(t types.Type) bool {
_, ok := t.(*types.Basic)
return ok
}
// isString reports whether t is exactly a string type.
// t is assumed to be an Underlying type (not Named or Alias).
func isString(t types.Type) bool {
basic, ok := t.(*types.Basic)
return ok && basic.Info()&types.IsString != 0
}
// isByteSlice reports whether t is of the form []~bytes.
// t is assumed to be an Underlying type (not Named or Alias).
func isByteSlice(t types.Type) bool {
if b, ok := t.(*types.Slice); ok {
e, _ := b.Elem().Underlying().(*types.Basic)
return e != nil && e.Kind() == types.Byte
}
return false
}
// isRuneSlice reports whether t is of the form []~runes.
// t is assumed to be an Underlying type (not Named or Alias).
func isRuneSlice(t types.Type) bool {
if b, ok := t.(*types.Slice); ok {
e, _ := b.Elem().Underlying().(*types.Basic)
return e != nil && e.Kind() == types.Rune
}
return false
}
// isBasicConvTypes returns true when the type set of a type
// can be one side of a Convert operation. This is when:
// - All are basic, []byte, or []rune.
// - At least 1 is basic.
// - At most 1 is []byte or []rune.
func isBasicConvTypes(typ types.Type) bool {
basics, cnt := 0, 0
ok := underIs(typ, func(t types.Type) bool {
cnt++
if isBasic(t) {
basics++
return true
}
return isByteSlice(t) || isRuneSlice(t)
})
return ok && basics >= 1 && cnt-basics <= 1
}
// isPointer reports whether t's underlying type is a pointer.
func isPointer(t types.Type) bool {
return is[*types.Pointer](t.Underlying())
}
// isPointerCore reports whether t's core type is a pointer.
//
// (Most pointer manipulation is related to receivers, in which case
// isPointer is appropriate. tecallers can use isPointer(t).
func isPointerCore(t types.Type) bool {
return is[*types.Pointer](typeparams.CoreType(t))
}
func is[T any](x any) bool {
_, ok := x.(T)
return ok
}
// recvType returns the receiver type of method obj.
func recvType(obj *types.Func) types.Type {
return obj.Signature().Recv().Type()
}
// fieldOf returns the index'th field of the (core type of) a struct type;
// otherwise returns nil.
func fieldOf(typ types.Type, index int) *types.Var {
if st, ok := typeparams.CoreType(typ).(*types.Struct); ok {
if 0 <= index && index < st.NumFields() {
return st.Field(index)
}
}
return nil
}
// isUntyped reports whether typ is the type of an untyped constant.
func isUntyped(typ types.Type) bool {
// No Underlying/Unalias: untyped constant types cannot be Named or Alias.
b, ok := typ.(*types.Basic)
return ok && b.Info()&types.IsUntyped != 0
}
// declaredWithin reports whether an object is declared within a function.
//
// obj must not be a method or a field.
func declaredWithin(obj types.Object, fn *types.Func) bool {
if obj.Pos() != token.NoPos {
return fn.Scope().Contains(obj.Pos()) // trust the positions if they exist.
}
if fn.Pkg() != obj.Pkg() {
return false // fast path for different packages
}
// Traverse Parent() scopes for fn.Scope().
for p := obj.Parent(); p != nil; p = p.Parent() {
if p == fn.Scope() {
return true
}
}
return false
}
// logStack prints the formatted "start" message to stderr and
// returns a closure that prints the corresponding "end" message.
// Call using 'defer logStack(...)()' to show builder stack on panic.
// Don't forget trailing parens!
func logStack(format string, args ...any) func() {
msg := fmt.Sprintf(format, args...)
io.WriteString(os.Stderr, msg)
io.WriteString(os.Stderr, "\n")
return func() {
io.WriteString(os.Stderr, msg)
io.WriteString(os.Stderr, " end\n")
}
}
// newVar creates a 'var' for use in a types.Tuple.
func newVar(name string, typ types.Type) *types.Var {
return types.NewParam(token.NoPos, nil, name, typ)
}
// anonVar creates an anonymous 'var' for use in a types.Tuple.
func anonVar(typ types.Type) *types.Var {
return newVar("", typ)
}
var lenResults = types.NewTuple(anonVar(tInt))
// makeLen returns the len builtin specialized to type func(T)int.
func makeLen(T types.Type) *Builtin {
lenParams := types.NewTuple(anonVar(T))
return &Builtin{
name: "len",
sig: types.NewSignatureType(nil, nil, nil, lenParams, lenResults, false),
}
}
// receiverTypeArgs returns the type arguments to a method's receiver.
// Returns an empty list if the receiver does not have type arguments.
func receiverTypeArgs(method *types.Func) []types.Type {
recv := method.Signature().Recv()
_, named := typesinternal.ReceiverNamed(recv)
if named == nil {
return nil // recv is anonymous struct/interface
}
ts := named.TypeArgs()
if ts.Len() == 0 {
return nil
}
targs := make([]types.Type, ts.Len())
for i := 0; i < ts.Len(); i++ {
targs[i] = ts.At(i)
}
return targs
}
// recvAsFirstArg takes a method signature and returns a function
// signature with receiver as the first parameter.
func recvAsFirstArg(sig *types.Signature) *types.Signature {
params := make([]*types.Var, 0, 1+sig.Params().Len())
params = append(params, sig.Recv())
for v := range sig.Params().Variables() {
params = append(params, v)
}
return types.NewSignatureType(nil, nil, nil, types.NewTuple(params...), sig.Results(), sig.Variadic())
}
// instance returns whether an expression is a simple or qualified identifier
// that is a generic instantiation.
func instance(info *types.Info, expr ast.Expr) bool {
// Compare the logic here against go/types.instantiatedIdent,
// which also handles *IndexExpr and *IndexListExpr.
var id *ast.Ident
switch x := expr.(type) {
case *ast.Ident:
id = x
case *ast.SelectorExpr:
id = x.Sel
default:
return false
}
_, ok := info.Instances[id]
return ok
}
// instanceArgs returns the Instance[id].TypeArgs as a slice.
func instanceArgs(info *types.Info, id *ast.Ident) []types.Type {
targList := info.Instances[id].TypeArgs
if targList == nil {
return nil
}
targs := make([]types.Type, targList.Len())
for i, n := 0, targList.Len(); i < n; i++ {
targs[i] = targList.At(i)
}
return targs
}
// Mapping of a type T to a canonical instance C s.t. types.Identical(T, C).
// Thread-safe.
type canonizer struct {
mu sync.Mutex
types typeutil.Map // map from type to a canonical instance
lists typeListMap // map from a list of types to a canonical instance
}
func newCanonizer() *canonizer {
c := &canonizer{}
h := typeutil.MakeHasher()
c.types.SetHasher(h)
c.lists.hasher = h
return c
}
// List returns a canonical representative of a list of types.
// Representative of the empty list is nil.
func (c *canonizer) List(ts []types.Type) *typeList {
if len(ts) == 0 {
return nil
}
unaliasAll := func(ts []types.Type) []types.Type {
// Is there some top level alias?
var found bool
for _, t := range ts {
if _, ok := t.(*types.Alias); ok {
found = true
break
}
}
if !found {
return ts // no top level alias
}
cp := make([]types.Type, len(ts)) // copy with top level aliases removed.
for i, t := range ts {
cp[i] = types.Unalias(t)
}
return cp
}
l := unaliasAll(ts)
c.mu.Lock()
defer c.mu.Unlock()
return c.lists.rep(l)
}
// Type returns a canonical representative of type T.
// Removes top-level aliases.
//
// For performance, reasons the canonical instance is order-dependent,
// and may contain deeply nested aliases.
func (c *canonizer) Type(T types.Type) types.Type {
T = types.Unalias(T) // remove the top level alias.
c.mu.Lock()
defer c.mu.Unlock()
if r := c.types.At(T); r != nil {
return r.(types.Type)
}
c.types.Set(T, T)
return T
}
// A type for representing a canonized list of types.
type typeList []types.Type
func (l *typeList) identical(ts []types.Type) bool {
if l == nil {
return len(ts) == 0
}
n := len(*l)
if len(ts) != n {
return false
}
for i, left := range *l {
right := ts[i]
if !types.Identical(left, right) {
return false
}
}
return true
}
type typeListMap struct {
hasher typeutil.Hasher
buckets map[uint32][]*typeList
}
// rep returns a canonical representative of a slice of types.
func (m *typeListMap) rep(ts []types.Type) *typeList {
if m == nil || len(ts) == 0 {
return nil
}
if m.buckets == nil {
m.buckets = make(map[uint32][]*typeList)
}
h := m.hash(ts)
bucket := m.buckets[h]
for _, l := range bucket {
if l.identical(ts) {
return l
}
}
// not present. create a representative.
cp := make(typeList, len(ts))
copy(cp, ts)
rep := &cp
m.buckets[h] = append(bucket, rep)
return rep
}
func (m *typeListMap) hash(ts []types.Type) uint32 {
if m == nil {
return 0
}
// Some smallish prime far away from typeutil.Hash.
n := len(ts)
h := uint32(13619) + 2*uint32(n)
for i := range n {
h += 3 * m.hasher.Hash(ts[i])
}
return h
}
// instantiateMethod instantiates m with targs and returns a canonical representative for this method.
func (canon *canonizer) instantiateMethod(m *types.Func, targs []types.Type, ctxt *types.Context) *types.Func {
recv := recvType(m)
if p, ok := types.Unalias(recv).(*types.Pointer); ok {
recv = p.Elem()
}
named := types.Unalias(recv).(*types.Named)
inst, err := types.Instantiate(ctxt, named.Origin(), targs, false)
if err != nil {
panic(err)
}
rep := canon.Type(inst)
obj, _, _ := types.LookupFieldOrMethod(rep, true, m.Pkg(), m.Name())
return obj.(*types.Func)
}
// Exposed to ssautil using the linkname hack.
//
//go:linkname isSyntactic golang.org/x/tools/go/ssa.isSyntactic
func isSyntactic(pkg *Package) bool { return pkg.syntax }

346
vendor/golang.org/x/tools/go/ssa/wrappers.go generated vendored Normal file
View File

@@ -0,0 +1,346 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// This file defines synthesis of Functions that delegate to declared
// methods; they come in three kinds:
//
// (1) wrappers: methods that wrap declared methods, performing
// implicit pointer indirections and embedded field selections.
//
// (2) thunks: funcs that wrap declared methods. Like wrappers,
// thunks perform indirections and field selections. The thunk's
// first parameter is used as the receiver for the method call.
//
// (3) bounds: funcs that wrap declared methods. The bound's sole
// free variable, supplied by a closure, is used as the receiver
// for the method call. No indirections or field selections are
// performed since they can be done before the call.
import (
"fmt"
"go/token"
"go/types"
"golang.org/x/tools/internal/typeparams"
)
// -- wrappers -----------------------------------------------------------
// createWrapper returns a synthetic method that delegates to the
// declared method denoted by meth.Obj(), first performing any
// necessary pointer indirections or field selections implied by meth.
//
// The resulting method's receiver type is meth.Recv().
//
// This function is versatile but quite subtle! Consider the
// following axes of variation when making changes:
// - optional receiver indirection
// - optional implicit field selections
// - meth.Obj() may denote a concrete or an interface method
// - the result may be a thunk or a wrapper.
func createWrapper(prog *Program, sel *selection) *Function {
obj := sel.obj.(*types.Func) // the declared function
sig := sel.typ.(*types.Signature) // type of this wrapper
var recv *types.Var // wrapper's receiver or thunk's params[0]
name := obj.Name()
var description string
if sel.kind == types.MethodExpr {
name += "$thunk"
description = "thunk"
recv = sig.Params().At(0)
} else {
description = "wrapper"
recv = sig.Recv()
}
description = fmt.Sprintf("%s for %s", description, sel.obj)
if prog.mode&LogSource != 0 {
defer logStack("create %s to (%s)", description, recv.Type())()
}
/* method wrapper */
return &Function{
name: name,
method: sel,
object: obj,
Signature: sig,
Synthetic: description,
Prog: prog,
pos: obj.Pos(),
// wrappers have no syntax
build: (*builder).buildWrapper,
syntax: nil,
info: nil,
goversion: "",
}
}
// buildWrapper builds fn.Body for a method wrapper.
func (b *builder) buildWrapper(fn *Function) {
var recv *types.Var // wrapper's receiver or thunk's params[0]
var start int // first regular param
if fn.method.kind == types.MethodExpr {
recv = fn.Signature.Params().At(0)
start = 1
} else {
recv = fn.Signature.Recv()
}
fn.startBody()
fn.addSpilledParam(recv)
createParams(fn, start)
indices := fn.method.index
var v Value = fn.Locals[0] // spilled receiver
if isPointer(fn.method.recv) {
v = emitLoad(fn, v)
// For simple indirection wrappers, perform an informative nil-check:
// "value method (T).f called using nil *T pointer"
if len(indices) == 1 && !isPointer(recvType(fn.object)) {
var c Call
c.Call.Value = &Builtin{
name: "ssa:wrapnilchk",
sig: types.NewSignatureType(nil, nil, nil, types.NewTuple(anonVar(fn.method.recv), anonVar(tString), anonVar(tString)), types.NewTuple(anonVar(fn.method.recv)), false),
}
c.Call.Args = []Value{
v,
stringConst(typeparams.MustDeref(fn.method.recv).String()),
stringConst(fn.method.obj.Name()),
}
c.setType(v.Type())
v = fn.emit(&c)
}
}
// Invariant: v is a pointer, either
// value of *A receiver param, or
// address of A spilled receiver.
// We use pointer arithmetic (FieldAddr possibly followed by
// Load) in preference to value extraction (Field possibly
// preceded by Load).
v = emitImplicitSelections(fn, v, indices[:len(indices)-1], token.NoPos)
// Invariant: v is a pointer, either
// value of implicit *C field, or
// address of implicit C field.
var c Call
if r := recvType(fn.object); !types.IsInterface(r) { // concrete method
if !isPointer(r) {
v = emitLoad(fn, v)
}
c.Call.Value = fn.Prog.objectMethod(fn.object, b)
c.Call.Args = append(c.Call.Args, v)
} else {
c.Call.Method = fn.object
c.Call.Value = emitLoad(fn, v) // interface (possibly a typeparam)
}
for _, arg := range fn.Params[1:] {
c.Call.Args = append(c.Call.Args, arg)
}
emitTailCall(fn, &c)
fn.finishBody()
}
// createParams creates parameters for wrapper method fn based on its
// Signature.Params, which do not include the receiver.
// start is the index of the first regular parameter to use.
func createParams(fn *Function, start int) {
tparams := fn.Signature.Params()
for i, n := start, tparams.Len(); i < n; i++ {
fn.addParamVar(tparams.At(i))
}
}
// -- bounds -----------------------------------------------------------
// createBound returns a bound method wrapper (or "bound"), a synthetic
// function that delegates to a concrete or interface method denoted
// by obj. The resulting function has no receiver, but has one free
// variable which will be used as the method's receiver in the
// tail-call.
//
// Use MakeClosure with such a wrapper to construct a bound method
// closure. e.g.:
//
// type T int or: type T interface { meth() }
// func (t T) meth()
// var t T
// f := t.meth
// f() // calls t.meth()
//
// f is a closure of a synthetic wrapper defined as if by:
//
// f := func() { return t.meth() }
//
// Unlike createWrapper, createBound need perform no indirection or field
// selections because that can be done before the closure is
// constructed.
func createBound(prog *Program, obj *types.Func) *Function {
description := fmt.Sprintf("bound method wrapper for %s", obj)
if prog.mode&LogSource != 0 {
defer logStack("%s", description)()
}
/* bound method wrapper */
fn := &Function{
name: obj.Name() + "$bound",
object: obj,
Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver
Synthetic: description,
Prog: prog,
pos: obj.Pos(),
// wrappers have no syntax
build: (*builder).buildBound,
syntax: nil,
info: nil,
goversion: "",
}
fn.FreeVars = []*FreeVar{{name: "recv", typ: recvType(obj), parent: fn}} // (cyclic)
return fn
}
// buildBound builds fn.Body for a bound method closure.
func (b *builder) buildBound(fn *Function) {
fn.startBody()
createParams(fn, 0)
var c Call
recv := fn.FreeVars[0]
if !types.IsInterface(recvType(fn.object)) { // concrete
c.Call.Value = fn.Prog.objectMethod(fn.object, b)
c.Call.Args = []Value{recv}
} else {
c.Call.Method = fn.object
c.Call.Value = recv // interface (possibly a typeparam)
}
for _, arg := range fn.Params {
c.Call.Args = append(c.Call.Args, arg)
}
emitTailCall(fn, &c)
fn.finishBody()
}
// -- thunks -----------------------------------------------------------
// createThunk returns a thunk, a synthetic function that delegates to a
// concrete or interface method denoted by sel.obj. The resulting
// function has no receiver, but has an additional (first) regular
// parameter.
//
// Precondition: sel.kind == types.MethodExpr.
//
// type T int or: type T interface { meth() }
// func (t T) meth()
// f := T.meth
// var t T
// f(t) // calls t.meth()
//
// f is a synthetic wrapper defined as if by:
//
// f := func(t T) { return t.meth() }
func createThunk(prog *Program, sel *selection) *Function {
if sel.kind != types.MethodExpr {
panic(sel)
}
fn := createWrapper(prog, sel)
if fn.Signature.Recv() != nil {
panic(fn) // unexpected receiver
}
return fn
}
func changeRecv(s *types.Signature, recv *types.Var) *types.Signature {
return types.NewSignatureType(recv, nil, nil, s.Params(), s.Results(), s.Variadic())
}
// A local version of *types.Selection.
// Needed for some additional control, such as creating a MethodExpr for an instantiation.
type selection struct {
kind types.SelectionKind
recv types.Type
typ types.Type
obj types.Object
index []int
indirect bool
}
func toSelection(sel *types.Selection) *selection {
return &selection{
kind: sel.Kind(),
recv: sel.Recv(),
typ: sel.Type(),
obj: sel.Obj(),
index: sel.Index(),
indirect: sel.Indirect(),
}
}
// -- instantiations --------------------------------------------------
// buildInstantiationWrapper builds the body of an instantiation
// wrapper fn. The body calls the original generic function,
// bracketed by ChangeType conversions on its arguments and results.
func (b *builder) buildInstantiationWrapper(fn *Function) {
orig := fn.topLevelOrigin
sig := fn.Signature
fn.startBody()
if sig.Recv() != nil {
fn.addParamVar(sig.Recv())
}
createParams(fn, 0)
// Create body. Add a call to origin generic function
// and make type changes between argument and parameters,
// as well as return values.
var c Call
c.Call.Value = orig
if res := orig.Signature.Results(); res.Len() == 1 {
c.typ = res.At(0).Type()
} else {
c.typ = res
}
// parameter of instance becomes an argument to the call
// to the original generic function.
argOffset := 0
for i, arg := range fn.Params {
var typ types.Type
if i == 0 && sig.Recv() != nil {
typ = orig.Signature.Recv().Type()
argOffset = 1
} else {
typ = orig.Signature.Params().At(i - argOffset).Type()
}
c.Call.Args = append(c.Call.Args, emitTypeCoercion(fn, arg, typ))
}
results := fn.emit(&c)
var ret Return
switch res := sig.Results(); res.Len() {
case 0:
// no results, do nothing.
case 1:
ret.Results = []Value{emitTypeCoercion(fn, results, res.At(0).Type())}
default:
for i := 0; i < sig.Results().Len(); i++ {
v := emitExtract(fn, results, i)
ret.Results = append(ret.Results, emitTypeCoercion(fn, v, res.At(i).Type()))
}
}
fn.emit(&ret)
fn.currentBlock = nil
fn.finishBody()
}