mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
The flag values have grown fairly haphazard, with no organization
or even common naming convention. This CL moves all flag values
into the Flag struct (formerly misnamed Debug), except for a few
that live in Ctxt fields instead.
This CL is entirely automated changes.
A followup CL will make a few manual cleanups, leaving this CL
completely automated and easier to regenerate during merge
conflicts.
Cleaning up flags is necessary because the printing routines
look at some of them, and the printing routines need to move
out of package gc to a new package shared by gc and any
other packages that split out of gc.
[git-generate]
cd src/cmd/compile/internal/gc
rf '
mv Debug Flag
mv DebugFlags Flags
mv Flags.e Flags.LowerE
mv Flags.h Flags.LowerH
mv Flags.j Flags.LowerJ
mv Flags.l Flags.LowerL
mv Flags.m Flags.LowerM
mv Flags.r Flags.LowerR
mv Flags.w Flags.LowerW
mv Flags.P Flags.Percent
mv compiling_runtime Flag.CompilingRuntime
mv compiling_std Flag.Std
mv localimport Flag.D
mv asmhdr Flag.AsmHdr
mv buildid Flag.BuildID
mv nBackendWorkers Flag.LowerC
mv pure_go Flag.Complete
mv debugstr Flag.LowerD
mv flagDWARF Flag.Dwarf
mv genDwarfInline Flag.GenDwarfInl
mv flag_installsuffix Flag.InstallSuffix
mv flag_lang Flag.Lang
mv linkobj Flag.LinkObj
mv debuglive Flag.Live
mv flag_msan Flag.MSan
mv nolocalimports Flag.NoLocalImports
mv outfile Flag.LowerO
mv myimportpath Ctxt.Pkgpath
mv writearchive Flag.Pack
mv flag_race Flag.Race
mv spectre Flag.Spectre
mv trace Flag.LowerT
mv pathPrefix Flag.TrimPath
mv Debug_vlog Ctxt.Debugvlog
mv use_writebarrier Flag.WB
mv Main.flag_shared Flag.Shared
mv Main.flag_dynlink Flag.Dynlink
mv Main.goversion Flag.GoVersion
mv Main.symabisPath Flag.SymABIs
mv cpuprofile Flag.CPUProfile
mv memprofile Flag.MemProfile
mv traceprofile Flag.TraceProfile
mv blockprofile Flag.BlockProfile
mv mutexprofile Flag.MutexProfile
mv benchfile Flag.Bench
mv Main.smallFrames Flag.SmallFrames
mv Main.jsonLogOpt Flag.JSON
add Flag:$ \
Cfg struct{}
mv embedCfg Flag.Cfg.Embed
mv idirs Flag.Cfg.ImportDirs
mv importMap Flag.Cfg.ImportMap
mv packageFile Flag.Cfg.PackageFile
mv spectreIndex Flag.Cfg.SpectreIndex
mv addidir addImportDir
mv main.go:/Wasm/-0,/ssaDump/-3 ParseFlags
mv usage Flag Flags ParseFlags \
concurrentFlagOk concurrentBackendAllowed \
addImportDir addImportMap \
readImportCfg readEmbedCfg \
flag.go
# Remove //go:generate line copied from main.go
# along with two self-assignments from the merge.
rm flag.go:/go:generate/-+ \
flag.go:/Ctxt.Pkgpath = Ctxt.Pkgpath/-+ \
flag.go:/Ctxt.Debugvlog = Ctxt.Debugvlog/-+
'
Change-Id: I10431c15fe7d9f48024d53141d4224d957dbf334
Reviewed-on: https://go-review.googlesource.com/c/go/+/271667
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
1737 lines
41 KiB
Go
1737 lines
41 KiB
Go
// Copyright 2009 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 gc
|
|
|
|
import (
|
|
"cmd/compile/internal/types"
|
|
"cmd/internal/src"
|
|
"crypto/md5"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"go/constant"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// largeStack is info about a function whose stack frame is too large (rare).
|
|
type largeStack struct {
|
|
locals int64
|
|
args int64
|
|
callee int64
|
|
pos src.XPos
|
|
}
|
|
|
|
var (
|
|
largeStackFramesMu sync.Mutex // protects largeStackFrames
|
|
largeStackFrames []largeStack
|
|
)
|
|
|
|
// hasUniquePos reports whether n has a unique position that can be
|
|
// used for reporting error messages.
|
|
//
|
|
// It's primarily used to distinguish references to named objects,
|
|
// whose Pos will point back to their declaration position rather than
|
|
// their usage position.
|
|
func hasUniquePos(n *Node) bool {
|
|
switch n.Op {
|
|
case ONAME, OPACK:
|
|
return false
|
|
case OLITERAL, ONIL, OTYPE:
|
|
if n.Sym != nil {
|
|
return false
|
|
}
|
|
}
|
|
|
|
if !n.Pos.IsKnown() {
|
|
if Flag.K != 0 {
|
|
Warn("setlineno: unknown position (line 0)")
|
|
}
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func setlineno(n *Node) src.XPos {
|
|
lno := lineno
|
|
if n != nil && hasUniquePos(n) {
|
|
lineno = n.Pos
|
|
}
|
|
return lno
|
|
}
|
|
|
|
func lookup(name string) *types.Sym {
|
|
return localpkg.Lookup(name)
|
|
}
|
|
|
|
// lookupN looks up the symbol starting with prefix and ending with
|
|
// the decimal n. If prefix is too long, lookupN panics.
|
|
func lookupN(prefix string, n int) *types.Sym {
|
|
var buf [20]byte // plenty long enough for all current users
|
|
copy(buf[:], prefix)
|
|
b := strconv.AppendInt(buf[:len(prefix)], int64(n), 10)
|
|
return localpkg.LookupBytes(b)
|
|
}
|
|
|
|
// autolabel generates a new Name node for use with
|
|
// an automatically generated label.
|
|
// prefix is a short mnemonic (e.g. ".s" for switch)
|
|
// to help with debugging.
|
|
// It should begin with "." to avoid conflicts with
|
|
// user labels.
|
|
func autolabel(prefix string) *types.Sym {
|
|
if prefix[0] != '.' {
|
|
Fatalf("autolabel prefix must start with '.', have %q", prefix)
|
|
}
|
|
fn := Curfn
|
|
if Curfn == nil {
|
|
Fatalf("autolabel outside function")
|
|
}
|
|
n := fn.Func.Label
|
|
fn.Func.Label++
|
|
return lookupN(prefix, int(n))
|
|
}
|
|
|
|
// find all the exported symbols in package opkg
|
|
// and make them available in the current package
|
|
func importdot(opkg *types.Pkg, pack *Node) {
|
|
n := 0
|
|
for _, s := range opkg.Syms {
|
|
if s.Def == nil {
|
|
continue
|
|
}
|
|
if !types.IsExported(s.Name) || strings.ContainsRune(s.Name, 0xb7) { // 0xb7 = center dot
|
|
continue
|
|
}
|
|
s1 := lookup(s.Name)
|
|
if s1.Def != nil {
|
|
pkgerror := fmt.Sprintf("during import %q", opkg.Path)
|
|
redeclare(lineno, s1, pkgerror)
|
|
continue
|
|
}
|
|
|
|
s1.Def = s.Def
|
|
s1.Block = s.Block
|
|
if asNode(s1.Def).Name == nil {
|
|
Dump("s1def", asNode(s1.Def))
|
|
Fatalf("missing Name")
|
|
}
|
|
asNode(s1.Def).Name.Pack = pack
|
|
s1.Origpkg = opkg
|
|
n++
|
|
}
|
|
|
|
if n == 0 {
|
|
// can't possibly be used - there were no symbols
|
|
yyerrorl(pack.Pos, "imported and not used: %q", opkg.Path)
|
|
}
|
|
}
|
|
|
|
func nod(op Op, nleft, nright *Node) *Node {
|
|
return nodl(lineno, op, nleft, nright)
|
|
}
|
|
|
|
func nodl(pos src.XPos, op Op, nleft, nright *Node) *Node {
|
|
var n *Node
|
|
switch op {
|
|
case ODCLFUNC:
|
|
var x struct {
|
|
n Node
|
|
f Func
|
|
}
|
|
n = &x.n
|
|
n.Func = &x.f
|
|
n.Func.Decl = n
|
|
case ONAME:
|
|
Fatalf("use newname instead")
|
|
case OLABEL, OPACK:
|
|
var x struct {
|
|
n Node
|
|
m Name
|
|
}
|
|
n = &x.n
|
|
n.Name = &x.m
|
|
default:
|
|
n = new(Node)
|
|
}
|
|
n.Op = op
|
|
n.Left = nleft
|
|
n.Right = nright
|
|
n.Pos = pos
|
|
n.Xoffset = BADWIDTH
|
|
n.Orig = n
|
|
return n
|
|
}
|
|
|
|
// newname returns a new ONAME Node associated with symbol s.
|
|
func newname(s *types.Sym) *Node {
|
|
n := newnamel(lineno, s)
|
|
n.Name.Curfn = Curfn
|
|
return n
|
|
}
|
|
|
|
// newnamel returns a new ONAME Node associated with symbol s at position pos.
|
|
// The caller is responsible for setting n.Name.Curfn.
|
|
func newnamel(pos src.XPos, s *types.Sym) *Node {
|
|
if s == nil {
|
|
Fatalf("newnamel nil")
|
|
}
|
|
|
|
var x struct {
|
|
n Node
|
|
m Name
|
|
p Param
|
|
}
|
|
n := &x.n
|
|
n.Name = &x.m
|
|
n.Name.Param = &x.p
|
|
|
|
n.Op = ONAME
|
|
n.Pos = pos
|
|
n.Orig = n
|
|
|
|
n.Sym = s
|
|
return n
|
|
}
|
|
|
|
// nodSym makes a Node with Op op and with the Left field set to left
|
|
// and the Sym field set to sym. This is for ODOT and friends.
|
|
func nodSym(op Op, left *Node, sym *types.Sym) *Node {
|
|
return nodlSym(lineno, op, left, sym)
|
|
}
|
|
|
|
// nodlSym makes a Node with position Pos, with Op op, and with the Left field set to left
|
|
// and the Sym field set to sym. This is for ODOT and friends.
|
|
func nodlSym(pos src.XPos, op Op, left *Node, sym *types.Sym) *Node {
|
|
n := nodl(pos, op, left, nil)
|
|
n.Sym = sym
|
|
return n
|
|
}
|
|
|
|
// rawcopy returns a shallow copy of n.
|
|
// Note: copy or sepcopy (rather than rawcopy) is usually the
|
|
// correct choice (see comment with Node.copy, below).
|
|
func (n *Node) rawcopy() *Node {
|
|
copy := *n
|
|
return ©
|
|
}
|
|
|
|
// sepcopy returns a separate shallow copy of n, with the copy's
|
|
// Orig pointing to itself.
|
|
func (n *Node) sepcopy() *Node {
|
|
copy := *n
|
|
copy.Orig = ©
|
|
return ©
|
|
}
|
|
|
|
// copy returns shallow copy of n and adjusts the copy's Orig if
|
|
// necessary: In general, if n.Orig points to itself, the copy's
|
|
// Orig should point to itself as well. Otherwise, if n is modified,
|
|
// the copy's Orig node appears modified, too, and then doesn't
|
|
// represent the original node anymore.
|
|
// (This caused the wrong complit Op to be used when printing error
|
|
// messages; see issues #26855, #27765).
|
|
func (n *Node) copy() *Node {
|
|
copy := *n
|
|
if n.Orig == n {
|
|
copy.Orig = ©
|
|
}
|
|
return ©
|
|
}
|
|
|
|
// methcmp sorts methods by symbol.
|
|
type methcmp []*types.Field
|
|
|
|
func (x methcmp) Len() int { return len(x) }
|
|
func (x methcmp) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
func (x methcmp) Less(i, j int) bool { return x[i].Sym.Less(x[j].Sym) }
|
|
|
|
func nodintconst(v int64) *Node {
|
|
return nodlit(constant.MakeInt64(v))
|
|
}
|
|
|
|
func nodnil() *Node {
|
|
n := nod(ONIL, nil, nil)
|
|
n.Type = types.Types[TNIL]
|
|
return n
|
|
}
|
|
|
|
func nodbool(b bool) *Node {
|
|
return nodlit(constant.MakeBool(b))
|
|
}
|
|
|
|
func nodstr(s string) *Node {
|
|
return nodlit(constant.MakeString(s))
|
|
}
|
|
|
|
// treecopy recursively copies n, with the exception of
|
|
// ONAME, OLITERAL, OTYPE, and ONONAME leaves.
|
|
// If pos.IsKnown(), it sets the source position of newly
|
|
// allocated nodes to pos.
|
|
func treecopy(n *Node, pos src.XPos) *Node {
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
|
|
switch n.Op {
|
|
default:
|
|
m := n.sepcopy()
|
|
m.Left = treecopy(n.Left, pos)
|
|
m.Right = treecopy(n.Right, pos)
|
|
m.List.Set(listtreecopy(n.List.Slice(), pos))
|
|
if pos.IsKnown() {
|
|
m.Pos = pos
|
|
}
|
|
if m.Name != nil && n.Op != ODCLFIELD {
|
|
Dump("treecopy", n)
|
|
Fatalf("treecopy Name")
|
|
}
|
|
return m
|
|
|
|
case OPACK:
|
|
// OPACK nodes are never valid in const value declarations,
|
|
// but allow them like any other declared symbol to avoid
|
|
// crashing (golang.org/issue/11361).
|
|
fallthrough
|
|
|
|
case ONAME, ONONAME, OLITERAL, ONIL, OTYPE:
|
|
return n
|
|
|
|
}
|
|
}
|
|
|
|
// isNil reports whether n represents the universal untyped zero value "nil".
|
|
func (n *Node) isNil() bool {
|
|
// Check n.Orig because constant propagation may produce typed nil constants,
|
|
// which don't exist in the Go spec.
|
|
return n.Orig.Op == ONIL
|
|
}
|
|
|
|
func isptrto(t *types.Type, et types.EType) bool {
|
|
if t == nil {
|
|
return false
|
|
}
|
|
if !t.IsPtr() {
|
|
return false
|
|
}
|
|
t = t.Elem()
|
|
if t == nil {
|
|
return false
|
|
}
|
|
if t.Etype != et {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (n *Node) isBlank() bool {
|
|
if n == nil {
|
|
return false
|
|
}
|
|
return n.Sym.IsBlank()
|
|
}
|
|
|
|
// methtype returns the underlying type, if any,
|
|
// that owns methods with receiver parameter t.
|
|
// The result is either a named type or an anonymous struct.
|
|
func methtype(t *types.Type) *types.Type {
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
|
|
// Strip away pointer if it's there.
|
|
if t.IsPtr() {
|
|
if t.Sym != nil {
|
|
return nil
|
|
}
|
|
t = t.Elem()
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Must be a named type or anonymous struct.
|
|
if t.Sym == nil && !t.IsStruct() {
|
|
return nil
|
|
}
|
|
|
|
// Check types.
|
|
if issimple[t.Etype] {
|
|
return t
|
|
}
|
|
switch t.Etype {
|
|
case TARRAY, TCHAN, TFUNC, TMAP, TSLICE, TSTRING, TSTRUCT:
|
|
return t
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Is type src assignment compatible to type dst?
|
|
// If so, return op code to use in conversion.
|
|
// If not, return OXXX. In this case, the string return parameter may
|
|
// hold a reason why. In all other cases, it'll be the empty string.
|
|
func assignop(src, dst *types.Type) (Op, string) {
|
|
if src == dst {
|
|
return OCONVNOP, ""
|
|
}
|
|
if src == nil || dst == nil || src.Etype == TFORW || dst.Etype == TFORW || src.Orig == nil || dst.Orig == nil {
|
|
return OXXX, ""
|
|
}
|
|
|
|
// 1. src type is identical to dst.
|
|
if types.Identical(src, dst) {
|
|
return OCONVNOP, ""
|
|
}
|
|
|
|
// 2. src and dst have identical underlying types
|
|
// and either src or dst is not a named type or
|
|
// both are empty interface types.
|
|
// For assignable but different non-empty interface types,
|
|
// we want to recompute the itab. Recomputing the itab ensures
|
|
// that itabs are unique (thus an interface with a compile-time
|
|
// type I has an itab with interface type I).
|
|
if types.Identical(src.Orig, dst.Orig) {
|
|
if src.IsEmptyInterface() {
|
|
// Conversion between two empty interfaces
|
|
// requires no code.
|
|
return OCONVNOP, ""
|
|
}
|
|
if (src.Sym == nil || dst.Sym == nil) && !src.IsInterface() {
|
|
// Conversion between two types, at least one unnamed,
|
|
// needs no conversion. The exception is nonempty interfaces
|
|
// which need to have their itab updated.
|
|
return OCONVNOP, ""
|
|
}
|
|
}
|
|
|
|
// 3. dst is an interface type and src implements dst.
|
|
if dst.IsInterface() && src.Etype != TNIL {
|
|
var missing, have *types.Field
|
|
var ptr int
|
|
if implements(src, dst, &missing, &have, &ptr) {
|
|
return OCONVIFACE, ""
|
|
}
|
|
|
|
// we'll have complained about this method anyway, suppress spurious messages.
|
|
if have != nil && have.Sym == missing.Sym && (have.Type.Broke() || missing.Type.Broke()) {
|
|
return OCONVIFACE, ""
|
|
}
|
|
|
|
var why string
|
|
if isptrto(src, TINTER) {
|
|
why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", src)
|
|
} else if have != nil && have.Sym == missing.Sym && have.Nointerface() {
|
|
why = fmt.Sprintf(":\n\t%v does not implement %v (%v method is marked 'nointerface')", src, dst, missing.Sym)
|
|
} else if have != nil && have.Sym == missing.Sym {
|
|
why = fmt.Sprintf(":\n\t%v does not implement %v (wrong type for %v method)\n"+
|
|
"\t\thave %v%0S\n\t\twant %v%0S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
|
|
} else if ptr != 0 {
|
|
why = fmt.Sprintf(":\n\t%v does not implement %v (%v method has pointer receiver)", src, dst, missing.Sym)
|
|
} else if have != nil {
|
|
why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)\n"+
|
|
"\t\thave %v%0S\n\t\twant %v%0S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
|
|
} else {
|
|
why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)", src, dst, missing.Sym)
|
|
}
|
|
|
|
return OXXX, why
|
|
}
|
|
|
|
if isptrto(dst, TINTER) {
|
|
why := fmt.Sprintf(":\n\t%v is pointer to interface, not interface", dst)
|
|
return OXXX, why
|
|
}
|
|
|
|
if src.IsInterface() && dst.Etype != TBLANK {
|
|
var missing, have *types.Field
|
|
var ptr int
|
|
var why string
|
|
if implements(dst, src, &missing, &have, &ptr) {
|
|
why = ": need type assertion"
|
|
}
|
|
return OXXX, why
|
|
}
|
|
|
|
// 4. src is a bidirectional channel value, dst is a channel type,
|
|
// src and dst have identical element types, and
|
|
// either src or dst is not a named type.
|
|
if src.IsChan() && src.ChanDir() == types.Cboth && dst.IsChan() {
|
|
if types.Identical(src.Elem(), dst.Elem()) && (src.Sym == nil || dst.Sym == nil) {
|
|
return OCONVNOP, ""
|
|
}
|
|
}
|
|
|
|
// 5. src is the predeclared identifier nil and dst is a nillable type.
|
|
if src.Etype == TNIL {
|
|
switch dst.Etype {
|
|
case TPTR,
|
|
TFUNC,
|
|
TMAP,
|
|
TCHAN,
|
|
TINTER,
|
|
TSLICE:
|
|
return OCONVNOP, ""
|
|
}
|
|
}
|
|
|
|
// 6. rule about untyped constants - already converted by defaultlit.
|
|
|
|
// 7. Any typed value can be assigned to the blank identifier.
|
|
if dst.Etype == TBLANK {
|
|
return OCONVNOP, ""
|
|
}
|
|
|
|
return OXXX, ""
|
|
}
|
|
|
|
// Can we convert a value of type src to a value of type dst?
|
|
// If so, return op code to use in conversion (maybe OCONVNOP).
|
|
// If not, return OXXX. In this case, the string return parameter may
|
|
// hold a reason why. In all other cases, it'll be the empty string.
|
|
// srcConstant indicates whether the value of type src is a constant.
|
|
func convertop(srcConstant bool, src, dst *types.Type) (Op, string) {
|
|
if src == dst {
|
|
return OCONVNOP, ""
|
|
}
|
|
if src == nil || dst == nil {
|
|
return OXXX, ""
|
|
}
|
|
|
|
// Conversions from regular to go:notinheap are not allowed
|
|
// (unless it's unsafe.Pointer). These are runtime-specific
|
|
// rules.
|
|
// (a) Disallow (*T) to (*U) where T is go:notinheap but U isn't.
|
|
if src.IsPtr() && dst.IsPtr() && dst.Elem().NotInHeap() && !src.Elem().NotInHeap() {
|
|
why := fmt.Sprintf(":\n\t%v is incomplete (or unallocatable), but %v is not", dst.Elem(), src.Elem())
|
|
return OXXX, why
|
|
}
|
|
// (b) Disallow string to []T where T is go:notinheap.
|
|
if src.IsString() && dst.IsSlice() && dst.Elem().NotInHeap() && (dst.Elem().Etype == types.Bytetype.Etype || dst.Elem().Etype == types.Runetype.Etype) {
|
|
why := fmt.Sprintf(":\n\t%v is incomplete (or unallocatable)", dst.Elem())
|
|
return OXXX, why
|
|
}
|
|
|
|
// 1. src can be assigned to dst.
|
|
op, why := assignop(src, dst)
|
|
if op != OXXX {
|
|
return op, why
|
|
}
|
|
|
|
// The rules for interfaces are no different in conversions
|
|
// than assignments. If interfaces are involved, stop now
|
|
// with the good message from assignop.
|
|
// Otherwise clear the error.
|
|
if src.IsInterface() || dst.IsInterface() {
|
|
return OXXX, why
|
|
}
|
|
|
|
// 2. Ignoring struct tags, src and dst have identical underlying types.
|
|
if types.IdenticalIgnoreTags(src.Orig, dst.Orig) {
|
|
return OCONVNOP, ""
|
|
}
|
|
|
|
// 3. src and dst are unnamed pointer types and, ignoring struct tags,
|
|
// their base types have identical underlying types.
|
|
if src.IsPtr() && dst.IsPtr() && src.Sym == nil && dst.Sym == nil {
|
|
if types.IdenticalIgnoreTags(src.Elem().Orig, dst.Elem().Orig) {
|
|
return OCONVNOP, ""
|
|
}
|
|
}
|
|
|
|
// 4. src and dst are both integer or floating point types.
|
|
if (src.IsInteger() || src.IsFloat()) && (dst.IsInteger() || dst.IsFloat()) {
|
|
if simtype[src.Etype] == simtype[dst.Etype] {
|
|
return OCONVNOP, ""
|
|
}
|
|
return OCONV, ""
|
|
}
|
|
|
|
// 5. src and dst are both complex types.
|
|
if src.IsComplex() && dst.IsComplex() {
|
|
if simtype[src.Etype] == simtype[dst.Etype] {
|
|
return OCONVNOP, ""
|
|
}
|
|
return OCONV, ""
|
|
}
|
|
|
|
// Special case for constant conversions: any numeric
|
|
// conversion is potentially okay. We'll validate further
|
|
// within evconst. See #38117.
|
|
if srcConstant && (src.IsInteger() || src.IsFloat() || src.IsComplex()) && (dst.IsInteger() || dst.IsFloat() || dst.IsComplex()) {
|
|
return OCONV, ""
|
|
}
|
|
|
|
// 6. src is an integer or has type []byte or []rune
|
|
// and dst is a string type.
|
|
if src.IsInteger() && dst.IsString() {
|
|
return ORUNESTR, ""
|
|
}
|
|
|
|
if src.IsSlice() && dst.IsString() {
|
|
if src.Elem().Etype == types.Bytetype.Etype {
|
|
return OBYTES2STR, ""
|
|
}
|
|
if src.Elem().Etype == types.Runetype.Etype {
|
|
return ORUNES2STR, ""
|
|
}
|
|
}
|
|
|
|
// 7. src is a string and dst is []byte or []rune.
|
|
// String to slice.
|
|
if src.IsString() && dst.IsSlice() {
|
|
if dst.Elem().Etype == types.Bytetype.Etype {
|
|
return OSTR2BYTES, ""
|
|
}
|
|
if dst.Elem().Etype == types.Runetype.Etype {
|
|
return OSTR2RUNES, ""
|
|
}
|
|
}
|
|
|
|
// 8. src is a pointer or uintptr and dst is unsafe.Pointer.
|
|
if (src.IsPtr() || src.IsUintptr()) && dst.IsUnsafePtr() {
|
|
return OCONVNOP, ""
|
|
}
|
|
|
|
// 9. src is unsafe.Pointer and dst is a pointer or uintptr.
|
|
if src.IsUnsafePtr() && (dst.IsPtr() || dst.IsUintptr()) {
|
|
return OCONVNOP, ""
|
|
}
|
|
|
|
// src is map and dst is a pointer to corresponding hmap.
|
|
// This rule is needed for the implementation detail that
|
|
// go gc maps are implemented as a pointer to a hmap struct.
|
|
if src.Etype == TMAP && dst.IsPtr() &&
|
|
src.MapType().Hmap == dst.Elem() {
|
|
return OCONVNOP, ""
|
|
}
|
|
|
|
return OXXX, ""
|
|
}
|
|
|
|
func assignconv(n *Node, t *types.Type, context string) *Node {
|
|
return assignconvfn(n, t, func() string { return context })
|
|
}
|
|
|
|
// Convert node n for assignment to type t.
|
|
func assignconvfn(n *Node, t *types.Type, context func() string) *Node {
|
|
if n == nil || n.Type == nil || n.Type.Broke() {
|
|
return n
|
|
}
|
|
|
|
if t.Etype == TBLANK && n.Type.Etype == TNIL {
|
|
yyerror("use of untyped nil")
|
|
}
|
|
|
|
n = convlit1(n, t, false, context)
|
|
if n.Type == nil {
|
|
return n
|
|
}
|
|
if t.Etype == TBLANK {
|
|
return n
|
|
}
|
|
|
|
// Convert ideal bool from comparison to plain bool
|
|
// if the next step is non-bool (like interface{}).
|
|
if n.Type == types.UntypedBool && !t.IsBoolean() {
|
|
if n.Op == ONAME || n.Op == OLITERAL {
|
|
r := nod(OCONVNOP, n, nil)
|
|
r.Type = types.Types[TBOOL]
|
|
r.SetTypecheck(1)
|
|
r.SetImplicit(true)
|
|
n = r
|
|
}
|
|
}
|
|
|
|
if types.Identical(n.Type, t) {
|
|
return n
|
|
}
|
|
|
|
op, why := assignop(n.Type, t)
|
|
if op == OXXX {
|
|
yyerror("cannot use %L as type %v in %s%s", n, t, context(), why)
|
|
op = OCONV
|
|
}
|
|
|
|
r := nod(op, n, nil)
|
|
r.Type = t
|
|
r.SetTypecheck(1)
|
|
r.SetImplicit(true)
|
|
r.Orig = n.Orig
|
|
return r
|
|
}
|
|
|
|
// IsMethod reports whether n is a method.
|
|
// n must be a function or a method.
|
|
func (n *Node) IsMethod() bool {
|
|
return n.Type.Recv() != nil
|
|
}
|
|
|
|
// SliceBounds returns n's slice bounds: low, high, and max in expr[low:high:max].
|
|
// n must be a slice expression. max is nil if n is a simple slice expression.
|
|
func (n *Node) SliceBounds() (low, high, max *Node) {
|
|
if n.List.Len() == 0 {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
switch n.Op {
|
|
case OSLICE, OSLICEARR, OSLICESTR:
|
|
s := n.List.Slice()
|
|
return s[0], s[1], nil
|
|
case OSLICE3, OSLICE3ARR:
|
|
s := n.List.Slice()
|
|
return s[0], s[1], s[2]
|
|
}
|
|
Fatalf("SliceBounds op %v: %v", n.Op, n)
|
|
return nil, nil, nil
|
|
}
|
|
|
|
// SetSliceBounds sets n's slice bounds, where n is a slice expression.
|
|
// n must be a slice expression. If max is non-nil, n must be a full slice expression.
|
|
func (n *Node) SetSliceBounds(low, high, max *Node) {
|
|
switch n.Op {
|
|
case OSLICE, OSLICEARR, OSLICESTR:
|
|
if max != nil {
|
|
Fatalf("SetSliceBounds %v given three bounds", n.Op)
|
|
}
|
|
s := n.List.Slice()
|
|
if s == nil {
|
|
if low == nil && high == nil {
|
|
return
|
|
}
|
|
n.List.Set2(low, high)
|
|
return
|
|
}
|
|
s[0] = low
|
|
s[1] = high
|
|
return
|
|
case OSLICE3, OSLICE3ARR:
|
|
s := n.List.Slice()
|
|
if s == nil {
|
|
if low == nil && high == nil && max == nil {
|
|
return
|
|
}
|
|
n.List.Set3(low, high, max)
|
|
return
|
|
}
|
|
s[0] = low
|
|
s[1] = high
|
|
s[2] = max
|
|
return
|
|
}
|
|
Fatalf("SetSliceBounds op %v: %v", n.Op, n)
|
|
}
|
|
|
|
// IsSlice3 reports whether o is a slice3 op (OSLICE3, OSLICE3ARR).
|
|
// o must be a slicing op.
|
|
func (o Op) IsSlice3() bool {
|
|
switch o {
|
|
case OSLICE, OSLICEARR, OSLICESTR:
|
|
return false
|
|
case OSLICE3, OSLICE3ARR:
|
|
return true
|
|
}
|
|
Fatalf("IsSlice3 op %v", o)
|
|
return false
|
|
}
|
|
|
|
// backingArrayPtrLen extracts the pointer and length from a slice or string.
|
|
// This constructs two nodes referring to n, so n must be a cheapexpr.
|
|
func (n *Node) backingArrayPtrLen() (ptr, len *Node) {
|
|
var init Nodes
|
|
c := cheapexpr(n, &init)
|
|
if c != n || init.Len() != 0 {
|
|
Fatalf("backingArrayPtrLen not cheap: %v", n)
|
|
}
|
|
ptr = nod(OSPTR, n, nil)
|
|
if n.Type.IsString() {
|
|
ptr.Type = types.Types[TUINT8].PtrTo()
|
|
} else {
|
|
ptr.Type = n.Type.Elem().PtrTo()
|
|
}
|
|
len = nod(OLEN, n, nil)
|
|
len.Type = types.Types[TINT]
|
|
return ptr, len
|
|
}
|
|
|
|
// labeledControl returns the control flow Node (for, switch, select)
|
|
// associated with the label n, if any.
|
|
func (n *Node) labeledControl() *Node {
|
|
if n.Op != OLABEL {
|
|
Fatalf("labeledControl %v", n.Op)
|
|
}
|
|
ctl := n.Name.Defn
|
|
if ctl == nil {
|
|
return nil
|
|
}
|
|
switch ctl.Op {
|
|
case OFOR, OFORUNTIL, OSWITCH, OSELECT:
|
|
return ctl
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func syslook(name string) *Node {
|
|
s := Runtimepkg.Lookup(name)
|
|
if s == nil || s.Def == nil {
|
|
Fatalf("syslook: can't find runtime.%s", name)
|
|
}
|
|
return asNode(s.Def)
|
|
}
|
|
|
|
// typehash computes a hash value for type t to use in type switch statements.
|
|
func typehash(t *types.Type) uint32 {
|
|
p := t.LongString()
|
|
|
|
// Using MD5 is overkill, but reduces accidental collisions.
|
|
h := md5.Sum([]byte(p))
|
|
return binary.LittleEndian.Uint32(h[:4])
|
|
}
|
|
|
|
// updateHasCall checks whether expression n contains any function
|
|
// calls and sets the n.HasCall flag if so.
|
|
func updateHasCall(n *Node) {
|
|
if n == nil {
|
|
return
|
|
}
|
|
n.SetHasCall(calcHasCall(n))
|
|
}
|
|
|
|
func calcHasCall(n *Node) bool {
|
|
if n.Ninit.Len() != 0 {
|
|
// TODO(mdempsky): This seems overly conservative.
|
|
return true
|
|
}
|
|
|
|
switch n.Op {
|
|
case OLITERAL, ONIL, ONAME, OTYPE:
|
|
if n.HasCall() {
|
|
Fatalf("OLITERAL/ONAME/OTYPE should never have calls: %+v", n)
|
|
}
|
|
return false
|
|
case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER:
|
|
return true
|
|
case OANDAND, OOROR:
|
|
// hard with instrumented code
|
|
if instrumenting {
|
|
return true
|
|
}
|
|
case OINDEX, OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR, OSLICESTR,
|
|
ODEREF, ODOTPTR, ODOTTYPE, ODIV, OMOD:
|
|
// These ops might panic, make sure they are done
|
|
// before we start marshaling args for a call. See issue 16760.
|
|
return true
|
|
|
|
// When using soft-float, these ops might be rewritten to function calls
|
|
// so we ensure they are evaluated first.
|
|
case OADD, OSUB, ONEG, OMUL:
|
|
if thearch.SoftFloat && (isFloat[n.Type.Etype] || isComplex[n.Type.Etype]) {
|
|
return true
|
|
}
|
|
case OLT, OEQ, ONE, OLE, OGE, OGT:
|
|
if thearch.SoftFloat && (isFloat[n.Left.Type.Etype] || isComplex[n.Left.Type.Etype]) {
|
|
return true
|
|
}
|
|
case OCONV:
|
|
if thearch.SoftFloat && ((isFloat[n.Type.Etype] || isComplex[n.Type.Etype]) || (isFloat[n.Left.Type.Etype] || isComplex[n.Left.Type.Etype])) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
if n.Left != nil && n.Left.HasCall() {
|
|
return true
|
|
}
|
|
if n.Right != nil && n.Right.HasCall() {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func badtype(op Op, tl, tr *types.Type) {
|
|
var s string
|
|
if tl != nil {
|
|
s += fmt.Sprintf("\n\t%v", tl)
|
|
}
|
|
if tr != nil {
|
|
s += fmt.Sprintf("\n\t%v", tr)
|
|
}
|
|
|
|
// common mistake: *struct and *interface.
|
|
if tl != nil && tr != nil && tl.IsPtr() && tr.IsPtr() {
|
|
if tl.Elem().IsStruct() && tr.Elem().IsInterface() {
|
|
s += "\n\t(*struct vs *interface)"
|
|
} else if tl.Elem().IsInterface() && tr.Elem().IsStruct() {
|
|
s += "\n\t(*interface vs *struct)"
|
|
}
|
|
}
|
|
|
|
yyerror("illegal types for operand: %v%s", op, s)
|
|
}
|
|
|
|
// brcom returns !(op).
|
|
// For example, brcom(==) is !=.
|
|
func brcom(op Op) Op {
|
|
switch op {
|
|
case OEQ:
|
|
return ONE
|
|
case ONE:
|
|
return OEQ
|
|
case OLT:
|
|
return OGE
|
|
case OGT:
|
|
return OLE
|
|
case OLE:
|
|
return OGT
|
|
case OGE:
|
|
return OLT
|
|
}
|
|
Fatalf("brcom: no com for %v\n", op)
|
|
return op
|
|
}
|
|
|
|
// brrev returns reverse(op).
|
|
// For example, Brrev(<) is >.
|
|
func brrev(op Op) Op {
|
|
switch op {
|
|
case OEQ:
|
|
return OEQ
|
|
case ONE:
|
|
return ONE
|
|
case OLT:
|
|
return OGT
|
|
case OGT:
|
|
return OLT
|
|
case OLE:
|
|
return OGE
|
|
case OGE:
|
|
return OLE
|
|
}
|
|
Fatalf("brrev: no rev for %v\n", op)
|
|
return op
|
|
}
|
|
|
|
// return side effect-free n, appending side effects to init.
|
|
// result is assignable if n is.
|
|
func safeexpr(n *Node, init *Nodes) *Node {
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
|
|
if n.Ninit.Len() != 0 {
|
|
walkstmtlist(n.Ninit.Slice())
|
|
init.AppendNodes(&n.Ninit)
|
|
}
|
|
|
|
switch n.Op {
|
|
case ONAME, OLITERAL, ONIL:
|
|
return n
|
|
|
|
case ODOT, OLEN, OCAP:
|
|
l := safeexpr(n.Left, init)
|
|
if l == n.Left {
|
|
return n
|
|
}
|
|
r := n.copy()
|
|
r.Left = l
|
|
r = typecheck(r, ctxExpr)
|
|
r = walkexpr(r, init)
|
|
return r
|
|
|
|
case ODOTPTR, ODEREF:
|
|
l := safeexpr(n.Left, init)
|
|
if l == n.Left {
|
|
return n
|
|
}
|
|
a := n.copy()
|
|
a.Left = l
|
|
a = walkexpr(a, init)
|
|
return a
|
|
|
|
case OINDEX, OINDEXMAP:
|
|
l := safeexpr(n.Left, init)
|
|
r := safeexpr(n.Right, init)
|
|
if l == n.Left && r == n.Right {
|
|
return n
|
|
}
|
|
a := n.copy()
|
|
a.Left = l
|
|
a.Right = r
|
|
a = walkexpr(a, init)
|
|
return a
|
|
|
|
case OSTRUCTLIT, OARRAYLIT, OSLICELIT:
|
|
if isStaticCompositeLiteral(n) {
|
|
return n
|
|
}
|
|
}
|
|
|
|
// make a copy; must not be used as an lvalue
|
|
if islvalue(n) {
|
|
Fatalf("missing lvalue case in safeexpr: %v", n)
|
|
}
|
|
return cheapexpr(n, init)
|
|
}
|
|
|
|
func copyexpr(n *Node, t *types.Type, init *Nodes) *Node {
|
|
l := temp(t)
|
|
a := nod(OAS, l, n)
|
|
a = typecheck(a, ctxStmt)
|
|
a = walkexpr(a, init)
|
|
init.Append(a)
|
|
return l
|
|
}
|
|
|
|
// return side-effect free and cheap n, appending side effects to init.
|
|
// result may not be assignable.
|
|
func cheapexpr(n *Node, init *Nodes) *Node {
|
|
switch n.Op {
|
|
case ONAME, OLITERAL, ONIL:
|
|
return n
|
|
}
|
|
|
|
return copyexpr(n, n.Type, init)
|
|
}
|
|
|
|
// Code to resolve elided DOTs in embedded types.
|
|
|
|
// A Dlist stores a pointer to a TFIELD Type embedded within
|
|
// a TSTRUCT or TINTER Type.
|
|
type Dlist struct {
|
|
field *types.Field
|
|
}
|
|
|
|
// dotlist is used by adddot1 to record the path of embedded fields
|
|
// used to access a target field or method.
|
|
// Must be non-nil so that dotpath returns a non-nil slice even if d is zero.
|
|
var dotlist = make([]Dlist, 10)
|
|
|
|
// lookdot0 returns the number of fields or methods named s associated
|
|
// with Type t. If exactly one exists, it will be returned in *save
|
|
// (if save is not nil).
|
|
func lookdot0(s *types.Sym, t *types.Type, save **types.Field, ignorecase bool) int {
|
|
u := t
|
|
if u.IsPtr() {
|
|
u = u.Elem()
|
|
}
|
|
|
|
c := 0
|
|
if u.IsStruct() || u.IsInterface() {
|
|
for _, f := range u.Fields().Slice() {
|
|
if f.Sym == s || (ignorecase && f.IsMethod() && strings.EqualFold(f.Sym.Name, s.Name)) {
|
|
if save != nil {
|
|
*save = f
|
|
}
|
|
c++
|
|
}
|
|
}
|
|
}
|
|
|
|
u = t
|
|
if t.Sym != nil && t.IsPtr() && !t.Elem().IsPtr() {
|
|
// If t is a defined pointer type, then x.m is shorthand for (*x).m.
|
|
u = t.Elem()
|
|
}
|
|
u = methtype(u)
|
|
if u != nil {
|
|
for _, f := range u.Methods().Slice() {
|
|
if f.Embedded == 0 && (f.Sym == s || (ignorecase && strings.EqualFold(f.Sym.Name, s.Name))) {
|
|
if save != nil {
|
|
*save = f
|
|
}
|
|
c++
|
|
}
|
|
}
|
|
}
|
|
|
|
return c
|
|
}
|
|
|
|
// adddot1 returns the number of fields or methods named s at depth d in Type t.
|
|
// If exactly one exists, it will be returned in *save (if save is not nil),
|
|
// and dotlist will contain the path of embedded fields traversed to find it,
|
|
// in reverse order. If none exist, more will indicate whether t contains any
|
|
// embedded fields at depth d, so callers can decide whether to retry at
|
|
// a greater depth.
|
|
func adddot1(s *types.Sym, t *types.Type, d int, save **types.Field, ignorecase bool) (c int, more bool) {
|
|
if t.Recur() {
|
|
return
|
|
}
|
|
t.SetRecur(true)
|
|
defer t.SetRecur(false)
|
|
|
|
var u *types.Type
|
|
d--
|
|
if d < 0 {
|
|
// We've reached our target depth. If t has any fields/methods
|
|
// named s, then we're done. Otherwise, we still need to check
|
|
// below for embedded fields.
|
|
c = lookdot0(s, t, save, ignorecase)
|
|
if c != 0 {
|
|
return c, false
|
|
}
|
|
}
|
|
|
|
u = t
|
|
if u.IsPtr() {
|
|
u = u.Elem()
|
|
}
|
|
if !u.IsStruct() && !u.IsInterface() {
|
|
return c, false
|
|
}
|
|
|
|
for _, f := range u.Fields().Slice() {
|
|
if f.Embedded == 0 || f.Sym == nil {
|
|
continue
|
|
}
|
|
if d < 0 {
|
|
// Found an embedded field at target depth.
|
|
return c, true
|
|
}
|
|
a, more1 := adddot1(s, f.Type, d, save, ignorecase)
|
|
if a != 0 && c == 0 {
|
|
dotlist[d].field = f
|
|
}
|
|
c += a
|
|
if more1 {
|
|
more = true
|
|
}
|
|
}
|
|
|
|
return c, more
|
|
}
|
|
|
|
// dotpath computes the unique shortest explicit selector path to fully qualify
|
|
// a selection expression x.f, where x is of type t and f is the symbol s.
|
|
// If no such path exists, dotpath returns nil.
|
|
// If there are multiple shortest paths to the same depth, ambig is true.
|
|
func dotpath(s *types.Sym, t *types.Type, save **types.Field, ignorecase bool) (path []Dlist, ambig bool) {
|
|
// The embedding of types within structs imposes a tree structure onto
|
|
// types: structs parent the types they embed, and types parent their
|
|
// fields or methods. Our goal here is to find the shortest path to
|
|
// a field or method named s in the subtree rooted at t. To accomplish
|
|
// that, we iteratively perform depth-first searches of increasing depth
|
|
// until we either find the named field/method or exhaust the tree.
|
|
for d := 0; ; d++ {
|
|
if d > len(dotlist) {
|
|
dotlist = append(dotlist, Dlist{})
|
|
}
|
|
if c, more := adddot1(s, t, d, save, ignorecase); c == 1 {
|
|
return dotlist[:d], false
|
|
} else if c > 1 {
|
|
return nil, true
|
|
} else if !more {
|
|
return nil, false
|
|
}
|
|
}
|
|
}
|
|
|
|
// in T.field
|
|
// find missing fields that
|
|
// will give shortest unique addressing.
|
|
// modify the tree with missing type names.
|
|
func adddot(n *Node) *Node {
|
|
n.Left = typecheck(n.Left, ctxType|ctxExpr)
|
|
if n.Left.Diag() {
|
|
n.SetDiag(true)
|
|
}
|
|
t := n.Left.Type
|
|
if t == nil {
|
|
return n
|
|
}
|
|
|
|
if n.Left.Op == OTYPE {
|
|
return n
|
|
}
|
|
|
|
s := n.Sym
|
|
if s == nil {
|
|
return n
|
|
}
|
|
|
|
switch path, ambig := dotpath(s, t, nil, false); {
|
|
case path != nil:
|
|
// rebuild elided dots
|
|
for c := len(path) - 1; c >= 0; c-- {
|
|
n.Left = nodSym(ODOT, n.Left, path[c].field.Sym)
|
|
n.Left.SetImplicit(true)
|
|
}
|
|
case ambig:
|
|
yyerror("ambiguous selector %v", n)
|
|
n.Left = nil
|
|
}
|
|
|
|
return n
|
|
}
|
|
|
|
// Code to help generate trampoline functions for methods on embedded
|
|
// types. These are approx the same as the corresponding adddot
|
|
// routines except that they expect to be called with unique tasks and
|
|
// they return the actual methods.
|
|
|
|
type Symlink struct {
|
|
field *types.Field
|
|
}
|
|
|
|
var slist []Symlink
|
|
|
|
func expand0(t *types.Type) {
|
|
u := t
|
|
if u.IsPtr() {
|
|
u = u.Elem()
|
|
}
|
|
|
|
if u.IsInterface() {
|
|
for _, f := range u.Fields().Slice() {
|
|
if f.Sym.Uniq() {
|
|
continue
|
|
}
|
|
f.Sym.SetUniq(true)
|
|
slist = append(slist, Symlink{field: f})
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
u = methtype(t)
|
|
if u != nil {
|
|
for _, f := range u.Methods().Slice() {
|
|
if f.Sym.Uniq() {
|
|
continue
|
|
}
|
|
f.Sym.SetUniq(true)
|
|
slist = append(slist, Symlink{field: f})
|
|
}
|
|
}
|
|
}
|
|
|
|
func expand1(t *types.Type, top bool) {
|
|
if t.Recur() {
|
|
return
|
|
}
|
|
t.SetRecur(true)
|
|
|
|
if !top {
|
|
expand0(t)
|
|
}
|
|
|
|
u := t
|
|
if u.IsPtr() {
|
|
u = u.Elem()
|
|
}
|
|
|
|
if u.IsStruct() || u.IsInterface() {
|
|
for _, f := range u.Fields().Slice() {
|
|
if f.Embedded == 0 {
|
|
continue
|
|
}
|
|
if f.Sym == nil {
|
|
continue
|
|
}
|
|
expand1(f.Type, false)
|
|
}
|
|
}
|
|
|
|
t.SetRecur(false)
|
|
}
|
|
|
|
func expandmeth(t *types.Type) {
|
|
if t == nil || t.AllMethods().Len() != 0 {
|
|
return
|
|
}
|
|
|
|
// mark top-level method symbols
|
|
// so that expand1 doesn't consider them.
|
|
for _, f := range t.Methods().Slice() {
|
|
f.Sym.SetUniq(true)
|
|
}
|
|
|
|
// generate all reachable methods
|
|
slist = slist[:0]
|
|
expand1(t, true)
|
|
|
|
// check each method to be uniquely reachable
|
|
var ms []*types.Field
|
|
for i, sl := range slist {
|
|
slist[i].field = nil
|
|
sl.field.Sym.SetUniq(false)
|
|
|
|
var f *types.Field
|
|
path, _ := dotpath(sl.field.Sym, t, &f, false)
|
|
if path == nil {
|
|
continue
|
|
}
|
|
|
|
// dotpath may have dug out arbitrary fields, we only want methods.
|
|
if !f.IsMethod() {
|
|
continue
|
|
}
|
|
|
|
// add it to the base type method list
|
|
f = f.Copy()
|
|
f.Embedded = 1 // needs a trampoline
|
|
for _, d := range path {
|
|
if d.field.Type.IsPtr() {
|
|
f.Embedded = 2
|
|
break
|
|
}
|
|
}
|
|
ms = append(ms, f)
|
|
}
|
|
|
|
for _, f := range t.Methods().Slice() {
|
|
f.Sym.SetUniq(false)
|
|
}
|
|
|
|
ms = append(ms, t.Methods().Slice()...)
|
|
sort.Sort(methcmp(ms))
|
|
t.AllMethods().Set(ms)
|
|
}
|
|
|
|
// Given funarg struct list, return list of ODCLFIELD Node fn args.
|
|
func structargs(tl *types.Type, mustname bool) []*Node {
|
|
var args []*Node
|
|
gen := 0
|
|
for _, t := range tl.Fields().Slice() {
|
|
s := t.Sym
|
|
if mustname && (s == nil || s.Name == "_") {
|
|
// invent a name so that we can refer to it in the trampoline
|
|
s = lookupN(".anon", gen)
|
|
gen++
|
|
}
|
|
a := symfield(s, t.Type)
|
|
a.Pos = t.Pos
|
|
a.SetIsDDD(t.IsDDD())
|
|
args = append(args, a)
|
|
}
|
|
|
|
return args
|
|
}
|
|
|
|
// Generate a wrapper function to convert from
|
|
// a receiver of type T to a receiver of type U.
|
|
// That is,
|
|
//
|
|
// func (t T) M() {
|
|
// ...
|
|
// }
|
|
//
|
|
// already exists; this function generates
|
|
//
|
|
// func (u U) M() {
|
|
// u.M()
|
|
// }
|
|
//
|
|
// where the types T and U are such that u.M() is valid
|
|
// and calls the T.M method.
|
|
// The resulting function is for use in method tables.
|
|
//
|
|
// rcvr - U
|
|
// method - M func (t T)(), a TFIELD type struct
|
|
// newnam - the eventual mangled name of this function
|
|
func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) {
|
|
if false && Flag.LowerR != 0 {
|
|
fmt.Printf("genwrapper rcvrtype=%v method=%v newnam=%v\n", rcvr, method, newnam)
|
|
}
|
|
|
|
// Only generate (*T).M wrappers for T.M in T's own package.
|
|
if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type &&
|
|
rcvr.Elem().Sym != nil && rcvr.Elem().Sym.Pkg != localpkg {
|
|
return
|
|
}
|
|
|
|
// Only generate I.M wrappers for I in I's own package
|
|
// but keep doing it for error.Error (was issue #29304).
|
|
if rcvr.IsInterface() && rcvr.Sym != nil && rcvr.Sym.Pkg != localpkg && rcvr != types.Errortype {
|
|
return
|
|
}
|
|
|
|
lineno = autogeneratedPos
|
|
dclcontext = PEXTERN
|
|
|
|
tfn := nod(OTFUNC, nil, nil)
|
|
tfn.Left = namedfield(".this", rcvr)
|
|
tfn.List.Set(structargs(method.Type.Params(), true))
|
|
tfn.Rlist.Set(structargs(method.Type.Results(), false))
|
|
|
|
fn := dclfunc(newnam, tfn)
|
|
fn.Func.SetDupok(true)
|
|
|
|
nthis := asNode(tfn.Type.Recv().Nname)
|
|
|
|
methodrcvr := method.Type.Recv().Type
|
|
|
|
// generate nil pointer check for better error
|
|
if rcvr.IsPtr() && rcvr.Elem() == methodrcvr {
|
|
// generating wrapper from *T to T.
|
|
n := nod(OIF, nil, nil)
|
|
n.Left = nod(OEQ, nthis, nodnil())
|
|
call := nod(OCALL, syslook("panicwrap"), nil)
|
|
n.Nbody.Set1(call)
|
|
fn.Nbody.Append(n)
|
|
}
|
|
|
|
dot := adddot(nodSym(OXDOT, nthis, method.Sym))
|
|
|
|
// generate call
|
|
// It's not possible to use a tail call when dynamic linking on ppc64le. The
|
|
// bad scenario is when a local call is made to the wrapper: the wrapper will
|
|
// call the implementation, which might be in a different module and so set
|
|
// the TOC to the appropriate value for that module. But if it returns
|
|
// directly to the wrapper's caller, nothing will reset it to the correct
|
|
// value for that function.
|
|
if !instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !isifacemethod(method.Type) && !(thearch.LinkArch.Name == "ppc64le" && Ctxt.Flag_dynlink) {
|
|
// generate tail call: adjust pointer receiver and jump to embedded method.
|
|
dot = dot.Left // skip final .M
|
|
// TODO(mdempsky): Remove dependency on dotlist.
|
|
if !dotlist[0].field.Type.IsPtr() {
|
|
dot = nod(OADDR, dot, nil)
|
|
}
|
|
as := nod(OAS, nthis, convnop(dot, rcvr))
|
|
fn.Nbody.Append(as)
|
|
fn.Nbody.Append(nodSym(ORETJMP, nil, methodSym(methodrcvr, method.Sym)))
|
|
} else {
|
|
fn.Func.SetWrapper(true) // ignore frame for panic+recover matching
|
|
call := nod(OCALL, dot, nil)
|
|
call.List.Set(paramNnames(tfn.Type))
|
|
call.SetIsDDD(tfn.Type.IsVariadic())
|
|
if method.Type.NumResults() > 0 {
|
|
n := nod(ORETURN, nil, nil)
|
|
n.List.Set1(call)
|
|
call = n
|
|
}
|
|
fn.Nbody.Append(call)
|
|
}
|
|
|
|
if false && Flag.LowerR != 0 {
|
|
dumplist("genwrapper body", fn.Nbody)
|
|
}
|
|
|
|
funcbody()
|
|
if debug_dclstack != 0 {
|
|
testdclstack()
|
|
}
|
|
|
|
fn = typecheck(fn, ctxStmt)
|
|
|
|
Curfn = fn
|
|
typecheckslice(fn.Nbody.Slice(), ctxStmt)
|
|
|
|
// Inline calls within (*T).M wrappers. This is safe because we only
|
|
// generate those wrappers within the same compilation unit as (T).M.
|
|
// TODO(mdempsky): Investigate why we can't enable this more generally.
|
|
if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type && rcvr.Elem().Sym != nil {
|
|
inlcalls(fn)
|
|
}
|
|
escapeFuncs([]*Node{fn}, false)
|
|
|
|
Curfn = nil
|
|
xtop = append(xtop, fn)
|
|
}
|
|
|
|
func paramNnames(ft *types.Type) []*Node {
|
|
args := make([]*Node, ft.NumParams())
|
|
for i, f := range ft.Params().FieldSlice() {
|
|
args[i] = asNode(f.Nname)
|
|
}
|
|
return args
|
|
}
|
|
|
|
func hashmem(t *types.Type) *Node {
|
|
sym := Runtimepkg.Lookup("memhash")
|
|
|
|
n := newname(sym)
|
|
setNodeNameFunc(n)
|
|
n.Type = functype(nil, []*Node{
|
|
anonfield(types.NewPtr(t)),
|
|
anonfield(types.Types[TUINTPTR]),
|
|
anonfield(types.Types[TUINTPTR]),
|
|
}, []*Node{
|
|
anonfield(types.Types[TUINTPTR]),
|
|
})
|
|
return n
|
|
}
|
|
|
|
func ifacelookdot(s *types.Sym, t *types.Type, ignorecase bool) (m *types.Field, followptr bool) {
|
|
if t == nil {
|
|
return nil, false
|
|
}
|
|
|
|
path, ambig := dotpath(s, t, &m, ignorecase)
|
|
if path == nil {
|
|
if ambig {
|
|
yyerror("%v.%v is ambiguous", t, s)
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
for _, d := range path {
|
|
if d.field.Type.IsPtr() {
|
|
followptr = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !m.IsMethod() {
|
|
yyerror("%v.%v is a field, not a method", t, s)
|
|
return nil, followptr
|
|
}
|
|
|
|
return m, followptr
|
|
}
|
|
|
|
func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool {
|
|
t0 := t
|
|
if t == nil {
|
|
return false
|
|
}
|
|
|
|
if t.IsInterface() {
|
|
i := 0
|
|
tms := t.Fields().Slice()
|
|
for _, im := range iface.Fields().Slice() {
|
|
for i < len(tms) && tms[i].Sym != im.Sym {
|
|
i++
|
|
}
|
|
if i == len(tms) {
|
|
*m = im
|
|
*samename = nil
|
|
*ptr = 0
|
|
return false
|
|
}
|
|
tm := tms[i]
|
|
if !types.Identical(tm.Type, im.Type) {
|
|
*m = im
|
|
*samename = tm
|
|
*ptr = 0
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
t = methtype(t)
|
|
var tms []*types.Field
|
|
if t != nil {
|
|
expandmeth(t)
|
|
tms = t.AllMethods().Slice()
|
|
}
|
|
i := 0
|
|
for _, im := range iface.Fields().Slice() {
|
|
if im.Broke() {
|
|
continue
|
|
}
|
|
for i < len(tms) && tms[i].Sym != im.Sym {
|
|
i++
|
|
}
|
|
if i == len(tms) {
|
|
*m = im
|
|
*samename, _ = ifacelookdot(im.Sym, t, true)
|
|
*ptr = 0
|
|
return false
|
|
}
|
|
tm := tms[i]
|
|
if tm.Nointerface() || !types.Identical(tm.Type, im.Type) {
|
|
*m = im
|
|
*samename = tm
|
|
*ptr = 0
|
|
return false
|
|
}
|
|
followptr := tm.Embedded == 2
|
|
|
|
// if pointer receiver in method,
|
|
// the method does not exist for value types.
|
|
rcvr := tm.Type.Recv().Type
|
|
if rcvr.IsPtr() && !t0.IsPtr() && !followptr && !isifacemethod(tm.Type) {
|
|
if false && Flag.LowerR != 0 {
|
|
yyerror("interface pointer mismatch")
|
|
}
|
|
|
|
*m = im
|
|
*samename = nil
|
|
*ptr = 1
|
|
return false
|
|
}
|
|
}
|
|
|
|
// We're going to emit an OCONVIFACE.
|
|
// Call itabname so that (t, iface)
|
|
// gets added to itabs early, which allows
|
|
// us to de-virtualize calls through this
|
|
// type/interface pair later. See peekitabs in reflect.go
|
|
if isdirectiface(t0) && !iface.IsEmptyInterface() {
|
|
itabname(t0, iface)
|
|
}
|
|
return true
|
|
}
|
|
|
|
func listtreecopy(l []*Node, pos src.XPos) []*Node {
|
|
var out []*Node
|
|
for _, n := range l {
|
|
out = append(out, treecopy(n, pos))
|
|
}
|
|
return out
|
|
}
|
|
|
|
func liststmt(l []*Node) *Node {
|
|
n := nod(OBLOCK, nil, nil)
|
|
n.List.Set(l)
|
|
if len(l) != 0 {
|
|
n.Pos = l[0].Pos
|
|
}
|
|
return n
|
|
}
|
|
|
|
func ngotype(n *Node) *types.Sym {
|
|
if n.Type != nil {
|
|
return typenamesym(n.Type)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// The result of addinit MUST be assigned back to n, e.g.
|
|
// n.Left = addinit(n.Left, init)
|
|
func addinit(n *Node, init []*Node) *Node {
|
|
if len(init) == 0 {
|
|
return n
|
|
}
|
|
if n.mayBeShared() {
|
|
// Introduce OCONVNOP to hold init list.
|
|
n = nod(OCONVNOP, n, nil)
|
|
n.Type = n.Left.Type
|
|
n.SetTypecheck(1)
|
|
}
|
|
|
|
n.Ninit.Prepend(init...)
|
|
n.SetHasCall(true)
|
|
return n
|
|
}
|
|
|
|
// The linker uses the magic symbol prefixes "go." and "type."
|
|
// Avoid potential confusion between import paths and symbols
|
|
// by rejecting these reserved imports for now. Also, people
|
|
// "can do weird things in GOPATH and we'd prefer they didn't
|
|
// do _that_ weird thing" (per rsc). See also #4257.
|
|
var reservedimports = []string{
|
|
"go",
|
|
"type",
|
|
}
|
|
|
|
func isbadimport(path string, allowSpace bool) bool {
|
|
if strings.Contains(path, "\x00") {
|
|
yyerror("import path contains NUL")
|
|
return true
|
|
}
|
|
|
|
for _, ri := range reservedimports {
|
|
if path == ri {
|
|
yyerror("import path %q is reserved and cannot be used", path)
|
|
return true
|
|
}
|
|
}
|
|
|
|
for _, r := range path {
|
|
if r == utf8.RuneError {
|
|
yyerror("import path contains invalid UTF-8 sequence: %q", path)
|
|
return true
|
|
}
|
|
|
|
if r < 0x20 || r == 0x7f {
|
|
yyerror("import path contains control character: %q", path)
|
|
return true
|
|
}
|
|
|
|
if r == '\\' {
|
|
yyerror("import path contains backslash; use slash: %q", path)
|
|
return true
|
|
}
|
|
|
|
if !allowSpace && unicode.IsSpace(r) {
|
|
yyerror("import path contains space character: %q", path)
|
|
return true
|
|
}
|
|
|
|
if strings.ContainsRune("!\"#$%&'()*,:;<=>?[]^`{|}", r) {
|
|
yyerror("import path contains invalid character '%c': %q", r, path)
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Can this type be stored directly in an interface word?
|
|
// Yes, if the representation is a single pointer.
|
|
func isdirectiface(t *types.Type) bool {
|
|
if t.Broke() {
|
|
return false
|
|
}
|
|
|
|
switch t.Etype {
|
|
case TPTR:
|
|
// Pointers to notinheap types must be stored indirectly. See issue 42076.
|
|
return !t.Elem().NotInHeap()
|
|
case TCHAN,
|
|
TMAP,
|
|
TFUNC,
|
|
TUNSAFEPTR:
|
|
return true
|
|
|
|
case TARRAY:
|
|
// Array of 1 direct iface type can be direct.
|
|
return t.NumElem() == 1 && isdirectiface(t.Elem())
|
|
|
|
case TSTRUCT:
|
|
// Struct with 1 field of direct iface type can be direct.
|
|
return t.NumFields() == 1 && isdirectiface(t.Field(0).Type)
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// itabType loads the _type field from a runtime.itab struct.
|
|
func itabType(itab *Node) *Node {
|
|
typ := nodSym(ODOTPTR, itab, nil)
|
|
typ.Type = types.NewPtr(types.Types[TUINT8])
|
|
typ.SetTypecheck(1)
|
|
typ.Xoffset = int64(Widthptr) // offset of _type in runtime.itab
|
|
typ.SetBounded(true) // guaranteed not to fault
|
|
return typ
|
|
}
|
|
|
|
// ifaceData loads the data field from an interface.
|
|
// The concrete type must be known to have type t.
|
|
// It follows the pointer if !isdirectiface(t).
|
|
func ifaceData(pos src.XPos, n *Node, t *types.Type) *Node {
|
|
if t.IsInterface() {
|
|
Fatalf("ifaceData interface: %v", t)
|
|
}
|
|
ptr := nodlSym(pos, OIDATA, n, nil)
|
|
if isdirectiface(t) {
|
|
ptr.Type = t
|
|
ptr.SetTypecheck(1)
|
|
return ptr
|
|
}
|
|
ptr.Type = types.NewPtr(t)
|
|
ptr.SetTypecheck(1)
|
|
ind := nodl(pos, ODEREF, ptr, nil)
|
|
ind.Type = t
|
|
ind.SetTypecheck(1)
|
|
ind.SetBounded(true)
|
|
return ind
|
|
}
|
|
|
|
// typePos returns the position associated with t.
|
|
// This is where t was declared or where it appeared as a type expression.
|
|
func typePos(t *types.Type) src.XPos {
|
|
n := asNode(t.Nod)
|
|
if n == nil || !n.Pos.IsKnown() {
|
|
Fatalf("bad type: %v", t)
|
|
}
|
|
return n.Pos
|
|
}
|