diff options
Diffstat (limited to 'src/cmd/compile/internal/gc/subr.go')
-rw-r--r-- | src/cmd/compile/internal/gc/subr.go | 1918 |
1 files changed, 1918 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go new file mode 100644 index 0000000..defefd7 --- /dev/null +++ b/src/cmd/compile/internal/gc/subr.go @@ -0,0 +1,1918 @@ +// 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/objabi" + "cmd/internal/src" + "crypto/md5" + "encoding/binary" + "fmt" + "os" + "runtime/debug" + "sort" + "strconv" + "strings" + "sync" + "unicode" + "unicode/utf8" +) + +type Error struct { + pos src.XPos + msg string +} + +var errors []Error + +// 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 +) + +func errorexit() { + flusherrors() + if outfile != "" { + os.Remove(outfile) + } + os.Exit(2) +} + +func adderrorname(n *Node) { + if n.Op != ODOT { + return + } + old := fmt.Sprintf("%v: undefined: %v\n", n.Line(), n.Left) + if len(errors) > 0 && errors[len(errors)-1].pos.Line() == n.Pos.Line() && errors[len(errors)-1].msg == old { + errors[len(errors)-1].msg = fmt.Sprintf("%v: undefined: %v in %v\n", n.Line(), n.Left, n) + } +} + +func adderr(pos src.XPos, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + // Only add the position if know the position. + // See issue golang.org/issue/11361. + if pos.IsKnown() { + msg = fmt.Sprintf("%v: %s", linestr(pos), msg) + } + errors = append(errors, Error{ + pos: pos, + msg: msg + "\n", + }) +} + +// byPos sorts errors by source position. +type byPos []Error + +func (x byPos) Len() int { return len(x) } +func (x byPos) Less(i, j int) bool { return x[i].pos.Before(x[j].pos) } +func (x byPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +// flusherrors sorts errors seen so far by line number, prints them to stdout, +// and empties the errors array. +func flusherrors() { + Ctxt.Bso.Flush() + if len(errors) == 0 { + return + } + sort.Stable(byPos(errors)) + for i, err := range errors { + if i == 0 || err.msg != errors[i-1].msg { + fmt.Printf("%s", err.msg) + } + } + errors = errors[:0] +} + +func hcrash() { + if Debug.h != 0 { + flusherrors() + if outfile != "" { + os.Remove(outfile) + } + var x *int + *x = 0 + } +} + +func linestr(pos src.XPos) string { + return Ctxt.OutermostPos(pos).Format(Debug.C == 0, Debug.L == 1) +} + +// lasterror keeps track of the most recently issued error. +// It is used to avoid multiple error messages on the same +// line. +var lasterror struct { + syntax src.XPos // source position of last syntax error + other src.XPos // source position of last non-syntax error + msg string // error message of last non-syntax error +} + +// sameline reports whether two positions a, b are on the same line. +func sameline(a, b src.XPos) bool { + p := Ctxt.PosTable.Pos(a) + q := Ctxt.PosTable.Pos(b) + return p.Base() == q.Base() && p.Line() == q.Line() +} + +func yyerrorl(pos src.XPos, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + + if strings.HasPrefix(msg, "syntax error") { + nsyntaxerrors++ + // only one syntax error per line, no matter what error + if sameline(lasterror.syntax, pos) { + return + } + lasterror.syntax = pos + } else { + // only one of multiple equal non-syntax errors per line + // (flusherrors shows only one of them, so we filter them + // here as best as we can (they may not appear in order) + // so that we don't count them here and exit early, and + // then have nothing to show for.) + if sameline(lasterror.other, pos) && lasterror.msg == msg { + return + } + lasterror.other = pos + lasterror.msg = msg + } + + adderr(pos, "%s", msg) + + hcrash() + nerrors++ + if nsavederrors+nerrors >= 10 && Debug.e == 0 { + flusherrors() + fmt.Printf("%v: too many errors\n", linestr(pos)) + errorexit() + } +} + +func yyerrorv(lang string, format string, args ...interface{}) { + what := fmt.Sprintf(format, args...) + yyerrorl(lineno, "%s requires %s or later (-lang was set to %s; check go.mod)", what, lang, flag_lang) +} + +func yyerror(format string, args ...interface{}) { + yyerrorl(lineno, format, args...) +} + +func Warn(fmt_ string, args ...interface{}) { + Warnl(lineno, fmt_, args...) +} + +func Warnl(line src.XPos, fmt_ string, args ...interface{}) { + adderr(line, fmt_, args...) + if Debug.m != 0 { + flusherrors() + } +} + +func Fatalf(fmt_ string, args ...interface{}) { + flusherrors() + + if Debug_panic != 0 || nsavederrors+nerrors == 0 { + fmt.Printf("%v: internal compiler error: ", linestr(lineno)) + fmt.Printf(fmt_, args...) + fmt.Printf("\n") + + // If this is a released compiler version, ask for a bug report. + if strings.HasPrefix(objabi.Version, "go") { + fmt.Printf("\n") + fmt.Printf("Please file a bug report including a short program that triggers the error.\n") + fmt.Printf("https://golang.org/issue/new\n") + } else { + // Not a release; dump a stack trace, too. + fmt.Println() + os.Stdout.Write(debug.Stack()) + fmt.Println() + } + } + + hcrash() + errorexit() +} + +// 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, OTYPE: + if n.Sym != nil { + return false + } + } + + if !n.Pos.IsKnown() { + if Debug.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 OCLOSURE, ODCLFUNC: + var x struct { + n Node + f Func + } + n = &x.n + n.Func = &x.f + 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 { + u := new(Mpint) + u.SetInt64(v) + return nodlit(Val{u}) +} + +func nodnil() *Node { + return nodlit(Val{new(NilVal)}) +} + +func nodbool(b bool) *Node { + return nodlit(Val{b}) +} + +func nodstr(s string) *Node { + return nodlit(Val{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, 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 Isconst(n.Orig, CTNIL) +} + +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, 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: + 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: + 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 && Debug.r != 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 && Debug.r != 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 && Debug.r != 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 (l Nodes) asblock() *Node { + n := nod(OBLOCK, nil, nil) + n.List = l + if l.Len() != 0 { + n.Pos = l.First().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 +} |