// 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 ( "bytes" "cmd/internal/obj" "crypto/md5" "encoding/binary" "fmt" "os" "runtime/debug" "sort" "strconv" "strings" "unicode" "unicode/utf8" ) type Error struct { lineno int32 msg string } var errors []Error 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].lineno == n.Lineno && 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(line int32, format string, args ...interface{}) { errors = append(errors, Error{ lineno: line, msg: fmt.Sprintf("%v: %s\n", linestr(line), fmt.Sprintf(format, args...)), }) } // byLineno sorts errors by lineno. type byLineno []Error func (x byLineno) Len() int { return len(x) } func (x byLineno) Less(i, j int) bool { return x[i].lineno < x[j].lineno } func (x byLineno) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func Flusherrors() { bstdout.Flush() if len(errors) == 0 { return } sort.Stable(byLineno(errors)) for i := 0; i < len(errors); i++ { if i == 0 || errors[i].msg != errors[i-1].msg { fmt.Printf("%s", errors[i].msg) } } errors = errors[:0] } func hcrash() { if Debug['h'] != 0 { Flusherrors() if outfile != "" { os.Remove(outfile) } var x *int *x = 0 } } func linestr(line int32) string { return Ctxt.Line(int(line)) } func yyerrorl(line int32, format string, args ...interface{}) { adderr(line, format, args...) hcrash() nerrors++ if nsavederrors+nerrors >= 10 && Debug['e'] == 0 { Flusherrors() fmt.Printf("%v: too many errors\n", linestr(line)) errorexit() } } var yyerror_lastsyntax int32 func Yyerror(format string, args ...interface{}) { msg := fmt.Sprintf(format, args...) if strings.HasPrefix(msg, "syntax error") { nsyntaxerrors++ // only one syntax error per line if yyerror_lastsyntax == lineno { return } yyerror_lastsyntax = lineno yyerrorl(lineno, "%s", msg) return } adderr(lineno, "%s", msg) hcrash() nerrors++ if nsavederrors+nerrors >= 10 && Debug['e'] == 0 { Flusherrors() fmt.Printf("%v: too many errors\n", linestr(lineno)) errorexit() } } func Warn(fmt_ string, args ...interface{}) { adderr(lineno, fmt_, args...) hcrash() } func Warnl(line int32, fmt_ string, args ...interface{}) { adderr(line, fmt_, args...) if Debug['m'] != 0 { Flusherrors() } } func Fatalf(fmt_ string, args ...interface{}) { Flusherrors() 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(obj.Getgoversion(), "release") { 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() } func linehistpragma(file string) { if Debug['i'] != 0 { fmt.Printf("pragma %s at line %v\n", file, linestr(lexlineno)) } Ctxt.AddImport(file) } func linehistpush(file string) { if Debug['i'] != 0 { fmt.Printf("import %s at line %v\n", file, linestr(lexlineno)) } Ctxt.LineHist.Push(int(lexlineno), file) } func linehistpop() { if Debug['i'] != 0 { fmt.Printf("end of import at line %v\n", linestr(lexlineno)) } Ctxt.LineHist.Pop(int(lexlineno)) } func linehistupdate(file string, off int) { if Debug['i'] != 0 { fmt.Printf("line %s at line %v\n", file, linestr(lexlineno)) } Ctxt.LineHist.Update(int(lexlineno), file, off) } func setlineno(n *Node) int32 { lno := lineno if n != nil { switch n.Op { case ONAME, OTYPE, OPACK: break case OLITERAL: if n.Sym != nil { break } fallthrough default: lineno = n.Lineno if lineno == 0 { if Debug['K'] != 0 { Warn("setlineno: line 0") } lineno = lno } } } return lno } func Lookup(name string) *Sym { return localpkg.Lookup(name) } func Lookupf(format string, a ...interface{}) *Sym { return Lookup(fmt.Sprintf(format, a...)) } func LookupBytes(name []byte) *Sym { return localpkg.LookupBytes(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) *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 LookupBytes(b) } var initSyms []*Sym var nopkg = &Pkg{ Syms: make(map[string]*Sym), } func (pkg *Pkg) Lookup(name string) *Sym { if pkg == nil { pkg = nopkg } if s := pkg.Syms[name]; s != nil { return s } s := &Sym{ Name: name, Pkg: pkg, } if name == "init" { initSyms = append(initSyms, s) } pkg.Syms[name] = s return s } func (pkg *Pkg) LookupBytes(name []byte) *Sym { if pkg == nil { pkg = nopkg } if s := pkg.Syms[string(name)]; s != nil { return s } str := internString(name) return pkg.Lookup(str) } func Pkglookup(name string, pkg *Pkg) *Sym { return pkg.Lookup(name) } func restrictlookup(name string, pkg *Pkg) *Sym { if !exportname(name) && pkg != localpkg { Yyerror("cannot refer to unexported name %s.%s", pkg.Name, name) } return Pkglookup(name, pkg) } // find all the exported symbols in package opkg // and make them available in the current package func importdot(opkg *Pkg, pack *Node) { var s1 *Sym var pkgerror string n := 0 for _, s := range opkg.Syms { if s.Def == nil { continue } if !exportname(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(s1, pkgerror) continue } s1.Def = s.Def s1.Block = s.Block if s1.Def.Name == nil { Dump("s1def", s1.Def) Fatalf("missing Name") } s1.Def.Name.Pack = pack s1.Origpkg = opkg n++ } if n == 0 { // can't possibly be used - there were no symbols yyerrorl(pack.Lineno, "imported and not used: %q", opkg.Path) } } func Nod(op Op, nleft *Node, nright *Node) *Node { n := new(Node) n.Op = op n.Left = nleft n.Right = nright n.Lineno = lineno n.Xoffset = BADWIDTH n.Orig = n switch op { case OCLOSURE, ODCLFUNC: n.Func = new(Func) n.Func.FCurfn = Curfn case ONAME: n.Name = new(Name) n.Name.Param = new(Param) case OLABEL, OPACK: n.Name = new(Name) case ODCLFIELD: if nleft != nil { n.Name = nleft.Name } else { n.Name = new(Name) n.Name.Param = new(Param) } } if n.Name != nil { n.Name.Curfn = Curfn } 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 *Sym) *Node { n := Nod(op, left, nil) n.Sym = sym return n } func saveorignode(n *Node) { if n.Orig != nil { return } norig := Nod(n.Op, nil, nil) *norig = *n n.Orig = norig } // checkMapKeyType checks that Type key is valid for use as a map key. func checkMapKeyType(key *Type) { alg, bad := algtype1(key) if alg != ANOEQ { return } switch bad.Etype { default: Yyerror("invalid map key type %v", key) case TANY: // Will be resolved later. case TFORW: // map[key] used during definition of key. // postpone check until key is fully defined. // if there are multiple uses of map[key] // before key is fully defined, the error // will only be printed for the first one. // good enough. if key.Maplineno == 0 { key.Maplineno = lineno } } } // methcmp sorts by symbol, then by package path for unexported symbols. type methcmp []*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 { a := x[i] b := x[j] if a.Sym == nil && b.Sym == nil { return false } if a.Sym == nil { return true } if b.Sym == nil { return false } if a.Sym.Name != b.Sym.Name { return a.Sym.Name < b.Sym.Name } if !exportname(a.Sym.Name) { if a.Sym.Pkg.Path != b.Sym.Pkg.Path { return a.Sym.Pkg.Path < b.Sym.Pkg.Path } } return false } func Nodintconst(v int64) *Node { c := Nod(OLITERAL, nil, nil) c.Addable = true c.SetVal(Val{new(Mpint)}) c.Val().U.(*Mpint).SetInt64(v) c.Type = Types[TIDEAL] ullmancalc(c) return c } func nodfltconst(v *Mpflt) *Node { c := Nod(OLITERAL, nil, nil) c.Addable = true c.SetVal(Val{newMpflt()}) c.Val().U.(*Mpflt).Set(v) c.Type = Types[TIDEAL] ullmancalc(c) return c } func Nodconst(n *Node, t *Type, v int64) { *n = Node{} n.Op = OLITERAL n.Addable = true ullmancalc(n) n.SetVal(Val{new(Mpint)}) n.Val().U.(*Mpint).SetInt64(v) n.Type = t if t.IsFloat() { Fatalf("nodconst: bad type %v", t) } } func nodnil() *Node { c := Nodintconst(0) c.SetVal(Val{new(NilVal)}) c.Type = Types[TNIL] return c } func Nodbool(b bool) *Node { c := Nodintconst(0) c.SetVal(Val{b}) c.Type = idealbool return c } func aindex(b *Node, t *Type) *Type { hasbound := false var bound int64 b = typecheck(b, Erv) if b != nil { switch consttype(b) { default: Yyerror("array bound must be an integer expression") case CTINT, CTRUNE: hasbound = true bound = b.Int() if bound < 0 { Yyerror("array bound must be non negative") } } } if !hasbound { return typSlice(t) } return typArray(t, bound) } // treecopy recursively copies n, with the exception of // ONAME, OLITERAL, OTYPE, and non-iota ONONAME leaves. // Copies of iota ONONAME nodes are assigned the current // value of iota_. If lineno != 0, it sets the line number // of newly allocated nodes to lineno. func treecopy(n *Node, lineno int32) *Node { if n == nil { return nil } switch n.Op { default: m := *n m.Orig = &m m.Left = treecopy(n.Left, lineno) m.Right = treecopy(n.Right, lineno) m.List.Set(listtreecopy(n.List.Slice(), lineno)) if lineno != 0 { m.Lineno = lineno } if m.Name != nil && n.Op != ODCLFIELD { Dump("treecopy", n) Fatalf("treecopy Name") } return &m case ONONAME: if n.Sym == Lookup("iota") { // Not sure yet whether this is the real iota, // but make a copy of the Node* just in case, // so that all the copies of this const definition // don't have the same iota value. m := *n if lineno != 0 { m.Lineno = lineno } m.Name = new(Name) *m.Name = *n.Name m.Name.Iota = iota_ return &m } return n case ONAME, OLITERAL, OTYPE: return n } } // isnil reports whether n represents the universal untyped zero value "nil". func isnil(n *Node) 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 *Type, et 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 isblank(n *Node) bool { if n == nil { return false } return isblanksym(n.Sym) } func isblanksym(s *Sym) bool { return s != nil && s.Name == "_" } // given receiver of type t (t == r or t == *r) // return type to hang methods off (r). func methtype(t *Type, mustname int) *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 } } // need a type name if t.Sym == nil && (mustname != 0 || !t.IsStruct()) { return nil } // check types if !issimple[t.Etype] { switch t.Etype { default: return nil case TSTRUCT, TARRAY, TMAP, TCHAN, TSTRING, TFUNC: break } } return t } func cplxsubtype(et EType) EType { switch et { case TCOMPLEX64: return TFLOAT32 case TCOMPLEX128: return TFLOAT64 } Fatalf("cplxsubtype: %v\n", Econv(et)) return 0 } func eqnote(a, b *string) bool { return a == b || a != nil && b != nil && *a == *b } // Eqtype reports whether t1 and t2 are identical, following the spec rules. // // Any cyclic type must go through a named type, and if one is // named, it is only identical to the other if they are the same // pointer (t1 == t2), so there's no chance of chasing cycles // ad infinitum, so no need for a depth counter. func Eqtype(t1, t2 *Type) bool { return eqtype1(t1, t2, nil) } type typePair struct { t1 *Type t2 *Type } func eqtype1(t1, t2 *Type, assumedEqual map[typePair]struct{}) bool { if t1 == t2 { return true } if t1 == nil || t2 == nil || t1.Etype != t2.Etype { return false } if t1.Sym != nil || t2.Sym != nil { // Special case: we keep byte/uint8 and rune/int32 // separate for error messages. Treat them as equal. switch t1.Etype { case TUINT8: return (t1 == Types[TUINT8] || t1 == bytetype) && (t2 == Types[TUINT8] || t2 == bytetype) case TINT32: return (t1 == Types[TINT32] || t1 == runetype) && (t2 == Types[TINT32] || t2 == runetype) default: return false } } if assumedEqual == nil { assumedEqual = make(map[typePair]struct{}) } else if _, ok := assumedEqual[typePair{t1, t2}]; ok { return true } assumedEqual[typePair{t1, t2}] = struct{}{} switch t1.Etype { case TINTER, TSTRUCT: t1, i1 := IterFields(t1) t2, i2 := IterFields(t2) for ; t1 != nil && t2 != nil; t1, t2 = i1.Next(), i2.Next() { if t1.Sym != t2.Sym || t1.Embedded != t2.Embedded || !eqtype1(t1.Type, t2.Type, assumedEqual) || !eqnote(t1.Note, t2.Note) { return false } } if t1 == nil && t2 == nil { return true } return false case TFUNC: // Check parameters and result parameters for type equality. // We intentionally ignore receiver parameters for type // equality, because they're never relevant. for _, f := range paramsResults { // Loop over fields in structs, ignoring argument names. ta, ia := IterFields(f(t1)) tb, ib := IterFields(f(t2)) for ; ta != nil && tb != nil; ta, tb = ia.Next(), ib.Next() { if ta.Isddd != tb.Isddd || !eqtype1(ta.Type, tb.Type, assumedEqual) { return false } } if ta != nil || tb != nil { return false } } return true case TARRAY: if t1.NumElem() != t2.NumElem() { return false } case TCHAN: if t1.Chan != t2.Chan { return false } case TMAP: if !eqtype1(t1.Key(), t2.Key(), assumedEqual) { return false } return eqtype1(t1.Val(), t2.Val(), assumedEqual) } return eqtype1(t1.Elem(), t2.Elem(), assumedEqual) } // Are t1 and t2 equal struct types when field names are ignored? // For deciding whether the result struct from g can be copied // directly when compiling f(g()). func eqtypenoname(t1 *Type, t2 *Type) bool { if t1 == nil || t2 == nil || !t1.IsStruct() || !t2.IsStruct() { return false } f1, i1 := IterFields(t1) f2, i2 := IterFields(t2) for { if !Eqtype(f1.Type, f2.Type) { return false } if f1 == nil { return true } f1 = i1.Next() f2 = i2.Next() } } // Is type src assignment compatible to type dst? // If so, return op code to use in conversion. // If not, return 0. func assignop(src *Type, dst *Type, why *string) Op { if why != nil { *why = "" } // TODO(rsc,lvd): This behaves poorly in the presence of inlining. // https://golang.org/issue/2795 if safemode != 0 && importpkg == nil && src != nil && src.Etype == TUNSAFEPTR { Yyerror("cannot use unsafe.Pointer") errorexit() } if src == dst { return OCONVNOP } if src == nil || dst == nil || src.Etype == TFORW || dst.Etype == TFORW || src.Orig == nil || dst.Orig == nil { return 0 } // 1. src type is identical to dst. if Eqtype(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. if Eqtype(src.Orig, dst.Orig) && (src.Sym == nil || dst.Sym == nil || src.IsEmptyInterface()) { return OCONVNOP } // 3. dst is an interface type and src implements dst. if dst.IsInterface() && src.Etype != TNIL { var missing, have *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 } if why != nil { 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%v\n\t\twant %v%v", src, dst, missing.Sym, have.Sym, Tconv(have.Type, FmtShort|FmtByte), missing.Sym, Tconv(missing.Type, FmtShort|FmtByte)) } 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%v\n\t\twant %v%v", src, dst, missing.Sym, have.Sym, Tconv(have.Type, FmtShort|FmtByte), missing.Sym, Tconv(missing.Type, FmtShort|FmtByte)) } else { *why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)", src, dst, missing.Sym) } } return 0 } if isptrto(dst, TINTER) { if why != nil { *why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", dst) } return 0 } if src.IsInterface() && dst.Etype != TBLANK { var missing, have *Field var ptr int if why != nil && implements(dst, src, &missing, &have, &ptr) { *why = ": need type assertion" } return 0 } // 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.Chan == Cboth && dst.IsChan() { if Eqtype(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 TARRAY: if !dst.IsSlice() { break } fallthrough case TPTR32, TPTR64, TFUNC, TMAP, TCHAN, TINTER: 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 0 } // 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 0. func convertop(src *Type, dst *Type, why *string) Op { if why != nil { *why = "" } if src == dst { return OCONVNOP } if src == nil || dst == nil { return 0 } // 1. src can be assigned to dst. op := assignop(src, dst, why) if op != 0 { return op } // 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 0 } if why != nil { *why = "" } // 2. src and dst have identical underlying types. if Eqtype(src.Orig, dst.Orig) { return OCONVNOP } // 3. src and dst are unnamed pointer types // and their base types have identical underlying types. if src.IsPtr() && dst.IsPtr() && src.Sym == nil && dst.Sym == nil { if Eqtype(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 } // 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 == bytetype.Etype { return OARRAYBYTESTR } if src.Elem().Etype == runetype.Etype { return OARRAYRUNESTR } } // 7. src is a string and dst is []byte or []rune. // String to slice. if src.IsString() && dst.IsSlice() { if dst.Elem().Etype == bytetype.Etype { return OSTRARRAYBYTE } if dst.Elem().Etype == runetype.Etype { return OSTRARRAYRUNE } } // 8. src is a pointer or uintptr and dst is unsafe.Pointer. if (src.IsPtr() || src.Etype == TUINTPTR) && dst.Etype == TUNSAFEPTR { return OCONVNOP } // 9. src is unsafe.Pointer and dst is a pointer or uintptr. if src.Etype == TUNSAFEPTR && (dst.IsPtr() || dst.Etype == TUINTPTR) { return OCONVNOP } return 0 } func assignconv(n *Node, t *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 *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") } old := n old.Diag++ // silence errors about n; we'll issue one below n = defaultlit(n, t) old.Diag-- 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 == idealbool && !t.IsBoolean() { if n.Op == ONAME || n.Op == OLITERAL { r := Nod(OCONVNOP, n, nil) r.Type = Types[TBOOL] r.Typecheck = 1 r.Implicit = true n = r } } if Eqtype(n.Type, t) { return n } var why string op := assignop(n.Type, t, &why) if op == 0 { Yyerror("cannot use %v as type %v in %s%s", Nconv(n, FmtLong), t, context(), why) op = OCONV } r := Nod(op, n, nil) r.Type = t r.Typecheck = 1 r.Implicit = true r.Orig = n.Orig return r } // Is this a 64-bit type? func Is64(t *Type) bool { if t == nil { return false } switch Simtype[t.Etype] { case TINT64, TUINT64, TPTR64: return true } return false } // Is a conversion between t1 and t2 a no-op? func Noconv(t1 *Type, t2 *Type) bool { e1 := Simtype[t1.Etype] e2 := Simtype[t2.Etype] switch e1 { case TINT8, TUINT8: return e2 == TINT8 || e2 == TUINT8 case TINT16, TUINT16: return e2 == TINT16 || e2 == TUINT16 case TINT32, TUINT32, TPTR32: return e2 == TINT32 || e2 == TUINT32 || e2 == TPTR32 case TINT64, TUINT64, TPTR64: return e2 == TINT64 || e2 == TUINT64 || e2 == TPTR64 case TFLOAT32: return e2 == TFLOAT32 case TFLOAT64: return e2 == TFLOAT64 } return false } func syslook(name string) *Node { s := Pkglookup(name, Runtimepkg) if s == nil || s.Def == nil { Fatalf("syslook: can't find runtime.%s", name) } return s.Def } // typehash computes a hash value for type t to use in type switch // statements. func typehash(t *Type) uint32 { // Tconv already contains all the necessary logic to generate // a representation that completely describes the type, so using // it here avoids duplicating that code. p := Tconv(t, FmtLeft|FmtUnsigned) // Using MD5 is overkill, but reduces accidental collisions. h := md5.Sum([]byte(p)) return binary.LittleEndian.Uint32(h[:4]) } var initPtrtoDone bool var ( ptrToUint8 *Type ptrToAny *Type ptrToString *Type ptrToBool *Type ptrToInt32 *Type ) func initPtrto() { ptrToUint8 = typPtr(Types[TUINT8]) ptrToAny = typPtr(Types[TANY]) ptrToString = typPtr(Types[TSTRING]) ptrToBool = typPtr(Types[TBOOL]) ptrToInt32 = typPtr(Types[TINT32]) } // Ptrto returns the Type *t. // The returned struct must not be modified. func Ptrto(t *Type) *Type { if Tptr == 0 { Fatalf("ptrto: no tptr") } // Reduce allocations by pre-creating common cases. if !initPtrtoDone { initPtrto() initPtrtoDone = true } switch t { case Types[TUINT8]: return ptrToUint8 case Types[TINT32]: return ptrToInt32 case Types[TANY]: return ptrToAny case Types[TSTRING]: return ptrToString case Types[TBOOL]: return ptrToBool } return typPtr(t) } func frame(context int) { if context != 0 { fmt.Printf("--- external frame ---\n") for _, n := range externdcl { printframenode(n) } return } if Curfn != nil { fmt.Printf("--- %v frame ---\n", Curfn.Func.Nname.Sym) for _, ln := range Curfn.Func.Dcl { printframenode(ln) } } } func printframenode(n *Node) { w := int64(-1) if n.Type != nil { w = n.Type.Width } switch n.Op { case ONAME: fmt.Printf("%v %v G%d %v width=%d\n", Oconv(n.Op, 0), n.Sym, n.Name.Vargen, n.Type, w) case OTYPE: fmt.Printf("%v %v width=%d\n", Oconv(n.Op, 0), n.Type, w) } } // calculate sethi/ullman number // roughly how many registers needed to // compile a node. used to compile the // hardest side first to minimize registers. func ullmancalc(n *Node) { if n == nil { return } var ul int var ur int if n.Ninit.Len() != 0 { ul = UINF goto out } switch n.Op { case OREGISTER, OLITERAL, ONAME: ul = 1 if n.Class == PPARAMREF || (n.Class&PHEAP != 0) { ul++ } goto out case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OASWB: ul = UINF goto out // hard with instrumented code case OANDAND, OOROR: if instrumenting { ul = UINF goto out } } ul = 1 if n.Left != nil { ul = int(n.Left.Ullman) } ur = 1 if n.Right != nil { ur = int(n.Right.Ullman) } if ul == ur { ul += 1 } if ur > ul { ul = ur } out: if ul > 200 { ul = 200 // clamp to uchar with room to grow } n.Ullman = uint8(ul) } func badtype(op Op, tl *Type, tr *Type) { fmt_ := "" if tl != nil { fmt_ += fmt.Sprintf("\n\t%v", tl) } if tr != nil { fmt_ += 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() { fmt_ += "\n\t(*struct vs *interface)" } else if tl.Elem().IsInterface() && tr.Elem().IsStruct() { fmt_ += "\n\t(*interface vs *struct)" } } s := fmt_ Yyerror("illegal types for operand: %v%s", Oconv(op, 0), 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", Oconv(op, 0)) 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", Oconv(op, 0)) 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 := Nod(OXXX, nil, nil) *r = *n r.Left = l r = typecheck(r, Erv) r = walkexpr(r, init) return r case ODOTPTR, OIND: l := safeexpr(n.Left, init) if l == n.Left { return n } a := Nod(OXXX, nil, nil) *a = *n 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 := Nod(OXXX, nil, nil) *a = *n a.Left = l a.Right = r a = walkexpr(a, init) return a } // 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 *Type, init *Nodes) *Node { l := temp(t) a := Nod(OAS, l, n) a = typecheck(a, Etop) 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) } func Setmaxarg(t *Type, extra int32) { dowidth(t) w := t.ArgWidth() if w >= Thearch.MAXWIDTH { Fatalf("bad argwid %v", t) } w += int64(extra) if w >= Thearch.MAXWIDTH { Fatalf("bad argwid %d + %v", extra, t) } if w > Maxarg { Maxarg = w } } // 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 *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 *Sym, t *Type, save **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.Type.Etype == TFUNC && f.Type.Recv() != nil && strings.EqualFold(f.Sym.Name, s.Name)) { if save != nil { *save = f } c++ } } } u = methtype(t, 0) 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 *Sym, t *Type, d int, save **Field, ignorecase bool) (c int, more bool) { if t.Trecur != 0 { return } t.Trecur = 1 var u *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 { goto out } } u = t if u.IsPtr() { u = u.Elem() } if !u.IsStruct() && !u.IsInterface() { goto out } for _, f := range u.Fields().Slice() { if f.Embedded == 0 || f.Sym == nil { continue } if d < 0 { // Found an embedded field at target depth. more = true goto out } a, more1 := adddot1(s, f.Type, d, save, ignorecase) if a != 0 && c == 0 { dotlist[d].field = f } c += a if more1 { more = true } } out: t.Trecur = 0 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 *Sym, t *Type, save **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, Etype|Erv) n.Diag |= n.Left.Diag 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.Implicit = true } case ambig: Yyerror("ambiguous selector %v", n) n.Left = nil } return n } // code to help generate trampoline // functions for methods on embedded // subtypes. // 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 *Field followptr bool } var slist []Symlink func expand0(t *Type, followptr bool) { u := t if u.IsPtr() { followptr = true u = u.Elem() } if u.IsInterface() { for _, f := range u.Fields().Slice() { if f.Sym.Flags&SymUniq != 0 { continue } f.Sym.Flags |= SymUniq slist = append(slist, Symlink{field: f, followptr: followptr}) } return } u = methtype(t, 0) if u != nil { for _, f := range u.Methods().Slice() { if f.Sym.Flags&SymUniq != 0 { continue } f.Sym.Flags |= SymUniq slist = append(slist, Symlink{field: f, followptr: followptr}) } } } func expand1(t *Type, top, followptr bool) { if t.Trecur != 0 { return } t.Trecur = 1 if !top { expand0(t, followptr) } u := t if u.IsPtr() { followptr = true u = u.Elem() } if !u.IsStruct() && !u.IsInterface() { goto out } for _, f := range u.Fields().Slice() { if f.Embedded == 0 { continue } if f.Sym == nil { continue } expand1(f.Type, false, followptr) } out: t.Trecur = 0 } func expandmeth(t *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.Flags |= SymUniq } // generate all reachable methods slist = slist[:0] expand1(t, true, false) // check each method to be uniquely reachable var ms []*Field for i, sl := range slist { slist[i].field = nil sl.field.Sym.Flags &^= SymUniq var f *Field if path, _ := dotpath(sl.field.Sym, t, &f, false); path == nil { continue } // dotpath may have dug out arbitrary fields, we only want methods. if f.Type.Etype != TFUNC || f.Type.Recv() == nil { continue } // add it to the base type method list f = f.Copy() f.Embedded = 1 // needs a trampoline if sl.followptr { f.Embedded = 2 } ms = append(ms, f) } for _, f := range t.Methods().Slice() { f.Sym.Flags &^= SymUniq } ms = append(ms, t.Methods().Slice()...) t.AllMethods().Set(ms) } // Given funarg struct list, return list of ODCLFIELD Node fn args. func structargs(tl *Type, mustname bool) []*Node { var args []*Node gen := 0 for _, t := range tl.Fields().Slice() { var n *Node if mustname && (t.Sym == nil || t.Sym.Name == "_") { // invent a name so that we can refer to it in the trampoline buf := fmt.Sprintf(".anon%d", gen) gen++ n = newname(Lookup(buf)) } else if t.Sym != nil { n = newname(t.Sym) } a := Nod(ODCLFIELD, n, typenod(t.Type)) a.Isddd = t.Isddd if n != nil { n.Isddd = 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 var genwrapper_linehistdone int = 0 func genwrapper(rcvr *Type, method *Field, newnam *Sym, iface int) { if false && Debug['r'] != 0 { fmt.Printf("genwrapper rcvrtype=%v method=%v newnam=%v\n", rcvr, method, newnam) } lexlineno++ lineno = lexlineno if genwrapper_linehistdone == 0 { // All the wrappers can share the same linehist entry. linehistpush("") genwrapper_linehistdone = 1 } dclcontext = PEXTERN markdcl() this := Nod(ODCLFIELD, newname(Lookup(".this")), typenod(rcvr)) this.Left.Name.Param.Ntype = this.Right in := structargs(method.Type.Params(), true) out := structargs(method.Type.Results(), false) t := Nod(OTFUNC, nil, nil) l := []*Node{this} if iface != 0 && rcvr.Width < Types[Tptr].Width { // Building method for interface table and receiver // is smaller than the single pointer-sized word // that the interface call will pass in. // Add a dummy padding argument after the // receiver to make up the difference. tpad := typArray(Types[TUINT8], Types[Tptr].Width-rcvr.Width) pad := Nod(ODCLFIELD, newname(Lookup(".pad")), typenod(tpad)) l = append(l, pad) } t.List.Set(append(l, in...)) t.Rlist.Set(out) fn := Nod(ODCLFUNC, nil, nil) fn.Func.Nname = newname(newnam) fn.Func.Nname.Name.Defn = fn fn.Func.Nname.Name.Param.Ntype = t declare(fn.Func.Nname, PFUNC) funchdr(fn) // arg list var args []*Node isddd := false for _, n := range in { args = append(args, n.Left) isddd = n.Left.Isddd } 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, this.Left, nodnil()) // these strings are already in the reflect tables, // so no space cost to use them here. var l []*Node var v Val v.U = rcvr.Elem().Sym.Pkg.Name // package name l = append(l, nodlit(v)) v.U = rcvr.Elem().Sym.Name // type name l = append(l, nodlit(v)) v.U = method.Sym.Name l = append(l, nodlit(v)) // method name call := Nod(OCALL, syslook("panicwrap"), nil) call.List.Set(l) n.Nbody.Set1(call) fn.Nbody.Append(n) } dot := adddot(NodSym(OXDOT, this.Left, method.Sym)) // generate call if !instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !isifacemethod(method.Type) { // 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, this.Left, Nod(OCONVNOP, dot, nil)) as.Right.Type = rcvr fn.Nbody.Append(as) n := Nod(ORETJMP, nil, nil) n.Left = newname(methodsym(method.Sym, methodrcvr, 0)) fn.Nbody.Append(n) } else { fn.Func.Wrapper = true // ignore frame for panic+recover matching call := Nod(OCALL, dot, nil) call.List.Set(args) call.Isddd = isddd if method.Type.Results().NumFields() > 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(fn) Curfn = fn popdcl() testdclstack() // wrappers where T is anonymous (struct or interface) can be duplicated. if rcvr.IsStruct() || rcvr.IsInterface() || rcvr.IsPtr() && rcvr.Elem().IsStruct() { fn.Func.Dupok = true } fn = typecheck(fn, Etop) typecheckslice(fn.Nbody.Slice(), Etop) inlcalls(fn) escAnalyze([]*Node{fn}, false) Curfn = nil funccompile(fn) } func hashmem(t *Type) *Node { sym := Pkglookup("memhash", Runtimepkg) n := newname(sym) n.Class = PFUNC tfn := Nod(OTFUNC, nil, nil) tfn.List.Append(Nod(ODCLFIELD, nil, typenod(Ptrto(t)))) tfn.List.Append(Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR]))) tfn.List.Append(Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR]))) tfn.Rlist.Append(Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR]))) tfn = typecheck(tfn, Etype) n.Type = tfn.Type return n } func ifacelookdot(s *Sym, t *Type, followptr *bool, ignorecase bool) *Field { *followptr = false if t == nil { return nil } var m *Field path, ambig := dotpath(s, t, &m, ignorecase) if path == nil { if ambig { Yyerror("%v.%v is ambiguous", t, s) } return nil } for _, d := range path { if d.field.Type.IsPtr() { *followptr = true break } } if m.Type.Etype != TFUNC || m.Type.Recv() == nil { Yyerror("%v.%v is a field, not a method", t, s) return nil } return m } func implements(t, iface *Type, m, samename **Field, ptr *int) bool { t0 := t if t == nil { return false } // if this is too slow, // could sort these first // and then do one loop. if t.IsInterface() { for _, im := range iface.Fields().Slice() { for _, tm := range t.Fields().Slice() { if tm.Sym == im.Sym { if Eqtype(tm.Type, im.Type) { goto found } *m = im *samename = tm *ptr = 0 return false } } *m = im *samename = nil *ptr = 0 return false found: } return true } t = methtype(t, 0) if t != nil { expandmeth(t) } for _, im := range iface.Fields().Slice() { if im.Broke { continue } var followptr bool tm := ifacelookdot(im.Sym, t, &followptr, false) if tm == nil || tm.Nointerface || !Eqtype(tm.Type, im.Type) { if tm == nil { tm = ifacelookdot(im.Sym, t, &followptr, true) } *m = im *samename = tm *ptr = 0 return false } // 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 } } return true } // even simpler simtype; get rid of ptr, bool. // assuming that the front end has rejected // all the invalid conversions (like ptr -> bool) func Simsimtype(t *Type) EType { if t == nil { return 0 } et := Simtype[t.Etype] switch et { case TPTR32: et = TUINT32 case TPTR64: et = TUINT64 case TBOOL: et = TUINT8 } return et } func listtreecopy(l []*Node, lineno int32) []*Node { var out []*Node for _, n := range l { out = append(out, treecopy(n, lineno)) } return out } func liststmt(l []*Node) *Node { n := Nod(OBLOCK, nil, nil) n.List.Set(l) if len(l) != 0 { n.Lineno = l[0].Lineno } return n } // return power of 2 of the constant // operand. -1 if it is not a power of 2. // 1000+ if it is a -(power of 2) func powtwo(n *Node) int { if n == nil || n.Op != OLITERAL || n.Type == nil { return -1 } if !n.Type.IsInteger() { return -1 } v := uint64(n.Int()) b := uint64(1) for i := 0; i < 64; i++ { if b == v { return i } b = b << 1 } if !n.Type.IsSigned() { return -1 } v = -v b = 1 for i := 0; i < 64; i++ { if b == v { return i + 1000 } b = b << 1 } return -1 } // return the unsigned type for // a signed integer type. // returns T if input is not a // signed integer type. func tounsigned(t *Type) *Type { // this is types[et+1], but not sure // that this relation is immutable switch t.Etype { default: fmt.Printf("tounsigned: unknown type %v\n", t) t = nil case TINT: t = Types[TUINT] case TINT8: t = Types[TUINT8] case TINT16: t = Types[TUINT16] case TINT32: t = Types[TUINT32] case TINT64: t = Types[TUINT64] } return t } func ngotype(n *Node) *Sym { if n.Type != nil { return typenamesym(n.Type) } return nil } // Convert raw string to the prefix that will be used in the symbol // table. All control characters, space, '%' and '"', as well as // non-7-bit clean bytes turn into %xx. The period needs escaping // only in the last segment of the path, and it makes for happier // users if we escape that as little as possible. // // If you edit this, edit ../../debug/goobj/read.go:/importPathToPrefix too. func pathtoprefix(s string) string { slash := strings.LastIndex(s, "/") for i := 0; i < len(s); i++ { c := s[i] if c <= ' ' || i >= slash && c == '.' || c == '%' || c == '"' || c >= 0x7F { var buf bytes.Buffer for i := 0; i < len(s); i++ { c := s[i] if c <= ' ' || i >= slash && c == '.' || c == '%' || c == '"' || c >= 0x7F { fmt.Fprintf(&buf, "%%%02x", c) continue } buf.WriteByte(c) } return buf.String() } } return s } var pkgMap = make(map[string]*Pkg) var pkgs []*Pkg func mkpkg(path string) *Pkg { if p := pkgMap[path]; p != nil { return p } p := new(Pkg) p.Path = path p.Prefix = pathtoprefix(path) p.Syms = make(map[string]*Sym) pkgMap[path] = p pkgs = append(pkgs, p) return p } // 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 } switch n.Op { // There may be multiple refs to this node; // introduce OCONVNOP to hold init list. case ONAME, OLITERAL: n = Nod(OCONVNOP, n, nil) n.Type = n.Left.Type n.Typecheck = 1 } n.Ninit.Set(append(init, n.Ninit.Slice()...)) n.Ullman = UINF return n } var reservedimports = []string{ "go", "type", } func isbadimport(path string) 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 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 } func checknil(x *Node, init *Nodes) { if x.Type.IsInterface() { x = Nod(OITAB, x, nil) x = typecheck(x, Erv) } n := Nod(OCHECKNIL, x, nil) n.Typecheck = 1 init.Append(n) } // Can this type be stored directly in an interface word? // Yes, if the representation is a single pointer. func isdirectiface(t *Type) bool { switch t.Etype { case TPTR32, TPTR64, 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 } // iet returns 'T' if t is a concrete type, // 'I' if t is an interface type, and 'E' if t is an empty interface type. // It is used to build calls to the conv* and assert* runtime routines. func (t *Type) iet() byte { if t.IsEmptyInterface() { return 'E' } if t.IsInterface() { return 'I' } return 'T' }