diff options
Diffstat (limited to 'src/cmd/compile/internal/noder')
34 files changed, 16448 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/noder/codes.go b/src/cmd/compile/internal/noder/codes.go new file mode 100644 index 0000000..f8cb772 --- /dev/null +++ b/src/cmd/compile/internal/noder/codes.go @@ -0,0 +1,124 @@ +// UNREVIEWED + +// Copyright 2021 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 noder + +type code interface { + marker() syncMarker + value() int +} + +type codeVal int + +func (c codeVal) marker() syncMarker { return syncVal } +func (c codeVal) value() int { return int(c) } + +const ( + valBool codeVal = iota + valString + valInt64 + valBigInt + valBigRat + valBigFloat +) + +type codeType int + +func (c codeType) marker() syncMarker { return syncType } +func (c codeType) value() int { return int(c) } + +const ( + typeBasic codeType = iota + typeNamed + typePointer + typeSlice + typeArray + typeChan + typeMap + typeSignature + typeStruct + typeInterface + typeUnion + typeTypeParam +) + +type codeObj int + +func (c codeObj) marker() syncMarker { return syncCodeObj } +func (c codeObj) value() int { return int(c) } + +const ( + objAlias codeObj = iota + objConst + objType + objFunc + objVar + objStub +) + +type codeStmt int + +func (c codeStmt) marker() syncMarker { return syncStmt1 } +func (c codeStmt) value() int { return int(c) } + +const ( + stmtEnd codeStmt = iota + stmtLabel + stmtBlock + stmtExpr + stmtSend + stmtAssign + stmtAssignOp + stmtIncDec + stmtBranch + stmtCall + stmtReturn + stmtIf + stmtFor + stmtSwitch + stmtSelect + + // TODO(mdempsky): Remove after we don't care about toolstash -cmp. + stmtTypeDeclHack +) + +type codeExpr int + +func (c codeExpr) marker() syncMarker { return syncExpr } +func (c codeExpr) value() int { return int(c) } + +// TODO(mdempsky): Split expr into addr, for lvalues. +const ( + exprNone codeExpr = iota + exprConst + exprType // type expression + exprLocal // local variable + exprName // global variable or function + exprBlank + exprCompLit + exprFuncLit + exprSelector + exprIndex + exprSlice + exprAssert + exprUnaryOp + exprBinaryOp + exprCall + exprConvert +) + +type codeDecl int + +func (c codeDecl) marker() syncMarker { return syncDecl } +func (c codeDecl) value() int { return int(c) } + +const ( + declEnd codeDecl = iota + declFunc + declMethod + declVar + declOther +) diff --git a/src/cmd/compile/internal/noder/decl.go b/src/cmd/compile/internal/noder/decl.go new file mode 100644 index 0000000..f985648 --- /dev/null +++ b/src/cmd/compile/internal/noder/decl.go @@ -0,0 +1,358 @@ +// Copyright 2021 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 noder + +import ( + "go/constant" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/syntax" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/compile/internal/types2" +) + +// TODO(mdempsky): Skip blank declarations? Probably only safe +// for declarations without pragmas. + +func (g *irgen) decls(res *ir.Nodes, decls []syntax.Decl) { + for _, decl := range decls { + switch decl := decl.(type) { + case *syntax.ConstDecl: + g.constDecl(res, decl) + case *syntax.FuncDecl: + g.funcDecl(res, decl) + case *syntax.TypeDecl: + if ir.CurFunc == nil { + continue // already handled in irgen.generate + } + g.typeDecl(res, decl) + case *syntax.VarDecl: + g.varDecl(res, decl) + default: + g.unhandled("declaration", decl) + } + } +} + +func (g *irgen) importDecl(p *noder, decl *syntax.ImportDecl) { + g.pragmaFlags(decl.Pragma, 0) + + // Get the imported package's path, as resolved already by types2 + // and gcimporter. This is the same path as would be computed by + // parseImportPath. + switch pkgNameOf(g.info, decl).Imported().Path() { + case "unsafe": + p.importedUnsafe = true + case "embed": + p.importedEmbed = true + } +} + +// pkgNameOf returns the PkgName associated with the given ImportDecl. +func pkgNameOf(info *types2.Info, decl *syntax.ImportDecl) *types2.PkgName { + if name := decl.LocalPkgName; name != nil { + return info.Defs[name].(*types2.PkgName) + } + return info.Implicits[decl].(*types2.PkgName) +} + +func (g *irgen) constDecl(out *ir.Nodes, decl *syntax.ConstDecl) { + g.pragmaFlags(decl.Pragma, 0) + + for _, name := range decl.NameList { + name, obj := g.def(name) + + // For untyped numeric constants, make sure the value + // representation matches what the rest of the + // compiler (really just iexport) expects. + // TODO(mdempsky): Revisit after #43891 is resolved. + val := obj.(*types2.Const).Val() + switch name.Type() { + case types.UntypedInt, types.UntypedRune: + val = constant.ToInt(val) + case types.UntypedFloat: + val = constant.ToFloat(val) + case types.UntypedComplex: + val = constant.ToComplex(val) + } + name.SetVal(val) + + out.Append(ir.NewDecl(g.pos(decl), ir.ODCLCONST, name)) + } +} + +func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) { + assert(g.curDecl == "") + // Set g.curDecl to the function name, as context for the type params declared + // during types2-to-types1 translation if this is a generic function. + g.curDecl = decl.Name.Value + obj2 := g.info.Defs[decl.Name] + recv := types2.AsSignature(obj2.Type()).Recv() + if recv != nil { + t2 := deref2(recv.Type()) + // This is a method, so set g.curDecl to recvTypeName.methName instead. + g.curDecl = t2.(*types2.Named).Obj().Name() + "." + g.curDecl + } + + fn := ir.NewFunc(g.pos(decl)) + fn.Nname, _ = g.def(decl.Name) + fn.Nname.Func = fn + fn.Nname.Defn = fn + + fn.Pragma = g.pragmaFlags(decl.Pragma, funcPragmas) + if fn.Pragma&ir.Systemstack != 0 && fn.Pragma&ir.Nosplit != 0 { + base.ErrorfAt(fn.Pos(), "go:nosplit and go:systemstack cannot be combined") + } + if fn.Pragma&ir.Nointerface != 0 { + // Propagate //go:nointerface from Func.Pragma to Field.Nointerface. + // This is a bit roundabout, but this is the earliest point where we've + // processed the function's pragma flags, and we've also already created + // the Fields to represent the receiver's method set. + if recv := fn.Type().Recv(); recv != nil { + typ := types.ReceiverBaseType(recv.Type) + if orig := typ.OrigType(); orig != nil { + // For a generic method, we mark the methods on the + // base generic type, since those are the methods + // that will be stenciled. + typ = orig + } + meth := typecheck.Lookdot1(fn, typecheck.Lookup(decl.Name.Value), typ, typ.Methods(), 0) + meth.SetNointerface(true) + } + } + + if decl.Body != nil && fn.Pragma&ir.Noescape != 0 { + base.ErrorfAt(fn.Pos(), "can only use //go:noescape with external func implementations") + } + + if decl.Name.Value == "init" && decl.Recv == nil { + g.target.Inits = append(g.target.Inits, fn) + } + + saveHaveEmbed := g.haveEmbed + saveCurDecl := g.curDecl + g.curDecl = "" + g.later(func() { + defer func(b bool, s string) { + // Revert haveEmbed and curDecl back to what they were before + // the "later" function. + g.haveEmbed = b + g.curDecl = s + }(g.haveEmbed, g.curDecl) + + // Set haveEmbed and curDecl to what they were for this funcDecl. + g.haveEmbed = saveHaveEmbed + g.curDecl = saveCurDecl + if fn.Type().HasTParam() { + g.topFuncIsGeneric = true + } + g.funcBody(fn, decl.Recv, decl.Type, decl.Body) + g.topFuncIsGeneric = false + if fn.Type().HasTParam() && fn.Body != nil { + // Set pointers to the dcls/body of a generic function/method in + // the Inl struct, so it is marked for export, is available for + // stenciling, and works with Inline_Flood(). + fn.Inl = &ir.Inline{ + Cost: 1, + Dcl: fn.Dcl, + Body: fn.Body, + } + } + + out.Append(fn) + }) +} + +func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) { + // Set the position for any error messages we might print (e.g. too large types). + base.Pos = g.pos(decl) + assert(ir.CurFunc != nil || g.curDecl == "") + // Set g.curDecl to the type name, as context for the type params declared + // during types2-to-types1 translation if this is a generic type. + saveCurDecl := g.curDecl + g.curDecl = decl.Name.Value + if decl.Alias { + name, _ := g.def(decl.Name) + g.pragmaFlags(decl.Pragma, 0) + assert(name.Alias()) // should be set by irgen.obj + + out.Append(ir.NewDecl(g.pos(decl), ir.ODCLTYPE, name)) + g.curDecl = "" + return + } + + // Prevent size calculations until we set the underlying type. + types.DeferCheckSize() + + name, obj := g.def(decl.Name) + ntyp, otyp := name.Type(), obj.Type() + if ir.CurFunc != nil { + ntyp.SetVargen() + } + + pragmas := g.pragmaFlags(decl.Pragma, typePragmas) + name.SetPragma(pragmas) // TODO(mdempsky): Is this still needed? + + if pragmas&ir.NotInHeap != 0 { + ntyp.SetNotInHeap(true) + } + + // We need to use g.typeExpr(decl.Type) here to ensure that for + // chained, defined-type declarations like: + // + // type T U + // + // //go:notinheap + // type U struct { … } + // + // we mark both T and U as NotInHeap. If we instead used just + // g.typ(otyp.Underlying()), then we'd instead set T's underlying + // type directly to the struct type (which is not marked NotInHeap) + // and fail to mark T as NotInHeap. + // + // Also, we rely here on Type.SetUnderlying allowing passing a + // defined type and handling forward references like from T to U + // above. Contrast with go/types's Named.SetUnderlying, which + // disallows this. + // + // [mdempsky: Subtleties like these are why I always vehemently + // object to new type pragmas.] + ntyp.SetUnderlying(g.typeExpr(decl.Type)) + + tparams := otyp.(*types2.Named).TypeParams() + if n := tparams.Len(); n > 0 { + rparams := make([]*types.Type, n) + for i := range rparams { + rparams[i] = g.typ(tparams.At(i)) + } + // This will set hasTParam flag if any rparams are not concrete types. + ntyp.SetRParams(rparams) + } + types.ResumeCheckSize() + + g.curDecl = saveCurDecl + if otyp, ok := otyp.(*types2.Named); ok && otyp.NumMethods() != 0 { + methods := make([]*types.Field, otyp.NumMethods()) + for i := range methods { + m := otyp.Method(i) + // Set g.curDecl to recvTypeName.methName, as context for the + // method-specific type params in the receiver. + g.curDecl = decl.Name.Value + "." + m.Name() + meth := g.obj(m) + methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type()) + methods[i].Nname = meth + g.curDecl = "" + } + ntyp.Methods().Set(methods) + } + + out.Append(ir.NewDecl(g.pos(decl), ir.ODCLTYPE, name)) +} + +func (g *irgen) varDecl(out *ir.Nodes, decl *syntax.VarDecl) { + pos := g.pos(decl) + // Set the position for any error messages we might print (e.g. too large types). + base.Pos = pos + names := make([]*ir.Name, len(decl.NameList)) + for i, name := range decl.NameList { + names[i], _ = g.def(name) + } + + if decl.Pragma != nil { + pragma := decl.Pragma.(*pragmas) + varEmbed(g.makeXPos, names[0], decl, pragma, g.haveEmbed) + g.reportUnused(pragma) + } + + haveEmbed := g.haveEmbed + do := func() { + defer func(b bool) { g.haveEmbed = b }(g.haveEmbed) + + g.haveEmbed = haveEmbed + values := g.exprList(decl.Values) + + var as2 *ir.AssignListStmt + if len(values) != 0 && len(names) != len(values) { + as2 = ir.NewAssignListStmt(pos, ir.OAS2, make([]ir.Node, len(names)), values) + } + + for i, name := range names { + if ir.CurFunc != nil { + out.Append(ir.NewDecl(pos, ir.ODCL, name)) + } + if as2 != nil { + as2.Lhs[i] = name + name.Defn = as2 + } else { + as := ir.NewAssignStmt(pos, name, nil) + if len(values) != 0 { + as.Y = values[i] + name.Defn = as + } else if ir.CurFunc == nil { + name.Defn = as + } + if !g.delayTransform() { + lhs := []ir.Node{as.X} + rhs := []ir.Node{} + if as.Y != nil { + rhs = []ir.Node{as.Y} + } + transformAssign(as, lhs, rhs) + as.X = lhs[0] + if as.Y != nil { + as.Y = rhs[0] + } + } + as.SetTypecheck(1) + out.Append(as) + } + } + if as2 != nil { + if !g.delayTransform() { + transformAssign(as2, as2.Lhs, as2.Rhs) + } + as2.SetTypecheck(1) + out.Append(as2) + } + } + + // If we're within a function, we need to process the assignment + // part of the variable declaration right away. Otherwise, we leave + // it to be handled after all top-level declarations are processed. + if ir.CurFunc != nil { + do() + } else { + g.later(do) + } +} + +// pragmaFlags returns any specified pragma flags included in allowed, +// and reports errors about any other, unexpected pragmas. +func (g *irgen) pragmaFlags(pragma syntax.Pragma, allowed ir.PragmaFlag) ir.PragmaFlag { + if pragma == nil { + return 0 + } + p := pragma.(*pragmas) + present := p.Flag & allowed + p.Flag &^= allowed + g.reportUnused(p) + return present +} + +// reportUnused reports errors about any unused pragmas. +func (g *irgen) reportUnused(pragma *pragmas) { + for _, pos := range pragma.Pos { + if pos.Flag&pragma.Flag != 0 { + base.ErrorfAt(g.makeXPos(pos.Pos), "misplaced compiler directive") + } + } + if len(pragma.Embeds) > 0 { + for _, e := range pragma.Embeds { + base.ErrorfAt(g.makeXPos(e.Pos), "misplaced go:embed directive") + } + } +} diff --git a/src/cmd/compile/internal/noder/decoder.go b/src/cmd/compile/internal/noder/decoder.go new file mode 100644 index 0000000..2c18727 --- /dev/null +++ b/src/cmd/compile/internal/noder/decoder.go @@ -0,0 +1,302 @@ +// UNREVIEWED + +// Copyright 2021 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 noder + +import ( + "encoding/binary" + "fmt" + "go/constant" + "go/token" + "math/big" + "os" + "runtime" + "strings" + + "cmd/compile/internal/base" +) + +type pkgDecoder struct { + pkgPath string + + elemEndsEnds [numRelocs]uint32 + elemEnds []uint32 + elemData string +} + +func newPkgDecoder(pkgPath, input string) pkgDecoder { + pr := pkgDecoder{ + pkgPath: pkgPath, + } + + // TODO(mdempsky): Implement direct indexing of input string to + // avoid copying the position information. + + r := strings.NewReader(input) + + assert(binary.Read(r, binary.LittleEndian, pr.elemEndsEnds[:]) == nil) + + pr.elemEnds = make([]uint32, pr.elemEndsEnds[len(pr.elemEndsEnds)-1]) + assert(binary.Read(r, binary.LittleEndian, pr.elemEnds[:]) == nil) + + pos, err := r.Seek(0, os.SEEK_CUR) + assert(err == nil) + + pr.elemData = input[pos:] + assert(len(pr.elemData) == int(pr.elemEnds[len(pr.elemEnds)-1])) + + return pr +} + +func (pr *pkgDecoder) numElems(k reloc) int { + count := int(pr.elemEndsEnds[k]) + if k > 0 { + count -= int(pr.elemEndsEnds[k-1]) + } + return count +} + +func (pr *pkgDecoder) totalElems() int { + return len(pr.elemEnds) +} + +func (pr *pkgDecoder) absIdx(k reloc, idx int) int { + absIdx := idx + if k > 0 { + absIdx += int(pr.elemEndsEnds[k-1]) + } + if absIdx >= int(pr.elemEndsEnds[k]) { + base.Fatalf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds) + } + return absIdx +} + +func (pr *pkgDecoder) dataIdx(k reloc, idx int) string { + absIdx := pr.absIdx(k, idx) + + var start uint32 + if absIdx > 0 { + start = pr.elemEnds[absIdx-1] + } + end := pr.elemEnds[absIdx] + + return pr.elemData[start:end] +} + +func (pr *pkgDecoder) stringIdx(idx int) string { + return pr.dataIdx(relocString, idx) +} + +func (pr *pkgDecoder) newDecoder(k reloc, idx int, marker syncMarker) decoder { + r := pr.newDecoderRaw(k, idx) + r.sync(marker) + return r +} + +func (pr *pkgDecoder) newDecoderRaw(k reloc, idx int) decoder { + r := decoder{ + common: pr, + k: k, + idx: idx, + } + + // TODO(mdempsky) r.data.Reset(...) after #44505 is resolved. + r.data = *strings.NewReader(pr.dataIdx(k, idx)) + + r.sync(syncRelocs) + r.relocs = make([]relocEnt, r.len()) + for i := range r.relocs { + r.sync(syncReloc) + r.relocs[i] = relocEnt{reloc(r.len()), r.len()} + } + + return r +} + +type decoder struct { + common *pkgDecoder + + relocs []relocEnt + data strings.Reader + + k reloc + idx int +} + +func (r *decoder) checkErr(err error) { + if err != nil { + base.Fatalf("unexpected error: %v", err) + } +} + +func (r *decoder) rawUvarint() uint64 { + x, err := binary.ReadUvarint(&r.data) + r.checkErr(err) + return x +} + +func (r *decoder) rawVarint() int64 { + ux := r.rawUvarint() + + // Zig-zag decode. + x := int64(ux >> 1) + if ux&1 != 0 { + x = ^x + } + return x +} + +func (r *decoder) rawReloc(k reloc, idx int) int { + e := r.relocs[idx] + assert(e.kind == k) + return e.idx +} + +func (r *decoder) sync(mWant syncMarker) { + if !enableSync { + return + } + + pos, _ := r.data.Seek(0, os.SEEK_CUR) // TODO(mdempsky): io.SeekCurrent after #44505 is resolved + mHave := syncMarker(r.rawUvarint()) + writerPCs := make([]int, r.rawUvarint()) + for i := range writerPCs { + writerPCs[i] = int(r.rawUvarint()) + } + + if mHave == mWant { + return + } + + // There's some tension here between printing: + // + // (1) full file paths that tools can recognize (e.g., so emacs + // hyperlinks the "file:line" text for easy navigation), or + // + // (2) short file paths that are easier for humans to read (e.g., by + // omitting redundant or irrelevant details, so it's easier to + // focus on the useful bits that remain). + // + // The current formatting favors the former, as it seems more + // helpful in practice. But perhaps the formatting could be improved + // to better address both concerns. For example, use relative file + // paths if they would be shorter, or rewrite file paths to contain + // "$GOROOT" (like objabi.AbsFile does) if tools can be taught how + // to reliably expand that again. + + fmt.Printf("export data desync: package %q, section %v, index %v, offset %v\n", r.common.pkgPath, r.k, r.idx, pos) + + fmt.Printf("\nfound %v, written at:\n", mHave) + if len(writerPCs) == 0 { + fmt.Printf("\t[stack trace unavailable; recompile package %q with -d=syncframes]\n", r.common.pkgPath) + } + for _, pc := range writerPCs { + fmt.Printf("\t%s\n", r.common.stringIdx(r.rawReloc(relocString, pc))) + } + + fmt.Printf("\nexpected %v, reading at:\n", mWant) + var readerPCs [32]uintptr // TODO(mdempsky): Dynamically size? + n := runtime.Callers(2, readerPCs[:]) + for _, pc := range fmtFrames(readerPCs[:n]...) { + fmt.Printf("\t%s\n", pc) + } + + // We already printed a stack trace for the reader, so now we can + // simply exit. Printing a second one with panic or base.Fatalf + // would just be noise. + os.Exit(1) +} + +func (r *decoder) bool() bool { + r.sync(syncBool) + x, err := r.data.ReadByte() + r.checkErr(err) + assert(x < 2) + return x != 0 +} + +func (r *decoder) int64() int64 { + r.sync(syncInt64) + return r.rawVarint() +} + +func (r *decoder) uint64() uint64 { + r.sync(syncUint64) + return r.rawUvarint() +} + +func (r *decoder) len() int { x := r.uint64(); v := int(x); assert(uint64(v) == x); return v } +func (r *decoder) int() int { x := r.int64(); v := int(x); assert(int64(v) == x); return v } +func (r *decoder) uint() uint { x := r.uint64(); v := uint(x); assert(uint64(v) == x); return v } + +func (r *decoder) code(mark syncMarker) int { + r.sync(mark) + return r.len() +} + +func (r *decoder) reloc(k reloc) int { + r.sync(syncUseReloc) + return r.rawReloc(k, r.len()) +} + +func (r *decoder) string() string { + r.sync(syncString) + return r.common.stringIdx(r.reloc(relocString)) +} + +func (r *decoder) strings() []string { + res := make([]string, r.len()) + for i := range res { + res[i] = r.string() + } + return res +} + +func (r *decoder) value() constant.Value { + r.sync(syncValue) + isComplex := r.bool() + val := r.scalar() + if isComplex { + val = constant.BinaryOp(val, token.ADD, constant.MakeImag(r.scalar())) + } + return val +} + +func (r *decoder) scalar() constant.Value { + switch tag := codeVal(r.code(syncVal)); tag { + default: + panic(fmt.Sprintf("unexpected scalar tag: %v", tag)) + + case valBool: + return constant.MakeBool(r.bool()) + case valString: + return constant.MakeString(r.string()) + case valInt64: + return constant.MakeInt64(r.int64()) + case valBigInt: + return constant.Make(r.bigInt()) + case valBigRat: + num := r.bigInt() + denom := r.bigInt() + return constant.Make(new(big.Rat).SetFrac(num, denom)) + case valBigFloat: + return constant.Make(r.bigFloat()) + } +} + +func (r *decoder) bigInt() *big.Int { + v := new(big.Int).SetBytes([]byte(r.string())) + if r.bool() { + v.Neg(v) + } + return v +} + +func (r *decoder) bigFloat() *big.Float { + v := new(big.Float).SetPrec(512) + assert(v.UnmarshalText([]byte(r.string())) == nil) + return v +} diff --git a/src/cmd/compile/internal/noder/encoder.go b/src/cmd/compile/internal/noder/encoder.go new file mode 100644 index 0000000..b07b3a4 --- /dev/null +++ b/src/cmd/compile/internal/noder/encoder.go @@ -0,0 +1,285 @@ +// UNREVIEWED + +// Copyright 2021 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 noder + +import ( + "bytes" + "encoding/binary" + "fmt" + "go/constant" + "io" + "math/big" + "runtime" + + "cmd/compile/internal/base" +) + +type pkgEncoder struct { + elems [numRelocs][]string + + stringsIdx map[string]int +} + +func newPkgEncoder() pkgEncoder { + return pkgEncoder{ + stringsIdx: make(map[string]int), + } +} + +func (pw *pkgEncoder) dump(out io.Writer) { + writeUint32 := func(x uint32) { + assert(binary.Write(out, binary.LittleEndian, x) == nil) + } + + var sum uint32 + for _, elems := range &pw.elems { + sum += uint32(len(elems)) + writeUint32(sum) + } + + sum = 0 + for _, elems := range &pw.elems { + for _, elem := range elems { + sum += uint32(len(elem)) + writeUint32(sum) + } + } + + for _, elems := range &pw.elems { + for _, elem := range elems { + _, err := io.WriteString(out, elem) + assert(err == nil) + } + } +} + +func (pw *pkgEncoder) stringIdx(s string) int { + if idx, ok := pw.stringsIdx[s]; ok { + assert(pw.elems[relocString][idx] == s) + return idx + } + + idx := len(pw.elems[relocString]) + pw.elems[relocString] = append(pw.elems[relocString], s) + pw.stringsIdx[s] = idx + return idx +} + +func (pw *pkgEncoder) newEncoder(k reloc, marker syncMarker) encoder { + e := pw.newEncoderRaw(k) + e.sync(marker) + return e +} + +func (pw *pkgEncoder) newEncoderRaw(k reloc) encoder { + idx := len(pw.elems[k]) + pw.elems[k] = append(pw.elems[k], "") // placeholder + + return encoder{ + p: pw, + k: k, + idx: idx, + } +} + +// Encoders + +type encoder struct { + p *pkgEncoder + + relocs []relocEnt + data bytes.Buffer + + encodingRelocHeader bool + + k reloc + idx int +} + +func (w *encoder) flush() int { + var sb bytes.Buffer // TODO(mdempsky): strings.Builder after #44505 is resolved + + // Backup the data so we write the relocations at the front. + var tmp bytes.Buffer + io.Copy(&tmp, &w.data) + + // TODO(mdempsky): Consider writing these out separately so they're + // easier to strip, along with function bodies, so that we can prune + // down to just the data that's relevant to go/types. + if w.encodingRelocHeader { + base.Fatalf("encodingRelocHeader already true; recursive flush?") + } + w.encodingRelocHeader = true + w.sync(syncRelocs) + w.len(len(w.relocs)) + for _, rent := range w.relocs { + w.sync(syncReloc) + w.len(int(rent.kind)) + w.len(rent.idx) + } + + io.Copy(&sb, &w.data) + io.Copy(&sb, &tmp) + w.p.elems[w.k][w.idx] = sb.String() + + return w.idx +} + +func (w *encoder) checkErr(err error) { + if err != nil { + base.Fatalf("unexpected error: %v", err) + } +} + +func (w *encoder) rawUvarint(x uint64) { + var buf [binary.MaxVarintLen64]byte + n := binary.PutUvarint(buf[:], x) + _, err := w.data.Write(buf[:n]) + w.checkErr(err) +} + +func (w *encoder) rawVarint(x int64) { + // Zig-zag encode. + ux := uint64(x) << 1 + if x < 0 { + ux = ^ux + } + + w.rawUvarint(ux) +} + +func (w *encoder) rawReloc(r reloc, idx int) int { + // TODO(mdempsky): Use map for lookup. + for i, rent := range w.relocs { + if rent.kind == r && rent.idx == idx { + return i + } + } + + i := len(w.relocs) + w.relocs = append(w.relocs, relocEnt{r, idx}) + return i +} + +func (w *encoder) sync(m syncMarker) { + if !enableSync { + return + } + + // Writing out stack frame string references requires working + // relocations, but writing out the relocations themselves involves + // sync markers. To prevent infinite recursion, we simply trim the + // stack frame for sync markers within the relocation header. + var frames []string + if !w.encodingRelocHeader && base.Debug.SyncFrames > 0 { + pcs := make([]uintptr, base.Debug.SyncFrames) + n := runtime.Callers(2, pcs) + frames = fmtFrames(pcs[:n]...) + } + + // TODO(mdempsky): Save space by writing out stack frames as a + // linked list so we can share common stack frames. + w.rawUvarint(uint64(m)) + w.rawUvarint(uint64(len(frames))) + for _, frame := range frames { + w.rawUvarint(uint64(w.rawReloc(relocString, w.p.stringIdx(frame)))) + } +} + +func (w *encoder) bool(b bool) bool { + w.sync(syncBool) + var x byte + if b { + x = 1 + } + err := w.data.WriteByte(x) + w.checkErr(err) + return b +} + +func (w *encoder) int64(x int64) { + w.sync(syncInt64) + w.rawVarint(x) +} + +func (w *encoder) uint64(x uint64) { + w.sync(syncUint64) + w.rawUvarint(x) +} + +func (w *encoder) len(x int) { assert(x >= 0); w.uint64(uint64(x)) } +func (w *encoder) int(x int) { w.int64(int64(x)) } +func (w *encoder) uint(x uint) { w.uint64(uint64(x)) } + +func (w *encoder) reloc(r reloc, idx int) { + w.sync(syncUseReloc) + w.len(w.rawReloc(r, idx)) +} + +func (w *encoder) code(c code) { + w.sync(c.marker()) + w.len(c.value()) +} + +func (w *encoder) string(s string) { + w.sync(syncString) + w.reloc(relocString, w.p.stringIdx(s)) +} + +func (w *encoder) strings(ss []string) { + w.len(len(ss)) + for _, s := range ss { + w.string(s) + } +} + +func (w *encoder) value(val constant.Value) { + w.sync(syncValue) + if w.bool(val.Kind() == constant.Complex) { + w.scalar(constant.Real(val)) + w.scalar(constant.Imag(val)) + } else { + w.scalar(val) + } +} + +func (w *encoder) scalar(val constant.Value) { + switch v := constant.Val(val).(type) { + default: + panic(fmt.Sprintf("unhandled %v (%v)", val, val.Kind())) + case bool: + w.code(valBool) + w.bool(v) + case string: + w.code(valString) + w.string(v) + case int64: + w.code(valInt64) + w.int64(v) + case *big.Int: + w.code(valBigInt) + w.bigInt(v) + case *big.Rat: + w.code(valBigRat) + w.bigInt(v.Num()) + w.bigInt(v.Denom()) + case *big.Float: + w.code(valBigFloat) + w.bigFloat(v) + } +} + +func (w *encoder) bigInt(v *big.Int) { + b := v.Bytes() + w.string(string(b)) // TODO: More efficient encoding. + w.bool(v.Sign() < 0) +} + +func (w *encoder) bigFloat(v *big.Float) { + b := v.Append(nil, 'p', -1) + w.string(string(b)) // TODO: More efficient encoding. +} diff --git a/src/cmd/compile/internal/noder/export.go b/src/cmd/compile/internal/noder/export.go new file mode 100644 index 0000000..1a296e2 --- /dev/null +++ b/src/cmd/compile/internal/noder/export.go @@ -0,0 +1,65 @@ +// Copyright 2021 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 noder + +import ( + "bytes" + "fmt" + "io" + + "cmd/compile/internal/base" + "cmd/compile/internal/typecheck" + "cmd/internal/bio" +) + +// writeNewExportFunc is a hook that can be added to append extra +// export data after the normal export data section. It allows +// experimenting with new export data format designs without requiring +// immediate support in the go/internal or x/tools importers. +var writeNewExportFunc func(out io.Writer) + +func WriteExports(out *bio.Writer) { + // When unified IR exports are enable, we simply append it to the + // end of the normal export data (with compiler extensions + // disabled), and write an extra header giving its size. + // + // If the compiler sees this header, it knows to read the new data + // instead; meanwhile the go/types importers will silently ignore it + // and continue processing the old export instead. + // + // This allows us to experiment with changes to the new export data + // format without needing to update the go/internal/gcimporter or + // (worse) x/tools/go/gcexportdata. + + useNewExport := writeNewExportFunc != nil + + var old, new bytes.Buffer + + typecheck.WriteExports(&old, !useNewExport) + + if useNewExport { + writeNewExportFunc(&new) + } + + oldLen := old.Len() + newLen := new.Len() + + if useNewExport { + fmt.Fprintf(out, "\nnewexportsize %v\n", newLen) + } + + // The linker also looks for the $$ marker - use char after $$ to distinguish format. + out.WriteString("\n$$B\n") // indicate binary export format + io.Copy(out, &old) + out.WriteString("\n$$\n") + io.Copy(out, &new) + + if base.Debug.Export != 0 { + fmt.Printf("BenchmarkExportSize:%s 1 %d bytes\n", base.Ctxt.Pkgpath, oldLen) + if useNewExport { + fmt.Printf("BenchmarkNewExportSize:%s 1 %d bytes\n", base.Ctxt.Pkgpath, newLen) + } + } +} diff --git a/src/cmd/compile/internal/noder/expr.go b/src/cmd/compile/internal/noder/expr.go new file mode 100644 index 0000000..4b5ae70 --- /dev/null +++ b/src/cmd/compile/internal/noder/expr.go @@ -0,0 +1,493 @@ +// Copyright 2021 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 noder + +import ( + "fmt" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/syntax" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/compile/internal/types2" + "cmd/internal/src" +) + +func (g *irgen) expr(expr syntax.Expr) ir.Node { + expr = unparen(expr) // skip parens; unneeded after parse+typecheck + + if expr == nil { + return nil + } + + if expr, ok := expr.(*syntax.Name); ok && expr.Value == "_" { + return ir.BlankNode + } + + tv, ok := g.info.Types[expr] + if !ok { + base.FatalfAt(g.pos(expr), "missing type for %v (%T)", expr, expr) + } + switch { + case tv.IsBuiltin(): + // Qualified builtins, such as unsafe.Add and unsafe.Slice. + if expr, ok := expr.(*syntax.SelectorExpr); ok { + if name, ok := expr.X.(*syntax.Name); ok { + if _, ok := g.info.Uses[name].(*types2.PkgName); ok { + return g.use(expr.Sel) + } + } + } + return g.use(expr.(*syntax.Name)) + case tv.IsType(): + return ir.TypeNode(g.typ(tv.Type)) + case tv.IsValue(), tv.IsVoid(): + // ok + default: + base.FatalfAt(g.pos(expr), "unrecognized type-checker result") + } + + base.Assert(g.exprStmtOK) + + // The gc backend expects all expressions to have a concrete type, and + // types2 mostly satisfies this expectation already. But there are a few + // cases where the Go spec doesn't require converting to concrete type, + // and so types2 leaves them untyped. So we need to fix those up here. + typ := tv.Type + if basic, ok := typ.(*types2.Basic); ok && basic.Info()&types2.IsUntyped != 0 { + switch basic.Kind() { + case types2.UntypedNil: + // ok; can appear in type switch case clauses + // TODO(mdempsky): Handle as part of type switches instead? + case types2.UntypedBool: + typ = types2.Typ[types2.Bool] // expression in "if" or "for" condition + case types2.UntypedString: + typ = types2.Typ[types2.String] // argument to "append" or "copy" calls + default: + base.FatalfAt(g.pos(expr), "unexpected untyped type: %v", basic) + } + } + + // Constant expression. + if tv.Value != nil { + typ := g.typ(typ) + value := FixValue(typ, tv.Value) + return OrigConst(g.pos(expr), typ, value, constExprOp(expr), syntax.String(expr)) + } + + n := g.expr0(typ, expr) + if n.Typecheck() != 1 && n.Typecheck() != 3 { + base.FatalfAt(g.pos(expr), "missed typecheck: %+v", n) + } + if n.Op() != ir.OFUNCINST && !g.match(n.Type(), typ, tv.HasOk()) { + base.FatalfAt(g.pos(expr), "expected %L to have type %v", n, typ) + } + return n +} + +func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node { + pos := g.pos(expr) + assert(pos.IsKnown()) + + // Set base.Pos for transformation code that still uses base.Pos, rather than + // the pos of the node being converted. + base.Pos = pos + + switch expr := expr.(type) { + case *syntax.Name: + if _, isNil := g.info.Uses[expr].(*types2.Nil); isNil { + return Nil(pos, g.typ(typ)) + } + return g.use(expr) + + case *syntax.CompositeLit: + return g.compLit(typ, expr) + + case *syntax.FuncLit: + return g.funcLit(typ, expr) + + case *syntax.AssertExpr: + return Assert(pos, g.expr(expr.X), g.typeExpr(expr.Type)) + + case *syntax.CallExpr: + fun := g.expr(expr.Fun) + return g.callExpr(pos, g.typ(typ), fun, g.exprs(expr.ArgList), expr.HasDots) + + case *syntax.IndexExpr: + args := unpackListExpr(expr.Index) + if len(args) == 1 { + tv, ok := g.info.Types[args[0]] + assert(ok) + if tv.IsValue() { + // This is just a normal index expression + n := Index(pos, g.typ(typ), g.expr(expr.X), g.expr(args[0])) + if !g.delayTransform() { + // transformIndex will modify n.Type() for OINDEXMAP. + transformIndex(n) + } + return n + } + } + + // expr.Index is a list of type args, so we ignore it, since types2 has + // already provided this info with the Info.Instances map. + return g.expr(expr.X) + + case *syntax.SelectorExpr: + // Qualified identifier. + if name, ok := expr.X.(*syntax.Name); ok { + if _, ok := g.info.Uses[name].(*types2.PkgName); ok { + return g.use(expr.Sel) + } + } + return g.selectorExpr(pos, typ, expr) + + case *syntax.SliceExpr: + n := Slice(pos, g.typ(typ), g.expr(expr.X), g.expr(expr.Index[0]), g.expr(expr.Index[1]), g.expr(expr.Index[2])) + if !g.delayTransform() { + transformSlice(n) + } + return n + + case *syntax.Operation: + if expr.Y == nil { + n := Unary(pos, g.typ(typ), g.op(expr.Op, unOps[:]), g.expr(expr.X)) + if n.Op() == ir.OADDR && !g.delayTransform() { + transformAddr(n.(*ir.AddrExpr)) + } + return n + } + switch op := g.op(expr.Op, binOps[:]); op { + case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE: + n := Compare(pos, g.typ(typ), op, g.expr(expr.X), g.expr(expr.Y)) + if !g.delayTransform() { + transformCompare(n) + } + return n + case ir.OANDAND, ir.OOROR: + x := g.expr(expr.X) + y := g.expr(expr.Y) + return typed(x.Type(), ir.NewLogicalExpr(pos, op, x, y)) + default: + n := Binary(pos, op, g.typ(typ), g.expr(expr.X), g.expr(expr.Y)) + if op == ir.OADD && !g.delayTransform() { + return transformAdd(n) + } + return n + } + + default: + g.unhandled("expression", expr) + panic("unreachable") + } +} + +// substType does a normal type substition, but tparams is in the form of a field +// list, and targs is in terms of a slice of type nodes. substType records any newly +// instantiated types into g.instTypeList. +func (g *irgen) substType(typ *types.Type, tparams *types.Type, targs []ir.Node) *types.Type { + fields := tparams.FieldSlice() + tparams1 := make([]*types.Type, len(fields)) + for i, f := range fields { + tparams1[i] = f.Type + } + targs1 := make([]*types.Type, len(targs)) + for i, n := range targs { + targs1[i] = n.Type() + } + ts := typecheck.Tsubster{ + Tparams: tparams1, + Targs: targs1, + } + newt := ts.Typ(typ) + return newt +} + +// callExpr creates a call expression (which might be a type conversion, built-in +// call, or a regular call) and does standard transforms, unless we are in a generic +// function. +func (g *irgen) callExpr(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool) ir.Node { + n := ir.NewCallExpr(pos, ir.OCALL, fun, args) + n.IsDDD = dots + typed(typ, n) + + if fun.Op() == ir.OTYPE { + // Actually a type conversion, not a function call. + if !g.delayTransform() { + return transformConvCall(n) + } + return n + } + + if fun, ok := fun.(*ir.Name); ok && fun.BuiltinOp != 0 { + if !g.delayTransform() { + return transformBuiltin(n) + } + return n + } + + // Add information, now that we know that fun is actually being called. + switch fun := fun.(type) { + case *ir.SelectorExpr: + if fun.Op() == ir.OMETHVALUE { + op := ir.ODOTMETH + if fun.X.Type().IsInterface() { + op = ir.ODOTINTER + } + fun.SetOp(op) + // Set the type to include the receiver, since that's what + // later parts of the compiler expect + fun.SetType(fun.Selection.Type) + } + } + + // A function instantiation (even if fully concrete) shouldn't be + // transformed yet, because we need to add the dictionary during the + // transformation. + if fun.Op() != ir.OFUNCINST && !g.delayTransform() { + transformCall(n) + } + return n +} + +// selectorExpr resolves the choice of ODOT, ODOTPTR, OMETHVALUE (eventually +// ODOTMETH & ODOTINTER), and OMETHEXPR and deals with embedded fields here rather +// than in typecheck.go. +func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.SelectorExpr) ir.Node { + x := g.expr(expr.X) + if x.Type().HasTParam() { + // Leave a method call on a type param as an OXDOT, since it can + // only be fully transformed once it has an instantiated type. + n := ir.NewSelectorExpr(pos, ir.OXDOT, x, typecheck.Lookup(expr.Sel.Value)) + typed(g.typ(typ), n) + return n + } + + selinfo := g.info.Selections[expr] + // Everything up to the last selection is an implicit embedded field access, + // and the last selection is determined by selinfo.Kind(). + index := selinfo.Index() + embeds, last := index[:len(index)-1], index[len(index)-1] + + origx := x + for _, ix := range embeds { + x = Implicit(DotField(pos, x, ix)) + } + + kind := selinfo.Kind() + if kind == types2.FieldVal { + return DotField(pos, x, last) + } + + var n ir.Node + method2 := selinfo.Obj().(*types2.Func) + + if kind == types2.MethodExpr { + // OMETHEXPR is unusual in using directly the node and type of the + // original OTYPE node (origx) before passing through embedded + // fields, even though the method is selected from the type + // (x.Type()) reached after following the embedded fields. We will + // actually drop any ODOT nodes we created due to the embedded + // fields. + n = MethodExpr(pos, origx, x.Type(), last) + } else { + // Add implicit addr/deref for method values, if needed. + if x.Type().IsInterface() { + n = DotMethod(pos, x, last) + } else { + recvType2 := method2.Type().(*types2.Signature).Recv().Type() + _, wantPtr := recvType2.(*types2.Pointer) + havePtr := x.Type().IsPtr() + + if havePtr != wantPtr { + if havePtr { + x = Implicit(Deref(pos, x.Type().Elem(), x)) + } else { + x = Implicit(Addr(pos, x)) + } + } + recvType2Base := recvType2 + if wantPtr { + recvType2Base = types2.AsPointer(recvType2).Elem() + } + if recvType2Base.(*types2.Named).TypeParams().Len() > 0 { + // recvType2 is the original generic type that is + // instantiated for this method call. + // selinfo.Recv() is the instantiated type + recvType2 = recvType2Base + recvTypeSym := g.pkg(method2.Pkg()).Lookup(recvType2.(*types2.Named).Obj().Name()) + recvType := recvTypeSym.Def.(*ir.Name).Type() + // method is the generic method associated with + // the base generic type. The instantiated type may not + // have method bodies filled in, if it was imported. + method := recvType.Methods().Index(last).Nname.(*ir.Name) + n = ir.NewSelectorExpr(pos, ir.OMETHVALUE, x, typecheck.Lookup(expr.Sel.Value)) + n.(*ir.SelectorExpr).Selection = types.NewField(pos, method.Sym(), method.Type()) + n.(*ir.SelectorExpr).Selection.Nname = method + typed(method.Type(), n) + + xt := deref(x.Type()) + targs := make([]ir.Node, len(xt.RParams())) + for i := range targs { + targs[i] = ir.TypeNode(xt.RParams()[i]) + } + + // Create function instantiation with the type + // args for the receiver type for the method call. + n = ir.NewInstExpr(pos, ir.OFUNCINST, n, targs) + typed(g.typ(typ), n) + return n + } + + if !g.match(x.Type(), recvType2, false) { + base.FatalfAt(pos, "expected %L to have type %v", x, recvType2) + } else { + n = DotMethod(pos, x, last) + } + } + } + if have, want := n.Sym(), g.selector(method2); have != want { + base.FatalfAt(pos, "bad Sym: have %v, want %v", have, want) + } + return n +} + +func (g *irgen) exprList(expr syntax.Expr) []ir.Node { + return g.exprs(unpackListExpr(expr)) +} + +func unpackListExpr(expr syntax.Expr) []syntax.Expr { + switch expr := expr.(type) { + case nil: + return nil + case *syntax.ListExpr: + return expr.ElemList + default: + return []syntax.Expr{expr} + } +} + +func (g *irgen) exprs(exprs []syntax.Expr) []ir.Node { + nodes := make([]ir.Node, len(exprs)) + for i, expr := range exprs { + nodes[i] = g.expr(expr) + } + return nodes +} + +func (g *irgen) compLit(typ types2.Type, lit *syntax.CompositeLit) ir.Node { + if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok { + n := ir.NewAddrExpr(g.pos(lit), g.compLit(ptr.Elem(), lit)) + n.SetOp(ir.OPTRLIT) + return typed(g.typ(typ), n) + } + + _, isStruct := types2.CoreType(typ).(*types2.Struct) + + exprs := make([]ir.Node, len(lit.ElemList)) + for i, elem := range lit.ElemList { + switch elem := elem.(type) { + case *syntax.KeyValueExpr: + var key ir.Node + if isStruct { + key = ir.NewIdent(g.pos(elem.Key), g.name(elem.Key.(*syntax.Name))) + } else { + key = g.expr(elem.Key) + } + value := wrapname(g.pos(elem.Value), g.expr(elem.Value)) + if value.Op() == ir.OPAREN { + // Make sure any PAREN node added by wrapper has a type + typed(value.(*ir.ParenExpr).X.Type(), value) + } + exprs[i] = ir.NewKeyExpr(g.pos(elem), key, value) + default: + exprs[i] = wrapname(g.pos(elem), g.expr(elem)) + if exprs[i].Op() == ir.OPAREN { + // Make sure any PAREN node added by wrapper has a type + typed(exprs[i].(*ir.ParenExpr).X.Type(), exprs[i]) + } + } + } + + n := ir.NewCompLitExpr(g.pos(lit), ir.OCOMPLIT, nil, exprs) + typed(g.typ(typ), n) + var r ir.Node = n + if !g.delayTransform() { + r = transformCompLit(n) + } + return r +} + +func (g *irgen) funcLit(typ2 types2.Type, expr *syntax.FuncLit) ir.Node { + fn := ir.NewClosureFunc(g.pos(expr), ir.CurFunc != nil) + ir.NameClosure(fn.OClosure, ir.CurFunc) + + typ := g.typ(typ2) + typed(typ, fn.Nname) + typed(typ, fn.OClosure) + fn.SetTypecheck(1) + + g.funcBody(fn, nil, expr.Type, expr.Body) + + ir.FinishCaptureNames(fn.Pos(), ir.CurFunc, fn) + + // TODO(mdempsky): ir.CaptureName should probably handle + // copying these fields from the canonical variable. + for _, cv := range fn.ClosureVars { + cv.SetType(cv.Canonical().Type()) + cv.SetTypecheck(1) + cv.SetWalkdef(1) + } + + if g.topFuncIsGeneric { + // Don't add any closure inside a generic function/method to the + // g.target.Decls list, even though it may not be generic itself. + // See issue #47514. + return ir.UseClosure(fn.OClosure, nil) + } else { + return ir.UseClosure(fn.OClosure, g.target) + } +} + +func (g *irgen) typeExpr(typ syntax.Expr) *types.Type { + n := g.expr(typ) + if n.Op() != ir.OTYPE { + base.FatalfAt(g.pos(typ), "expected type: %L", n) + } + return n.Type() +} + +// constExprOp returns an ir.Op that represents the outermost +// operation of the given constant expression. It's intended for use +// with ir.RawOrigExpr. +func constExprOp(expr syntax.Expr) ir.Op { + switch expr := expr.(type) { + default: + panic(fmt.Sprintf("%s: unexpected expression: %T", expr.Pos(), expr)) + + case *syntax.BasicLit: + return ir.OLITERAL + case *syntax.Name, *syntax.SelectorExpr: + return ir.ONAME + case *syntax.CallExpr: + return ir.OCALL + case *syntax.Operation: + if expr.Y == nil { + return unOps[expr.Op] + } + return binOps[expr.Op] + } +} + +func unparen(expr syntax.Expr) syntax.Expr { + for { + paren, ok := expr.(*syntax.ParenExpr) + if !ok { + return expr + } + expr = paren.X + } +} diff --git a/src/cmd/compile/internal/noder/frames_go1.go b/src/cmd/compile/internal/noder/frames_go1.go new file mode 100644 index 0000000..d00e0f5 --- /dev/null +++ b/src/cmd/compile/internal/noder/frames_go1.go @@ -0,0 +1,21 @@ +// Copyright 2021 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. + +//go:build !go1.7 +// +build !go1.7 + +// TODO(mdempsky): Remove after #44505 is resolved + +package noder + +import "runtime" + +func walkFrames(pcs []uintptr, visit frameVisitor) { + for _, pc := range pcs { + fn := runtime.FuncForPC(pc) + file, line := fn.FileLine(pc) + + visit(file, line, fn.Name(), pc-fn.Entry()) + } +} diff --git a/src/cmd/compile/internal/noder/frames_go17.go b/src/cmd/compile/internal/noder/frames_go17.go new file mode 100644 index 0000000..48d7762 --- /dev/null +++ b/src/cmd/compile/internal/noder/frames_go17.go @@ -0,0 +1,25 @@ +// Copyright 2021 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. + +//go:build go1.7 +// +build go1.7 + +package noder + +import "runtime" + +func walkFrames(pcs []uintptr, visit frameVisitor) { + if len(pcs) == 0 { + return + } + + frames := runtime.CallersFrames(pcs) + for { + frame, more := frames.Next() + visit(frame.File, frame.Line, frame.Function, frame.PC-frame.Entry) + if !more { + return + } + } +} diff --git a/src/cmd/compile/internal/noder/func.go b/src/cmd/compile/internal/noder/func.go new file mode 100644 index 0000000..6077b34 --- /dev/null +++ b/src/cmd/compile/internal/noder/func.go @@ -0,0 +1,73 @@ +// Copyright 2021 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 noder + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/syntax" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/internal/src" +) + +func (g *irgen) funcBody(fn *ir.Func, recv *syntax.Field, sig *syntax.FuncType, block *syntax.BlockStmt) { + typecheck.Func(fn) + + // TODO(mdempsky): Remove uses of ir.CurFunc and + // typecheck.DeclContext after we stop relying on typecheck + // for desugaring. + outerfn, outerctxt := ir.CurFunc, typecheck.DeclContext + ir.CurFunc = fn + + typ := fn.Type() + if param := typ.Recv(); param != nil { + g.defParam(param, recv, ir.PPARAM) + } + for i, param := range typ.Params().FieldSlice() { + g.defParam(param, sig.ParamList[i], ir.PPARAM) + } + for i, result := range typ.Results().FieldSlice() { + g.defParam(result, sig.ResultList[i], ir.PPARAMOUT) + } + + // We may have type-checked a call to this function already and + // calculated its size, including parameter offsets. Now that we've + // created the parameter Names, force a recalculation to ensure + // their offsets are correct. + types.RecalcSize(typ) + + if block != nil { + typecheck.DeclContext = ir.PAUTO + + fn.Body = g.stmts(block.List) + if fn.Body == nil { + fn.Body = []ir.Node{ir.NewBlockStmt(src.NoXPos, nil)} + } + fn.Endlineno = g.makeXPos(block.Rbrace) + + if base.Flag.Dwarf { + g.recordScopes(fn, sig) + } + } + + ir.CurFunc, typecheck.DeclContext = outerfn, outerctxt +} + +func (g *irgen) defParam(param *types.Field, decl *syntax.Field, class ir.Class) { + typecheck.DeclContext = class + + var name *ir.Name + if decl.Name != nil { + name, _ = g.def(decl.Name) + } else if class == ir.PPARAMOUT { + name = g.obj(g.info.Implicits[decl]) + } + + if name != nil { + param.Nname = name + param.Sym = name.Sym() // in case it was renamed + } +} diff --git a/src/cmd/compile/internal/noder/helpers.go b/src/cmd/compile/internal/noder/helpers.go new file mode 100644 index 0000000..33acd60 --- /dev/null +++ b/src/cmd/compile/internal/noder/helpers.go @@ -0,0 +1,226 @@ +// Copyright 2021 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 noder + +import ( + "go/constant" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/internal/src" +) + +// Helpers for constructing typed IR nodes. +// +// TODO(mdempsky): Move into their own package so they can be easily +// reused by iimport and frontend optimizations. + +type ImplicitNode interface { + ir.Node + SetImplicit(x bool) +} + +// Implicit returns n after marking it as Implicit. +func Implicit(n ImplicitNode) ImplicitNode { + n.SetImplicit(true) + return n +} + +// typed returns n after setting its type to typ. +func typed(typ *types.Type, n ir.Node) ir.Node { + n.SetType(typ) + n.SetTypecheck(1) + return n +} + +// Values + +func Const(pos src.XPos, typ *types.Type, val constant.Value) ir.Node { + return typed(typ, ir.NewBasicLit(pos, val)) +} + +func OrigConst(pos src.XPos, typ *types.Type, val constant.Value, op ir.Op, raw string) ir.Node { + orig := ir.NewRawOrigExpr(pos, op, raw) + return ir.NewConstExpr(val, typed(typ, orig)) +} + +// FixValue returns val after converting and truncating it as +// appropriate for typ. +func FixValue(typ *types.Type, val constant.Value) constant.Value { + assert(typ.Kind() != types.TFORW) + switch { + case typ.IsInteger(): + val = constant.ToInt(val) + case typ.IsFloat(): + val = constant.ToFloat(val) + case typ.IsComplex(): + val = constant.ToComplex(val) + } + if !typ.IsUntyped() { + val = typecheck.DefaultLit(ir.NewBasicLit(src.NoXPos, val), typ).Val() + } + if !typ.IsTypeParam() { + ir.AssertValidTypeForConst(typ, val) + } + return val +} + +func Nil(pos src.XPos, typ *types.Type) ir.Node { + return typed(typ, ir.NewNilExpr(pos)) +} + +// Expressions + +func Addr(pos src.XPos, x ir.Node) *ir.AddrExpr { + n := typecheck.NodAddrAt(pos, x) + typed(types.NewPtr(x.Type()), n) + return n +} + +func Assert(pos src.XPos, x ir.Node, typ *types.Type) ir.Node { + return typed(typ, ir.NewTypeAssertExpr(pos, x, nil)) +} + +func Binary(pos src.XPos, op ir.Op, typ *types.Type, x, y ir.Node) *ir.BinaryExpr { + switch op { + case ir.OADD: + n := ir.NewBinaryExpr(pos, op, x, y) + typed(typ, n) + return n + default: + n := ir.NewBinaryExpr(pos, op, x, y) + typed(x.Type(), n) + return n + } +} + +func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) *ir.BinaryExpr { + n := ir.NewBinaryExpr(pos, op, x, y) + typed(typ, n) + return n +} + +func Deref(pos src.XPos, typ *types.Type, x ir.Node) *ir.StarExpr { + n := ir.NewStarExpr(pos, x) + typed(typ, n) + return n +} + +func DotField(pos src.XPos, x ir.Node, index int) *ir.SelectorExpr { + op, typ := ir.ODOT, x.Type() + if typ.IsPtr() { + op, typ = ir.ODOTPTR, typ.Elem() + } + if !typ.IsStruct() { + base.FatalfAt(pos, "DotField of non-struct: %L", x) + } + + // TODO(mdempsky): This is the backend's responsibility. + types.CalcSize(typ) + + field := typ.Field(index) + return dot(pos, field.Type, op, x, field) +} + +func DotMethod(pos src.XPos, x ir.Node, index int) *ir.SelectorExpr { + method := method(x.Type(), index) + + // Method value. + typ := typecheck.NewMethodType(method.Type, nil) + return dot(pos, typ, ir.OMETHVALUE, x, method) +} + +// MethodExpr returns a OMETHEXPR node with the indicated index into the methods +// of typ. The receiver type is set from recv, which is different from typ if the +// method was accessed via embedded fields. Similarly, the X value of the +// ir.SelectorExpr is recv, the original OTYPE node before passing through the +// embedded fields. +func MethodExpr(pos src.XPos, recv ir.Node, embed *types.Type, index int) *ir.SelectorExpr { + method := method(embed, index) + typ := typecheck.NewMethodType(method.Type, recv.Type()) + // The method expression T.m requires a wrapper when T + // is different from m's declared receiver type. We + // normally generate these wrappers while writing out + // runtime type descriptors, which is always done for + // types declared at package scope. However, we need + // to make sure to generate wrappers for anonymous + // receiver types too. + if recv.Sym() == nil { + typecheck.NeedRuntimeType(recv.Type()) + } + return dot(pos, typ, ir.OMETHEXPR, recv, method) +} + +func dot(pos src.XPos, typ *types.Type, op ir.Op, x ir.Node, selection *types.Field) *ir.SelectorExpr { + n := ir.NewSelectorExpr(pos, op, x, selection.Sym) + n.Selection = selection + typed(typ, n) + return n +} + +// TODO(mdempsky): Move to package types. +func method(typ *types.Type, index int) *types.Field { + if typ.IsInterface() { + return typ.AllMethods().Index(index) + } + return types.ReceiverBaseType(typ).Methods().Index(index) +} + +func Index(pos src.XPos, typ *types.Type, x, index ir.Node) *ir.IndexExpr { + n := ir.NewIndexExpr(pos, x, index) + typed(typ, n) + return n +} + +func Slice(pos src.XPos, typ *types.Type, x, low, high, max ir.Node) *ir.SliceExpr { + op := ir.OSLICE + if max != nil { + op = ir.OSLICE3 + } + n := ir.NewSliceExpr(pos, op, x, low, high, max) + typed(typ, n) + return n +} + +func Unary(pos src.XPos, typ *types.Type, op ir.Op, x ir.Node) ir.Node { + switch op { + case ir.OADDR: + return Addr(pos, x) + case ir.ODEREF: + return Deref(pos, typ, x) + } + + if op == ir.ORECV { + if typ.IsFuncArgStruct() && typ.NumFields() == 2 { + // Remove the second boolean type (if provided by type2), + // since that works better with the rest of the compiler + // (which will add it back in later). + assert(typ.Field(1).Type.Kind() == types.TBOOL) + typ = typ.Field(0).Type + } + } + return typed(typ, ir.NewUnaryExpr(pos, op, x)) +} + +// Statements + +var one = constant.MakeInt64(1) + +func IncDec(pos src.XPos, op ir.Op, x ir.Node) *ir.AssignOpStmt { + assert(x.Type() != nil) + bl := ir.NewBasicLit(pos, one) + if x.Type().HasTParam() { + // If the operand is generic, then types2 will have proved it must be + // a type that fits with increment/decrement, so just set the type of + // "one" to n.Type(). This works even for types that are eventually + // float or complex. + typed(x.Type(), bl) + } else { + bl = typecheck.DefaultLit(bl, x.Type()) + } + return ir.NewAssignOpStmt(pos, op, x, bl) +} diff --git a/src/cmd/compile/internal/noder/import.go b/src/cmd/compile/internal/noder/import.go new file mode 100644 index 0000000..58dffba --- /dev/null +++ b/src/cmd/compile/internal/noder/import.go @@ -0,0 +1,601 @@ +// 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 noder + +import ( + "errors" + "fmt" + "internal/buildcfg" + "os" + pathpkg "path" + "runtime" + "sort" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "cmd/compile/internal/base" + "cmd/compile/internal/importer" + "cmd/compile/internal/ir" + "cmd/compile/internal/syntax" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/compile/internal/types2" + "cmd/internal/archive" + "cmd/internal/bio" + "cmd/internal/goobj" + "cmd/internal/objabi" + "cmd/internal/src" +) + +// haveLegacyImports records whether we've imported any packages +// without a new export data section. This is useful for experimenting +// with new export data format designs, when you need to support +// existing tests that manually compile files with inconsistent +// compiler flags. +var haveLegacyImports = false + +// newReadImportFunc is an extension hook for experimenting with new +// export data formats. If a new export data payload was written out +// for an imported package by overloading writeNewExportFunc, then +// that payload will be mapped into memory and passed to +// newReadImportFunc. +var newReadImportFunc = func(data string, pkg1 *types.Pkg, env *types2.Context, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) { + panic("unexpected new export data payload") +} + +type gcimports struct { + ctxt *types2.Context + packages map[string]*types2.Package +} + +func (m *gcimports) Import(path string) (*types2.Package, error) { + return m.ImportFrom(path, "" /* no vendoring */, 0) +} + +func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*types2.Package, error) { + if mode != 0 { + panic("mode must be 0") + } + + _, pkg, err := readImportFile(path, typecheck.Target, m.ctxt, m.packages) + return pkg, err +} + +func isDriveLetter(b byte) bool { + return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z' +} + +// is this path a local name? begins with ./ or ../ or / +func islocalname(name string) bool { + return strings.HasPrefix(name, "/") || + runtime.GOOS == "windows" && len(name) >= 3 && isDriveLetter(name[0]) && name[1] == ':' && name[2] == '/' || + strings.HasPrefix(name, "./") || name == "." || + strings.HasPrefix(name, "../") || name == ".." +} + +func openPackage(path string) (*os.File, error) { + if islocalname(path) { + if base.Flag.NoLocalImports { + return nil, errors.New("local imports disallowed") + } + + if base.Flag.Cfg.PackageFile != nil { + return os.Open(base.Flag.Cfg.PackageFile[path]) + } + + // try .a before .o. important for building libraries: + // if there is an array.o in the array.a library, + // want to find all of array.a, not just array.o. + if file, err := os.Open(fmt.Sprintf("%s.a", path)); err == nil { + return file, nil + } + if file, err := os.Open(fmt.Sprintf("%s.o", path)); err == nil { + return file, nil + } + return nil, errors.New("file not found") + } + + // local imports should be canonicalized already. + // don't want to see "encoding/../encoding/base64" + // as different from "encoding/base64". + if q := pathpkg.Clean(path); q != path { + return nil, fmt.Errorf("non-canonical import path %q (should be %q)", path, q) + } + + if base.Flag.Cfg.PackageFile != nil { + return os.Open(base.Flag.Cfg.PackageFile[path]) + } + + for _, dir := range base.Flag.Cfg.ImportDirs { + if file, err := os.Open(fmt.Sprintf("%s/%s.a", dir, path)); err == nil { + return file, nil + } + if file, err := os.Open(fmt.Sprintf("%s/%s.o", dir, path)); err == nil { + return file, nil + } + } + + if buildcfg.GOROOT != "" { + suffix := "" + if base.Flag.InstallSuffix != "" { + suffix = "_" + base.Flag.InstallSuffix + } else if base.Flag.Race { + suffix = "_race" + } else if base.Flag.MSan { + suffix = "_msan" + } else if base.Flag.ASan { + suffix = "_asan" + } + + if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.a", buildcfg.GOROOT, buildcfg.GOOS, buildcfg.GOARCH, suffix, path)); err == nil { + return file, nil + } + if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.o", buildcfg.GOROOT, buildcfg.GOOS, buildcfg.GOARCH, suffix, path)); err == nil { + return file, nil + } + } + return nil, errors.New("file not found") +} + +// myheight tracks the local package's height based on packages +// imported so far. +var myheight int + +// resolveImportPath resolves an import path as it appears in a Go +// source file to the package's full path. +func resolveImportPath(path string) (string, error) { + // The package name main is no longer reserved, + // but we reserve the import path "main" to identify + // the main package, just as we reserve the import + // path "math" to identify the standard math package. + if path == "main" { + return "", errors.New("cannot import \"main\"") + } + + if base.Ctxt.Pkgpath != "" && path == base.Ctxt.Pkgpath { + return "", fmt.Errorf("import %q while compiling that package (import cycle)", path) + } + + if mapped, ok := base.Flag.Cfg.ImportMap[path]; ok { + path = mapped + } + + if islocalname(path) { + if path[0] == '/' { + return "", errors.New("import path cannot be absolute path") + } + + prefix := base.Flag.D + if prefix == "" { + // Questionable, but when -D isn't specified, historically we + // resolve local import paths relative to the directory the + // compiler's current directory, not the respective source + // file's directory. + prefix = base.Ctxt.Pathname + } + path = pathpkg.Join(prefix, path) + + if err := checkImportPath(path, true); err != nil { + return "", err + } + } + + return path, nil +} + +func importfile(decl *syntax.ImportDecl) *types.Pkg { + path, err := parseImportPath(decl.Path) + if err != nil { + base.Errorf("%s", err) + return nil + } + + pkg, _, err := readImportFile(path, typecheck.Target, nil, nil) + if err != nil { + base.Errorf("%s", err) + return nil + } + + if pkg != types.UnsafePkg && pkg.Height >= myheight { + myheight = pkg.Height + 1 + } + return pkg +} + +func parseImportPath(pathLit *syntax.BasicLit) (string, error) { + if pathLit.Kind != syntax.StringLit { + return "", errors.New("import path must be a string") + } + + path, err := strconv.Unquote(pathLit.Value) + if err != nil { + return "", errors.New("import path must be a string") + } + + if err := checkImportPath(path, false); err != nil { + return "", err + } + + return path, err +} + +// readImportFile reads the import file for the given package path and +// returns its types.Pkg representation. If packages is non-nil, the +// types2.Package representation is also returned. +func readImportFile(path string, target *ir.Package, env *types2.Context, packages map[string]*types2.Package) (pkg1 *types.Pkg, pkg2 *types2.Package, err error) { + path, err = resolveImportPath(path) + if err != nil { + return + } + + if path == "unsafe" { + pkg1, pkg2 = types.UnsafePkg, types2.Unsafe + + // TODO(mdempsky): Investigate if this actually matters. Why would + // the linker or runtime care whether a package imported unsafe? + if !pkg1.Direct { + pkg1.Direct = true + target.Imports = append(target.Imports, pkg1) + } + + return + } + + pkg1 = types.NewPkg(path, "") + if packages != nil { + pkg2 = packages[path] + assert(pkg1.Direct == (pkg2 != nil && pkg2.Complete())) + } + + if pkg1.Direct { + return + } + pkg1.Direct = true + target.Imports = append(target.Imports, pkg1) + + f, err := openPackage(path) + if err != nil { + return + } + defer f.Close() + + r, end, newsize, err := findExportData(f) + if err != nil { + return + } + + if base.Debug.Export != 0 { + fmt.Printf("importing %s (%s)\n", path, f.Name()) + } + + if newsize != 0 { + // We have unified IR data. Map it, and feed to the importers. + end -= newsize + var data string + data, err = base.MapFile(r.File(), end, newsize) + if err != nil { + return + } + + pkg2, err = newReadImportFunc(data, pkg1, env, packages) + } else { + // We only have old data. Oh well, fall back to the legacy importers. + haveLegacyImports = true + + var c byte + switch c, err = r.ReadByte(); { + case err != nil: + return + + case c != 'i': + // Indexed format is distinguished by an 'i' byte, + // whereas previous export formats started with 'c', 'd', or 'v'. + err = fmt.Errorf("unexpected package format byte: %v", c) + return + } + + pos := r.Offset() + + // Map string (and data) section into memory as a single large + // string. This reduces heap fragmentation and allows + // returning individual substrings very efficiently. + var data string + data, err = base.MapFile(r.File(), pos, end-pos) + if err != nil { + return + } + + typecheck.ReadImports(pkg1, data) + + if packages != nil { + pkg2, err = importer.ImportData(packages, data, path) + if err != nil { + return + } + } + } + + err = addFingerprint(path, f, end) + return +} + +// findExportData returns a *bio.Reader positioned at the start of the +// binary export data section, and a file offset for where to stop +// reading. +func findExportData(f *os.File) (r *bio.Reader, end, newsize int64, err error) { + r = bio.NewReader(f) + + // check object header + line, err := r.ReadString('\n') + if err != nil { + return + } + + if line == "!<arch>\n" { // package archive + // package export block should be first + sz := int64(archive.ReadHeader(r.Reader, "__.PKGDEF")) + if sz <= 0 { + err = errors.New("not a package file") + return + } + end = r.Offset() + sz + line, err = r.ReadString('\n') + if err != nil { + return + } + } else { + // Not an archive; provide end of file instead. + // TODO(mdempsky): I don't think this happens anymore. + var fi os.FileInfo + fi, err = f.Stat() + if err != nil { + return + } + end = fi.Size() + } + + if !strings.HasPrefix(line, "go object ") { + err = fmt.Errorf("not a go object file: %s", line) + return + } + if expect := objabi.HeaderString(); line != expect { + err = fmt.Errorf("object is [%s] expected [%s]", line, expect) + return + } + + // process header lines + for !strings.HasPrefix(line, "$$") { + if strings.HasPrefix(line, "newexportsize ") { + fields := strings.Fields(line) + newsize, err = strconv.ParseInt(fields[1], 10, 64) + if err != nil { + return + } + } + + line, err = r.ReadString('\n') + if err != nil { + return + } + } + + // Expect $$B\n to signal binary import format. + if line != "$$B\n" { + err = errors.New("old export format no longer supported (recompile library)") + return + } + + return +} + +// addFingerprint reads the linker fingerprint included at the end of +// the exportdata. +func addFingerprint(path string, f *os.File, end int64) error { + const eom = "\n$$\n" + var fingerprint goobj.FingerprintType + + var buf [len(fingerprint) + len(eom)]byte + if _, err := f.ReadAt(buf[:], end-int64(len(buf))); err != nil { + return err + } + + // Caller should have given us the end position of the export data, + // which should end with the "\n$$\n" marker. As a consistency check + // to make sure we're reading at the right offset, make sure we + // found the marker. + if s := string(buf[len(fingerprint):]); s != eom { + return fmt.Errorf("expected $$ marker, but found %q", s) + } + + copy(fingerprint[:], buf[:]) + + // assume files move (get installed) so don't record the full path + if base.Flag.Cfg.PackageFile != nil { + // If using a packageFile map, assume path_ can be recorded directly. + base.Ctxt.AddImport(path, fingerprint) + } else { + // For file "/Users/foo/go/pkg/darwin_amd64/math.a" record "math.a". + file := f.Name() + base.Ctxt.AddImport(file[len(file)-len(path)-len(".a"):], fingerprint) + } + return nil +} + +// 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 checkImportPath(path string, allowSpace bool) error { + if path == "" { + return errors.New("import path is empty") + } + + if strings.Contains(path, "\x00") { + return errors.New("import path contains NUL") + } + + for _, ri := range reservedimports { + if path == ri { + return fmt.Errorf("import path %q is reserved and cannot be used", path) + } + } + + for _, r := range path { + switch { + case r == utf8.RuneError: + return fmt.Errorf("import path contains invalid UTF-8 sequence: %q", path) + case r < 0x20 || r == 0x7f: + return fmt.Errorf("import path contains control character: %q", path) + case r == '\\': + return fmt.Errorf("import path contains backslash; use slash: %q", path) + case !allowSpace && unicode.IsSpace(r): + return fmt.Errorf("import path contains space character: %q", path) + case strings.ContainsRune("!\"#$%&'()*,:;<=>?[]^`{|}", r): + return fmt.Errorf("import path contains invalid character '%c': %q", r, path) + } + } + + return nil +} + +func pkgnotused(lineno src.XPos, path string, name string) { + // If the package was imported with a name other than the final + // import path element, show it explicitly in the error message. + // Note that this handles both renamed imports and imports of + // packages containing unconventional package declarations. + // Note that this uses / always, even on Windows, because Go import + // paths always use forward slashes. + elem := path + if i := strings.LastIndex(elem, "/"); i >= 0 { + elem = elem[i+1:] + } + if name == "" || elem == name { + base.ErrorfAt(lineno, "imported and not used: %q", path) + } else { + base.ErrorfAt(lineno, "imported and not used: %q as %s", path, name) + } +} + +func mkpackage(pkgname string) { + if types.LocalPkg.Name == "" { + if pkgname == "_" { + base.Errorf("invalid package name _") + } + types.LocalPkg.Name = pkgname + } else { + if pkgname != types.LocalPkg.Name { + base.Errorf("package %s; expected %s", pkgname, types.LocalPkg.Name) + } + } +} + +func clearImports() { + type importedPkg struct { + pos src.XPos + path string + name string + } + var unused []importedPkg + + for _, s := range types.LocalPkg.Syms { + n := ir.AsNode(s.Def) + if n == nil { + continue + } + if n.Op() == ir.OPACK { + // throw away top-level package name left over + // from previous file. + // leave s->block set to cause redeclaration + // errors if a conflicting top-level name is + // introduced by a different file. + p := n.(*ir.PkgName) + if !p.Used && base.SyntaxErrors() == 0 { + unused = append(unused, importedPkg{p.Pos(), p.Pkg.Path, s.Name}) + } + s.Def = nil + continue + } + if s.Def != nil && s.Def.Sym() != s { + // throw away top-level name left over + // from previous import . "x" + // We'll report errors after type checking in CheckDotImports. + s.Def = nil + continue + } + } + + sort.Slice(unused, func(i, j int) bool { return unused[i].pos.Before(unused[j].pos) }) + for _, pkg := range unused { + pkgnotused(pkg.pos, pkg.path, pkg.name) + } +} + +// CheckDotImports reports errors for any unused dot imports. +func CheckDotImports() { + for _, pack := range dotImports { + if !pack.Used { + base.ErrorfAt(pack.Pos(), "imported and not used: %q", pack.Pkg.Path) + } + } + + // No longer needed; release memory. + dotImports = nil + typecheck.DotImportRefs = nil +} + +// dotImports tracks all PkgNames that have been dot-imported. +var dotImports []*ir.PkgName + +// find all the exported symbols in package referenced by PkgName, +// and make them available in the current package +func importDot(pack *ir.PkgName) { + if typecheck.DotImportRefs == nil { + typecheck.DotImportRefs = make(map[*ir.Ident]*ir.PkgName) + } + + opkg := pack.Pkg + for _, s := range opkg.Syms { + if s.Def == nil { + if _, ok := typecheck.DeclImporter[s]; !ok { + continue + } + } + if !types.IsExported(s.Name) || strings.ContainsRune(s.Name, 0xb7) { // 0xb7 = center dot + continue + } + s1 := typecheck.Lookup(s.Name) + if s1.Def != nil { + pkgerror := fmt.Sprintf("during import %q", opkg.Path) + typecheck.Redeclared(base.Pos, s1, pkgerror) + continue + } + + id := ir.NewIdent(src.NoXPos, s) + typecheck.DotImportRefs[id] = pack + s1.Def = id + s1.Block = 1 + } + + dotImports = append(dotImports, pack) +} + +// importName is like oldname, +// but it reports an error if sym is from another package and not exported. +func importName(sym *types.Sym) ir.Node { + n := oldname(sym) + if !types.IsExported(sym.Name) && sym.Pkg != types.LocalPkg { + n.SetDiag(true) + base.Errorf("cannot refer to unexported name %s.%s", sym.Pkg.Name, sym.Name) + } + return n +} diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go new file mode 100644 index 0000000..52224c4 --- /dev/null +++ b/src/cmd/compile/internal/noder/irgen.go @@ -0,0 +1,357 @@ +// Copyright 2021 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 noder + +import ( + "fmt" + "os" + + "cmd/compile/internal/base" + "cmd/compile/internal/dwarfgen" + "cmd/compile/internal/ir" + "cmd/compile/internal/syntax" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/compile/internal/types2" + "cmd/internal/src" +) + +// checkFiles configures and runs the types2 checker on the given +// parsed source files and then returns the result. +func checkFiles(noders []*noder) (posMap, *types2.Package, *types2.Info) { + if base.SyntaxErrors() != 0 { + base.ErrorExit() + } + + // setup and syntax error reporting + var m posMap + files := make([]*syntax.File, len(noders)) + for i, p := range noders { + m.join(&p.posMap) + files[i] = p.file + } + + // typechecking + ctxt := types2.NewContext() + importer := gcimports{ + ctxt: ctxt, + packages: map[string]*types2.Package{"unsafe": types2.Unsafe}, + } + conf := types2.Config{ + Context: ctxt, + GoVersion: base.Flag.Lang, + IgnoreLabels: true, // parser already checked via syntax.CheckBranches mode + CompilerErrorMessages: true, // use error strings matching existing compiler errors + Error: func(err error) { + terr := err.(types2.Error) + base.ErrorfAt(m.makeXPos(terr.Pos), "%s", terr.Msg) + }, + Importer: &importer, + Sizes: &gcSizes{}, + } + info := &types2.Info{ + Types: make(map[syntax.Expr]types2.TypeAndValue), + Defs: make(map[*syntax.Name]types2.Object), + Uses: make(map[*syntax.Name]types2.Object), + Selections: make(map[*syntax.SelectorExpr]*types2.Selection), + Implicits: make(map[syntax.Node]types2.Object), + Scopes: make(map[syntax.Node]*types2.Scope), + Instances: make(map[*syntax.Name]types2.Instance), + // expand as needed + } + + pkg, err := conf.Check(base.Ctxt.Pkgpath, files, info) + + base.ExitIfErrors() + if err != nil { + base.FatalfAt(src.NoXPos, "conf.Check error: %v", err) + } + + return m, pkg, info +} + +// check2 type checks a Go package using types2, and then generates IR +// using the results. +func check2(noders []*noder) { + m, pkg, info := checkFiles(noders) + + if base.Flag.G < 2 { + os.Exit(0) + } + + g := irgen{ + target: typecheck.Target, + self: pkg, + info: info, + posMap: m, + objs: make(map[types2.Object]*ir.Name), + typs: make(map[types2.Type]*types.Type), + } + g.generate(noders) + + if base.Flag.G < 3 { + os.Exit(0) + } +} + +// Information about sub-dictionary entries in a dictionary +type subDictInfo struct { + // Call or XDOT node that requires a dictionary. + callNode ir.Node + // Saved CallExpr.X node (*ir.SelectorExpr or *InstExpr node) for a generic + // method or function call, since this node will get dropped when the generic + // method/function call is transformed to a call on the instantiated shape + // function. Nil for other kinds of calls or XDOTs. + savedXNode ir.Node +} + +// dictInfo is the dictionary format for an instantiation of a generic function with +// particular shapes. shapeParams, derivedTypes, subDictCalls, and itabConvs describe +// the actual dictionary entries in order, and the remaining fields are other info +// needed in doing dictionary processing during compilation. +type dictInfo struct { + // Types substituted for the type parameters, which are shape types. + shapeParams []*types.Type + // All types derived from those typeparams used in the instantiation. + derivedTypes []*types.Type + // Nodes in the instantiation that requires a subdictionary. Includes + // method and function calls (OCALL), function values (OFUNCINST), method + // values/expressions (OXDOT). + subDictCalls []subDictInfo + // Nodes in the instantiation that are a conversion from a typeparam/derived + // type to a specific interface. + itabConvs []ir.Node + + // Mapping from each shape type that substitutes a type param, to its + // type bound (which is also substituted with shapes if it is parameterized) + shapeToBound map[*types.Type]*types.Type + + // For type switches on nonempty interfaces, a map from OTYPE entries of + // HasShape type, to the interface type we're switching from. + type2switchType map[ir.Node]*types.Type + + startSubDict int // Start of dict entries for subdictionaries + startItabConv int // Start of dict entries for itab conversions + dictLen int // Total number of entries in dictionary +} + +// instInfo is information gathered on an shape instantiation of a function. +type instInfo struct { + fun *ir.Func // The instantiated function (with body) + dictParam *ir.Name // The node inside fun that refers to the dictionary param + + dictInfo *dictInfo +} + +type irgen struct { + target *ir.Package + self *types2.Package + info *types2.Info + + posMap + objs map[types2.Object]*ir.Name + typs map[types2.Type]*types.Type + marker dwarfgen.ScopeMarker + + // laterFuncs records tasks that need to run after all declarations + // are processed. + laterFuncs []func() + // haveEmbed indicates whether the current node belongs to file that + // imports "embed" package. + haveEmbed bool + + // exprStmtOK indicates whether it's safe to generate expressions or + // statements yet. + exprStmtOK bool + + // types which we need to finish, by doing g.fillinMethods. + typesToFinalize []*typeDelayInfo + + // True when we are compiling a top-level generic function or method. Use to + // avoid adding closures of generic functions/methods to the target.Decls + // list. + topFuncIsGeneric bool + + // The context during type/function/method declarations that is used to + // uniquely name type parameters. We need unique names for type params so we + // can be sure they match up correctly between types2-to-types1 translation + // and types1 importing. + curDecl string +} + +// genInst has the information for creating needed instantiations and modifying +// functions to use instantiations. +type genInst struct { + dnum int // for generating unique dictionary variables + + // Map from the names of all instantiations to information about the + // instantiations. + instInfoMap map[*types.Sym]*instInfo + + // Dictionary syms which we need to finish, by writing out any itabconv + // entries. + dictSymsToFinalize []*delayInfo + + // New instantiations created during this round of buildInstantiations(). + newInsts []ir.Node +} + +func (g *irgen) later(fn func()) { + g.laterFuncs = append(g.laterFuncs, fn) +} + +type delayInfo struct { + gf *ir.Name + targs []*types.Type + sym *types.Sym + off int + isMeth bool +} + +type typeDelayInfo struct { + typ *types2.Named + ntyp *types.Type +} + +func (g *irgen) generate(noders []*noder) { + types.LocalPkg.Name = g.self.Name() + types.LocalPkg.Height = g.self.Height() + typecheck.TypecheckAllowed = true + + // Prevent size calculations until we set the underlying type + // for all package-block defined types. + types.DeferCheckSize() + + // At this point, types2 has already handled name resolution and + // type checking. We just need to map from its object and type + // representations to those currently used by the rest of the + // compiler. This happens in a few passes. + + // 1. Process all import declarations. We use the compiler's own + // importer for this, rather than types2's gcimporter-derived one, + // to handle extensions and inline function bodies correctly. + // + // Also, we need to do this in a separate pass, because mappings are + // instantiated on demand. If we interleaved processing import + // declarations with other declarations, it's likely we'd end up + // wanting to map an object/type from another source file, but not + // yet have the import data it relies on. + declLists := make([][]syntax.Decl, len(noders)) +Outer: + for i, p := range noders { + g.pragmaFlags(p.file.Pragma, ir.GoBuildPragma) + for j, decl := range p.file.DeclList { + switch decl := decl.(type) { + case *syntax.ImportDecl: + g.importDecl(p, decl) + default: + declLists[i] = p.file.DeclList[j:] + continue Outer // no more ImportDecls + } + } + } + + // 2. Process all package-block type declarations. As with imports, + // we need to make sure all types are properly instantiated before + // trying to map any expressions that utilize them. In particular, + // we need to make sure type pragmas are already known (see comment + // in irgen.typeDecl). + // + // We could perhaps instead defer processing of package-block + // variable initializers and function bodies, like noder does, but + // special-casing just package-block type declarations minimizes the + // differences between processing package-block and function-scoped + // declarations. + for _, declList := range declLists { + for _, decl := range declList { + switch decl := decl.(type) { + case *syntax.TypeDecl: + g.typeDecl((*ir.Nodes)(&g.target.Decls), decl) + } + } + } + types.ResumeCheckSize() + + // 3. Process all remaining declarations. + for i, declList := range declLists { + old := g.haveEmbed + g.haveEmbed = noders[i].importedEmbed + g.decls((*ir.Nodes)(&g.target.Decls), declList) + g.haveEmbed = old + } + g.exprStmtOK = true + + // 4. Run any "later" tasks. Avoid using 'range' so that tasks can + // recursively queue further tasks. (Not currently utilized though.) + for len(g.laterFuncs) > 0 { + fn := g.laterFuncs[0] + g.laterFuncs = g.laterFuncs[1:] + fn() + } + + if base.Flag.W > 1 { + for _, n := range g.target.Decls { + s := fmt.Sprintf("\nafter noder2 %v", n) + ir.Dump(s, n) + } + } + + for _, p := range noders { + // Process linkname and cgo pragmas. + p.processPragmas() + + // Double check for any type-checking inconsistencies. This can be + // removed once we're confident in IR generation results. + syntax.Crawl(p.file, func(n syntax.Node) bool { + g.validate(n) + return false + }) + } + + if base.Flag.Complete { + for _, n := range g.target.Decls { + if fn, ok := n.(*ir.Func); ok { + if fn.Body == nil && fn.Nname.Sym().Linkname == "" { + base.ErrorfAt(fn.Pos(), "missing function body") + } + } + } + } + + // Check for unusual case where noder2 encounters a type error that types2 + // doesn't check for (e.g. notinheap incompatibility). + base.ExitIfErrors() + + typecheck.DeclareUniverse() + + // Create any needed instantiations of generic functions and transform + // existing and new functions to use those instantiations. + BuildInstantiations() + + // Remove all generic functions from g.target.Decl, since they have been + // used for stenciling, but don't compile. Generic functions will already + // have been marked for export as appropriate. + j := 0 + for i, decl := range g.target.Decls { + if decl.Op() != ir.ODCLFUNC || !decl.Type().HasTParam() { + g.target.Decls[j] = g.target.Decls[i] + j++ + } + } + g.target.Decls = g.target.Decls[:j] + + base.Assertf(len(g.laterFuncs) == 0, "still have %d later funcs", len(g.laterFuncs)) +} + +func (g *irgen) unhandled(what string, p poser) { + base.FatalfAt(g.pos(p), "unhandled %s: %T", what, p) + panic("unreachable") +} + +// delayTransform returns true if we should delay all transforms, because we are +// creating the nodes for a generic function/method. +func (g *irgen) delayTransform() bool { + return g.topFuncIsGeneric +} diff --git a/src/cmd/compile/internal/noder/lex.go b/src/cmd/compile/internal/noder/lex.go new file mode 100644 index 0000000..66a56a5 --- /dev/null +++ b/src/cmd/compile/internal/noder/lex.go @@ -0,0 +1,193 @@ +// 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 noder + +import ( + "fmt" + "internal/buildcfg" + "strings" + + "cmd/compile/internal/ir" + "cmd/compile/internal/syntax" +) + +func isSpace(c rune) bool { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' +} + +func isQuoted(s string) bool { + return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"' +} + +const ( + funcPragmas = ir.Nointerface | + ir.Noescape | + ir.Norace | + ir.Nosplit | + ir.Noinline | + ir.NoCheckPtr | + ir.RegisterParams | // TODO(register args) remove after register abi is working + ir.CgoUnsafeArgs | + ir.UintptrEscapes | + ir.Systemstack | + ir.Nowritebarrier | + ir.Nowritebarrierrec | + ir.Yeswritebarrierrec + + typePragmas = ir.NotInHeap +) + +func pragmaFlag(verb string) ir.PragmaFlag { + switch verb { + case "go:build": + return ir.GoBuildPragma + case "go:nointerface": + if buildcfg.Experiment.FieldTrack { + return ir.Nointerface + } + case "go:noescape": + return ir.Noescape + case "go:norace": + return ir.Norace + case "go:nosplit": + return ir.Nosplit | ir.NoCheckPtr // implies NoCheckPtr (see #34972) + case "go:noinline": + return ir.Noinline + case "go:nocheckptr": + return ir.NoCheckPtr + case "go:systemstack": + return ir.Systemstack + case "go:nowritebarrier": + return ir.Nowritebarrier + case "go:nowritebarrierrec": + return ir.Nowritebarrierrec | ir.Nowritebarrier // implies Nowritebarrier + case "go:yeswritebarrierrec": + return ir.Yeswritebarrierrec + case "go:cgo_unsafe_args": + return ir.CgoUnsafeArgs | ir.NoCheckPtr // implies NoCheckPtr (see #34968) + case "go:uintptrescapes": + // For the next function declared in the file + // any uintptr arguments may be pointer values + // converted to uintptr. This directive + // ensures that the referenced allocated + // object, if any, is retained and not moved + // until the call completes, even though from + // the types alone it would appear that the + // object is no longer needed during the + // call. The conversion to uintptr must appear + // in the argument list. + // Used in syscall/dll_windows.go. + return ir.UintptrEscapes + case "go:registerparams": // TODO(register args) remove after register abi is working + return ir.RegisterParams + case "go:notinheap": + return ir.NotInHeap + } + return 0 +} + +// pragcgo is called concurrently if files are parsed concurrently. +func (p *noder) pragcgo(pos syntax.Pos, text string) { + f := pragmaFields(text) + + verb := strings.TrimPrefix(f[0], "go:") + f[0] = verb + + switch verb { + case "cgo_export_static", "cgo_export_dynamic": + switch { + case len(f) == 2 && !isQuoted(f[1]): + case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]): + default: + p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf(`usage: //go:%s local [remote]`, verb)}) + return + } + case "cgo_import_dynamic": + switch { + case len(f) == 2 && !isQuoted(f[1]): + case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]): + case len(f) == 4 && !isQuoted(f[1]) && !isQuoted(f[2]) && isQuoted(f[3]): + f[3] = strings.Trim(f[3], `"`) + if buildcfg.GOOS == "aix" && f[3] != "" { + // On Aix, library pattern must be "lib.a/object.o" + // or "lib.a/libname.so.X" + n := strings.Split(f[3], "/") + if len(n) != 2 || !strings.HasSuffix(n[0], ".a") || (!strings.HasSuffix(n[1], ".o") && !strings.Contains(n[1], ".so.")) { + p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_dynamic local [remote ["lib.a/object.o"]]`}) + return + } + } + default: + p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_dynamic local [remote ["library"]]`}) + return + } + case "cgo_import_static": + switch { + case len(f) == 2 && !isQuoted(f[1]): + default: + p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_static local`}) + return + } + case "cgo_dynamic_linker": + switch { + case len(f) == 2 && isQuoted(f[1]): + f[1] = strings.Trim(f[1], `"`) + default: + p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_dynamic_linker "path"`}) + return + } + case "cgo_ldflag": + switch { + case len(f) == 2 && isQuoted(f[1]): + f[1] = strings.Trim(f[1], `"`) + default: + p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_ldflag "arg"`}) + return + } + default: + return + } + p.pragcgobuf = append(p.pragcgobuf, f) +} + +// pragmaFields is similar to strings.FieldsFunc(s, isSpace) +// but does not split when inside double quoted regions and always +// splits before the start and after the end of a double quoted region. +// pragmaFields does not recognize escaped quotes. If a quote in s is not +// closed the part after the opening quote will not be returned as a field. +func pragmaFields(s string) []string { + var a []string + inQuote := false + fieldStart := -1 // Set to -1 when looking for start of field. + for i, c := range s { + switch { + case c == '"': + if inQuote { + inQuote = false + a = append(a, s[fieldStart:i+1]) + fieldStart = -1 + } else { + inQuote = true + if fieldStart >= 0 { + a = append(a, s[fieldStart:i]) + } + fieldStart = i + } + case !inQuote && isSpace(c): + if fieldStart >= 0 { + a = append(a, s[fieldStart:i]) + fieldStart = -1 + } + default: + if fieldStart == -1 { + fieldStart = i + } + } + } + if !inQuote && fieldStart >= 0 { // Last field might end at the end of the string. + a = append(a, s[fieldStart:]) + } + return a +} diff --git a/src/cmd/compile/internal/noder/lex_test.go b/src/cmd/compile/internal/noder/lex_test.go new file mode 100644 index 0000000..85a3f06 --- /dev/null +++ b/src/cmd/compile/internal/noder/lex_test.go @@ -0,0 +1,122 @@ +// Copyright 2016 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 noder + +import ( + "reflect" + "runtime" + "testing" + + "cmd/compile/internal/syntax" +) + +func eq(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i := 0; i < len(a); i++ { + if a[i] != b[i] { + return false + } + } + return true +} + +func TestPragmaFields(t *testing.T) { + var tests = []struct { + in string + want []string + }{ + {"", []string{}}, + {" \t ", []string{}}, + {`""""`, []string{`""`, `""`}}, + {" a'b'c ", []string{"a'b'c"}}, + {"1 2 3 4", []string{"1", "2", "3", "4"}}, + {"\n☺\t☹\n", []string{"☺", "☹"}}, + {`"1 2 " 3 " 4 5"`, []string{`"1 2 "`, `3`, `" 4 5"`}}, + {`"1""2 3""4"`, []string{`"1"`, `"2 3"`, `"4"`}}, + {`12"34"`, []string{`12`, `"34"`}}, + {`12"34 `, []string{`12`}}, + } + + for _, tt := range tests { + got := pragmaFields(tt.in) + if !eq(got, tt.want) { + t.Errorf("pragmaFields(%q) = %v; want %v", tt.in, got, tt.want) + continue + } + } +} + +func TestPragcgo(t *testing.T) { + type testStruct struct { + in string + want []string + } + + var tests = []testStruct{ + {`go:cgo_export_dynamic local`, []string{`cgo_export_dynamic`, `local`}}, + {`go:cgo_export_dynamic local remote`, []string{`cgo_export_dynamic`, `local`, `remote`}}, + {`go:cgo_export_dynamic local' remote'`, []string{`cgo_export_dynamic`, `local'`, `remote'`}}, + {`go:cgo_export_static local`, []string{`cgo_export_static`, `local`}}, + {`go:cgo_export_static local remote`, []string{`cgo_export_static`, `local`, `remote`}}, + {`go:cgo_export_static local' remote'`, []string{`cgo_export_static`, `local'`, `remote'`}}, + {`go:cgo_import_dynamic local`, []string{`cgo_import_dynamic`, `local`}}, + {`go:cgo_import_dynamic local remote`, []string{`cgo_import_dynamic`, `local`, `remote`}}, + {`go:cgo_import_static local`, []string{`cgo_import_static`, `local`}}, + {`go:cgo_import_static local'`, []string{`cgo_import_static`, `local'`}}, + {`go:cgo_dynamic_linker "/path/"`, []string{`cgo_dynamic_linker`, `/path/`}}, + {`go:cgo_dynamic_linker "/p ath/"`, []string{`cgo_dynamic_linker`, `/p ath/`}}, + {`go:cgo_ldflag "arg"`, []string{`cgo_ldflag`, `arg`}}, + {`go:cgo_ldflag "a rg"`, []string{`cgo_ldflag`, `a rg`}}, + } + + if runtime.GOOS != "aix" { + tests = append(tests, []testStruct{ + {`go:cgo_import_dynamic local remote "library"`, []string{`cgo_import_dynamic`, `local`, `remote`, `library`}}, + {`go:cgo_import_dynamic local' remote' "lib rary"`, []string{`cgo_import_dynamic`, `local'`, `remote'`, `lib rary`}}, + }...) + } else { + // cgo_import_dynamic with a library is slightly different on AIX + // as the library field must follow the pattern [libc.a/object.o]. + tests = append(tests, []testStruct{ + {`go:cgo_import_dynamic local remote "lib.a/obj.o"`, []string{`cgo_import_dynamic`, `local`, `remote`, `lib.a/obj.o`}}, + // This test must fail. + {`go:cgo_import_dynamic local' remote' "library"`, []string{`<unknown position>: usage: //go:cgo_import_dynamic local [remote ["lib.a/object.o"]]`}}, + }...) + + } + + var p noder + var nopos syntax.Pos + for _, tt := range tests { + + p.err = make(chan syntax.Error) + gotch := make(chan [][]string, 1) + go func() { + p.pragcgobuf = nil + p.pragcgo(nopos, tt.in) + if p.pragcgobuf != nil { + gotch <- p.pragcgobuf + } + }() + + select { + case e := <-p.err: + want := tt.want[0] + if e.Error() != want { + t.Errorf("pragcgo(%q) = %q; want %q", tt.in, e, want) + continue + } + case got := <-gotch: + want := [][]string{tt.want} + if !reflect.DeepEqual(got, want) { + t.Errorf("pragcgo(%q) = %q; want %q", tt.in, got, want) + continue + } + } + + } +} diff --git a/src/cmd/compile/internal/noder/linker.go b/src/cmd/compile/internal/noder/linker.go new file mode 100644 index 0000000..2bc7f7c --- /dev/null +++ b/src/cmd/compile/internal/noder/linker.go @@ -0,0 +1,296 @@ +// UNREVIEWED + +// Copyright 2021 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 noder + +import ( + "io" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/reflectdata" + "cmd/compile/internal/types" + "cmd/internal/goobj" + "cmd/internal/obj" +) + +// This file implements the unified IR linker, which combines the +// local package's stub data with imported package data to produce a +// complete export data file. It also rewrites the compiler's +// extension data sections based on the results of compilation (e.g., +// the function inlining cost and linker symbol index assignments). +// +// TODO(mdempsky): Using the name "linker" here is confusing, because +// readers are likely to mistake references to it for cmd/link. But +// there's a shortage of good names for "something that combines +// multiple parts into a cohesive whole"... e.g., "assembler" and +// "compiler" are also already taken. + +type linker struct { + pw pkgEncoder + + pkgs map[string]int + decls map[*types.Sym]int +} + +func (l *linker) relocAll(pr *pkgReader, relocs []relocEnt) []relocEnt { + res := make([]relocEnt, len(relocs)) + for i, rent := range relocs { + rent.idx = l.relocIdx(pr, rent.kind, rent.idx) + res[i] = rent + } + return res +} + +func (l *linker) relocIdx(pr *pkgReader, k reloc, idx int) int { + assert(pr != nil) + + absIdx := pr.absIdx(k, idx) + + if newidx := pr.newindex[absIdx]; newidx != 0 { + return ^newidx + } + + var newidx int + switch k { + case relocString: + newidx = l.relocString(pr, idx) + case relocPkg: + newidx = l.relocPkg(pr, idx) + case relocObj: + newidx = l.relocObj(pr, idx) + + default: + // Generic relocations. + // + // TODO(mdempsky): Deduplicate more sections? In fact, I think + // every section could be deduplicated. This would also be easier + // if we do external relocations. + + w := l.pw.newEncoderRaw(k) + l.relocCommon(pr, &w, k, idx) + newidx = w.idx + } + + pr.newindex[absIdx] = ^newidx + + return newidx +} + +func (l *linker) relocString(pr *pkgReader, idx int) int { + return l.pw.stringIdx(pr.stringIdx(idx)) +} + +func (l *linker) relocPkg(pr *pkgReader, idx int) int { + path := pr.peekPkgPath(idx) + + if newidx, ok := l.pkgs[path]; ok { + return newidx + } + + r := pr.newDecoder(relocPkg, idx, syncPkgDef) + w := l.pw.newEncoder(relocPkg, syncPkgDef) + l.pkgs[path] = w.idx + + // TODO(mdempsky): We end up leaving an empty string reference here + // from when the package was originally written as "". Probably not + // a big deal, but a little annoying. Maybe relocating + // cross-references in place is the way to go after all. + w.relocs = l.relocAll(pr, r.relocs) + + _ = r.string() // original path + w.string(path) + + io.Copy(&w.data, &r.data) + + return w.flush() +} + +func (l *linker) relocObj(pr *pkgReader, idx int) int { + path, name, tag := pr.peekObj(idx) + sym := types.NewPkg(path, "").Lookup(name) + + if newidx, ok := l.decls[sym]; ok { + return newidx + } + + if tag == objStub && path != "builtin" && path != "unsafe" { + pri, ok := objReader[sym] + if !ok { + base.Fatalf("missing reader for %q.%v", path, name) + } + assert(ok) + + pr = pri.pr + idx = pri.idx + + path2, name2, tag2 := pr.peekObj(idx) + sym2 := types.NewPkg(path2, "").Lookup(name2) + assert(sym == sym2) + assert(tag2 != objStub) + } + + w := l.pw.newEncoderRaw(relocObj) + wext := l.pw.newEncoderRaw(relocObjExt) + wname := l.pw.newEncoderRaw(relocName) + wdict := l.pw.newEncoderRaw(relocObjDict) + + l.decls[sym] = w.idx + assert(wext.idx == w.idx) + assert(wname.idx == w.idx) + assert(wdict.idx == w.idx) + + l.relocCommon(pr, &w, relocObj, idx) + l.relocCommon(pr, &wname, relocName, idx) + l.relocCommon(pr, &wdict, relocObjDict, idx) + + var obj *ir.Name + if path == "" { + var ok bool + obj, ok = sym.Def.(*ir.Name) + + // Generic types and functions and declared constraint types won't + // have definitions. + // For now, just generically copy their extension data. + // TODO(mdempsky): Restore assertion. + if !ok && false { + base.Fatalf("missing definition for %v", sym) + } + } + + if obj != nil { + wext.sync(syncObject1) + switch tag { + case objFunc: + l.relocFuncExt(&wext, obj) + case objType: + l.relocTypeExt(&wext, obj) + case objVar: + l.relocVarExt(&wext, obj) + } + wext.flush() + } else { + l.relocCommon(pr, &wext, relocObjExt, idx) + } + + return w.idx +} + +func (l *linker) relocCommon(pr *pkgReader, w *encoder, k reloc, idx int) { + r := pr.newDecoderRaw(k, idx) + w.relocs = l.relocAll(pr, r.relocs) + io.Copy(&w.data, &r.data) + w.flush() +} + +func (l *linker) pragmaFlag(w *encoder, pragma ir.PragmaFlag) { + w.sync(syncPragma) + w.int(int(pragma)) +} + +func (l *linker) relocFuncExt(w *encoder, name *ir.Name) { + w.sync(syncFuncExt) + + l.pragmaFlag(w, name.Func.Pragma) + l.linkname(w, name) + + // Relocated extension data. + w.bool(true) + + // Record definition ABI so cross-ABI calls can be direct. + // This is important for the performance of calling some + // common functions implemented in assembly (e.g., bytealg). + w.uint64(uint64(name.Func.ABI)) + + // Escape analysis. + for _, fs := range &types.RecvsParams { + for _, f := range fs(name.Type()).FieldSlice() { + w.string(f.Note) + } + } + + if inl := name.Func.Inl; w.bool(inl != nil) { + w.len(int(inl.Cost)) + w.bool(inl.CanDelayResults) + + pri, ok := bodyReader[name.Func] + assert(ok) + w.reloc(relocBody, l.relocIdx(pri.pr, relocBody, pri.idx)) + } + + w.sync(syncEOF) +} + +func (l *linker) relocTypeExt(w *encoder, name *ir.Name) { + w.sync(syncTypeExt) + + typ := name.Type() + + l.pragmaFlag(w, name.Pragma()) + + // For type T, export the index of type descriptor symbols of T and *T. + l.lsymIdx(w, "", reflectdata.TypeLinksym(typ)) + l.lsymIdx(w, "", reflectdata.TypeLinksym(typ.PtrTo())) + + if typ.Kind() != types.TINTER { + for _, method := range typ.Methods().Slice() { + l.relocFuncExt(w, method.Nname.(*ir.Name)) + } + } +} + +func (l *linker) relocVarExt(w *encoder, name *ir.Name) { + w.sync(syncVarExt) + l.linkname(w, name) +} + +func (l *linker) linkname(w *encoder, name *ir.Name) { + w.sync(syncLinkname) + + linkname := name.Sym().Linkname + if !l.lsymIdx(w, linkname, name.Linksym()) { + w.string(linkname) + } +} + +func (l *linker) lsymIdx(w *encoder, linkname string, lsym *obj.LSym) bool { + if lsym.PkgIdx > goobj.PkgIdxSelf || (lsym.PkgIdx == goobj.PkgIdxInvalid && !lsym.Indexed()) || linkname != "" { + w.int64(-1) + return false + } + + // For a defined symbol, export its index. + // For re-exporting an imported symbol, pass its index through. + w.int64(int64(lsym.SymIdx)) + return true +} + +// @@@ Helpers + +// TODO(mdempsky): These should probably be removed. I think they're a +// smell that the export data format is not yet quite right. + +func (pr *pkgDecoder) peekPkgPath(idx int) string { + r := pr.newDecoder(relocPkg, idx, syncPkgDef) + path := r.string() + if path == "" { + path = pr.pkgPath + } + return path +} + +func (pr *pkgDecoder) peekObj(idx int) (string, string, codeObj) { + r := pr.newDecoder(relocName, idx, syncObject1) + r.sync(syncSym) + r.sync(syncPkg) + path := pr.peekPkgPath(r.reloc(relocPkg)) + name := r.string() + assert(name != "") + + tag := codeObj(r.code(syncCodeObj)) + + return path, name, tag +} diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go new file mode 100644 index 0000000..17ec877 --- /dev/null +++ b/src/cmd/compile/internal/noder/noder.go @@ -0,0 +1,1917 @@ +// Copyright 2016 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 noder + +import ( + "errors" + "fmt" + "go/constant" + "go/token" + "internal/buildcfg" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "cmd/compile/internal/base" + "cmd/compile/internal/dwarfgen" + "cmd/compile/internal/ir" + "cmd/compile/internal/syntax" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/internal/objabi" + "cmd/internal/src" +) + +func LoadPackage(filenames []string) { + base.Timer.Start("fe", "parse") + + // -G=3 and unified expect generics syntax, but -G=0 does not. + supportsGenerics := base.Flag.G != 0 || buildcfg.Experiment.Unified + + mode := syntax.CheckBranches + if supportsGenerics { + mode |= syntax.AllowGenerics + } + + // Limit the number of simultaneously open files. + sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10) + + noders := make([]*noder, len(filenames)) + for i, filename := range filenames { + p := noder{ + err: make(chan syntax.Error), + trackScopes: base.Flag.Dwarf, + } + noders[i] = &p + + filename := filename + go func() { + sem <- struct{}{} + defer func() { <-sem }() + defer close(p.err) + fbase := syntax.NewFileBase(filename) + + f, err := os.Open(filename) + if err != nil { + p.error(syntax.Error{Msg: err.Error()}) + return + } + defer f.Close() + + p.file, _ = syntax.Parse(fbase, f, p.error, p.pragma, mode) // errors are tracked via p.error + }() + } + + var lines uint + for _, p := range noders { + for e := range p.err { + p.errorAt(e.Pos, "%s", e.Msg) + } + if p.file == nil { + base.ErrorExit() + } + lines += p.file.EOF.Line() + } + base.Timer.AddEvent(int64(lines), "lines") + + if base.Debug.Unified != 0 { + unified(noders) + return + } + + if base.Flag.G != 0 { + // Use types2 to type-check and possibly generate IR. + check2(noders) + return + } + + for _, p := range noders { + p.node() + p.file = nil // release memory + } + + if base.SyntaxErrors() != 0 { + base.ErrorExit() + } + types.CheckDclstack() + + for _, p := range noders { + p.processPragmas() + } + + // Typecheck. + types.LocalPkg.Height = myheight + typecheck.DeclareUniverse() + typecheck.TypecheckAllowed = true + + // Process top-level declarations in phases. + + // Phase 1: const, type, and names and types of funcs. + // This will gather all the information about types + // and methods but doesn't depend on any of it. + // + // We also defer type alias declarations until phase 2 + // to avoid cycles like #18640. + // TODO(gri) Remove this again once we have a fix for #25838. + // + // Phase 2: Variable assignments. + // To check interface assignments, depends on phase 1. + + // Don't use range--typecheck can add closures to Target.Decls. + for phase, name := range []string{"top1", "top2"} { + base.Timer.Start("fe", "typecheck", name) + for i := 0; i < len(typecheck.Target.Decls); i++ { + n := typecheck.Target.Decls[i] + op := n.Op() + + // Closure function declarations are typechecked as part of the + // closure expression. + if fn, ok := n.(*ir.Func); ok && fn.OClosure != nil { + continue + } + + // We don't actually add ir.ODCL nodes to Target.Decls. Make sure of that. + if op == ir.ODCL { + base.FatalfAt(n.Pos(), "unexpected top declaration: %v", op) + } + + // Identify declarations that should be deferred to the second + // iteration. + late := op == ir.OAS || op == ir.OAS2 || op == ir.ODCLTYPE && n.(*ir.Decl).X.Alias() + + if late == (phase == 1) { + typecheck.Target.Decls[i] = typecheck.Stmt(n) + } + } + } + + // Phase 3: Type check function bodies. + // Don't use range--typecheck can add closures to Target.Decls. + base.Timer.Start("fe", "typecheck", "func") + for i := 0; i < len(typecheck.Target.Decls); i++ { + if fn, ok := typecheck.Target.Decls[i].(*ir.Func); ok { + if base.Flag.W > 1 { + s := fmt.Sprintf("\nbefore typecheck %v", fn) + ir.Dump(s, fn) + } + typecheck.FuncBody(fn) + if base.Flag.W > 1 { + s := fmt.Sprintf("\nafter typecheck %v", fn) + ir.Dump(s, fn) + } + } + } + + // Phase 4: Check external declarations. + // TODO(mdempsky): This should be handled when type checking their + // corresponding ODCL nodes. + base.Timer.Start("fe", "typecheck", "externdcls") + for i, n := range typecheck.Target.Externs { + if n.Op() == ir.ONAME { + typecheck.Target.Externs[i] = typecheck.Expr(typecheck.Target.Externs[i]) + } + } + + // Phase 5: With all user code type-checked, it's now safe to verify map keys. + // With all user code typechecked, it's now safe to verify unused dot imports. + typecheck.CheckMapKeys() + CheckDotImports() + base.ExitIfErrors() +} + +func (p *noder) errorAt(pos syntax.Pos, format string, args ...interface{}) { + base.ErrorfAt(p.makeXPos(pos), format, args...) +} + +// trimFilename returns the "trimmed" filename of b, which is the +// absolute filename after applying -trimpath processing. This +// filename form is suitable for use in object files and export data. +// +// If b's filename has already been trimmed (i.e., because it was read +// in from an imported package's export data), then the filename is +// returned unchanged. +func trimFilename(b *syntax.PosBase) string { + filename := b.Filename() + if !b.Trimmed() { + dir := "" + if b.IsFileBase() { + dir = base.Ctxt.Pathname + } + filename = objabi.AbsFile(dir, filename, base.Flag.TrimPath) + } + return filename +} + +// noder transforms package syntax's AST into a Node tree. +type noder struct { + posMap + + file *syntax.File + linknames []linkname + pragcgobuf [][]string + err chan syntax.Error + importedUnsafe bool + importedEmbed bool + trackScopes bool + + funcState *funcState +} + +// funcState tracks all per-function state to make handling nested +// functions easier. +type funcState struct { + // scopeVars is a stack tracking the number of variables declared in + // the current function at the moment each open scope was opened. + scopeVars []int + marker dwarfgen.ScopeMarker + + lastCloseScopePos syntax.Pos +} + +func (p *noder) funcBody(fn *ir.Func, block *syntax.BlockStmt) { + outerFuncState := p.funcState + p.funcState = new(funcState) + typecheck.StartFuncBody(fn) + + if block != nil { + body := p.stmts(block.List) + if body == nil { + body = []ir.Node{ir.NewBlockStmt(base.Pos, nil)} + } + fn.Body = body + + base.Pos = p.makeXPos(block.Rbrace) + fn.Endlineno = base.Pos + } + + typecheck.FinishFuncBody() + p.funcState.marker.WriteTo(fn) + p.funcState = outerFuncState +} + +func (p *noder) openScope(pos syntax.Pos) { + fs := p.funcState + types.Markdcl() + + if p.trackScopes { + fs.scopeVars = append(fs.scopeVars, len(ir.CurFunc.Dcl)) + fs.marker.Push(p.makeXPos(pos)) + } +} + +func (p *noder) closeScope(pos syntax.Pos) { + fs := p.funcState + fs.lastCloseScopePos = pos + types.Popdcl() + + if p.trackScopes { + scopeVars := fs.scopeVars[len(fs.scopeVars)-1] + fs.scopeVars = fs.scopeVars[:len(fs.scopeVars)-1] + if scopeVars == len(ir.CurFunc.Dcl) { + // no variables were declared in this scope, so we can retract it. + fs.marker.Unpush() + } else { + fs.marker.Pop(p.makeXPos(pos)) + } + } +} + +// closeAnotherScope is like closeScope, but it reuses the same mark +// position as the last closeScope call. This is useful for "for" and +// "if" statements, as their implicit blocks always end at the same +// position as an explicit block. +func (p *noder) closeAnotherScope() { + p.closeScope(p.funcState.lastCloseScopePos) +} + +// linkname records a //go:linkname directive. +type linkname struct { + pos syntax.Pos + local string + remote string +} + +func (p *noder) node() { + p.importedUnsafe = false + p.importedEmbed = false + + p.setlineno(p.file.PkgName) + mkpackage(p.file.PkgName.Value) + + if pragma, ok := p.file.Pragma.(*pragmas); ok { + pragma.Flag &^= ir.GoBuildPragma + p.checkUnused(pragma) + } + + typecheck.Target.Decls = append(typecheck.Target.Decls, p.decls(p.file.DeclList)...) + + base.Pos = src.NoXPos + clearImports() +} + +func (p *noder) processPragmas() { + for _, l := range p.linknames { + if !p.importedUnsafe { + p.errorAt(l.pos, "//go:linkname only allowed in Go files that import \"unsafe\"") + continue + } + n := ir.AsNode(typecheck.Lookup(l.local).Def) + if n == nil || n.Op() != ir.ONAME { + if types.AllowsGoVersion(types.LocalPkg, 1, 18) { + p.errorAt(l.pos, "//go:linkname must refer to declared function or variable") + } + continue + } + if n.Sym().Linkname != "" { + p.errorAt(l.pos, "duplicate //go:linkname for %s", l.local) + continue + } + n.Sym().Linkname = l.remote + } + typecheck.Target.CgoPragmas = append(typecheck.Target.CgoPragmas, p.pragcgobuf...) +} + +func (p *noder) decls(decls []syntax.Decl) (l []ir.Node) { + var cs constState + + for _, decl := range decls { + p.setlineno(decl) + switch decl := decl.(type) { + case *syntax.ImportDecl: + p.importDecl(decl) + + case *syntax.VarDecl: + l = append(l, p.varDecl(decl)...) + + case *syntax.ConstDecl: + l = append(l, p.constDecl(decl, &cs)...) + + case *syntax.TypeDecl: + l = append(l, p.typeDecl(decl)) + + case *syntax.FuncDecl: + l = append(l, p.funcDecl(decl)) + + default: + panic("unhandled Decl") + } + } + + return +} + +func (p *noder) importDecl(imp *syntax.ImportDecl) { + if imp.Path == nil || imp.Path.Bad { + return // avoid follow-on errors if there was a syntax error + } + + if pragma, ok := imp.Pragma.(*pragmas); ok { + p.checkUnused(pragma) + } + + ipkg := importfile(imp) + if ipkg == nil { + if base.Errors() == 0 { + base.Fatalf("phase error in import") + } + return + } + + if ipkg == types.UnsafePkg { + p.importedUnsafe = true + } + if ipkg.Path == "embed" { + p.importedEmbed = true + } + + var my *types.Sym + if imp.LocalPkgName != nil { + my = p.name(imp.LocalPkgName) + } else { + my = typecheck.Lookup(ipkg.Name) + } + + pack := ir.NewPkgName(p.pos(imp), my, ipkg) + + switch my.Name { + case ".": + importDot(pack) + return + case "init": + base.ErrorfAt(pack.Pos(), "cannot import package as init - init must be a func") + return + case "_": + return + } + if my.Def != nil { + typecheck.Redeclared(pack.Pos(), my, "as imported package name") + } + my.Def = pack + my.Lastlineno = pack.Pos() + my.Block = 1 // at top level +} + +func (p *noder) varDecl(decl *syntax.VarDecl) []ir.Node { + names := p.declNames(ir.ONAME, decl.NameList) + typ := p.typeExprOrNil(decl.Type) + exprs := p.exprList(decl.Values) + + if pragma, ok := decl.Pragma.(*pragmas); ok { + varEmbed(p.makeXPos, names[0], decl, pragma, p.importedEmbed) + p.checkUnused(pragma) + } + + var init []ir.Node + p.setlineno(decl) + + if len(names) > 1 && len(exprs) == 1 { + as2 := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, exprs) + for _, v := range names { + as2.Lhs.Append(v) + typecheck.Declare(v, typecheck.DeclContext) + v.Ntype = typ + v.Defn = as2 + if ir.CurFunc != nil { + init = append(init, ir.NewDecl(base.Pos, ir.ODCL, v)) + } + } + + return append(init, as2) + } + + for i, v := range names { + var e ir.Node + if i < len(exprs) { + e = exprs[i] + } + + typecheck.Declare(v, typecheck.DeclContext) + v.Ntype = typ + + if ir.CurFunc != nil { + init = append(init, ir.NewDecl(base.Pos, ir.ODCL, v)) + } + as := ir.NewAssignStmt(base.Pos, v, e) + init = append(init, as) + if e != nil || ir.CurFunc == nil { + v.Defn = as + } + } + + if len(exprs) != 0 && len(names) != len(exprs) { + base.Errorf("assignment mismatch: %d variables but %d values", len(names), len(exprs)) + } + + return init +} + +// constState tracks state between constant specifiers within a +// declaration group. This state is kept separate from noder so nested +// constant declarations are handled correctly (e.g., issue 15550). +type constState struct { + group *syntax.Group + typ ir.Ntype + values syntax.Expr + iota int64 +} + +func (p *noder) constDecl(decl *syntax.ConstDecl, cs *constState) []ir.Node { + if decl.Group == nil || decl.Group != cs.group { + *cs = constState{ + group: decl.Group, + } + } + + if pragma, ok := decl.Pragma.(*pragmas); ok { + p.checkUnused(pragma) + } + + names := p.declNames(ir.OLITERAL, decl.NameList) + typ := p.typeExprOrNil(decl.Type) + + if decl.Values != nil { + cs.typ, cs.values = typ, decl.Values + } else { + if typ != nil { + base.Errorf("const declaration cannot have type without expression") + } + typ = cs.typ + } + values := p.exprList(cs.values) + + nn := make([]ir.Node, 0, len(names)) + for i, n := range names { + if i >= len(values) { + base.Errorf("missing value in const declaration") + break + } + + v := values[i] + if decl.Values == nil { + ir.Visit(v, func(v ir.Node) { + if ir.HasUniquePos(v) { + v.SetPos(n.Pos()) + } + }) + } + + typecheck.Declare(n, typecheck.DeclContext) + + n.Ntype = typ + n.Defn = v + n.SetIota(cs.iota) + + nn = append(nn, ir.NewDecl(p.pos(decl), ir.ODCLCONST, n)) + } + + if len(values) > len(names) { + base.Errorf("extra expression in const declaration") + } + + cs.iota++ + + return nn +} + +func (p *noder) typeDecl(decl *syntax.TypeDecl) ir.Node { + n := p.declName(ir.OTYPE, decl.Name) + typecheck.Declare(n, typecheck.DeclContext) + + // decl.Type may be nil but in that case we got a syntax error during parsing + typ := p.typeExprOrNil(decl.Type) + + n.Ntype = typ + n.SetAlias(decl.Alias) + if pragma, ok := decl.Pragma.(*pragmas); ok { + if !decl.Alias { + n.SetPragma(pragma.Flag & typePragmas) + pragma.Flag &^= typePragmas + } + p.checkUnused(pragma) + } + + nod := ir.NewDecl(p.pos(decl), ir.ODCLTYPE, n) + if n.Alias() && !types.AllowsGoVersion(types.LocalPkg, 1, 9) { + base.ErrorfAt(nod.Pos(), "type aliases only supported as of -lang=go1.9") + } + return nod +} + +func (p *noder) declNames(op ir.Op, names []*syntax.Name) []*ir.Name { + nodes := make([]*ir.Name, 0, len(names)) + for _, name := range names { + nodes = append(nodes, p.declName(op, name)) + } + return nodes +} + +func (p *noder) declName(op ir.Op, name *syntax.Name) *ir.Name { + return ir.NewDeclNameAt(p.pos(name), op, p.name(name)) +} + +func (p *noder) funcDecl(fun *syntax.FuncDecl) ir.Node { + name := p.name(fun.Name) + t := p.signature(fun.Recv, fun.Type) + f := ir.NewFunc(p.pos(fun)) + + if fun.Recv == nil { + if name.Name == "init" { + name = renameinit() + if len(t.Params) > 0 || len(t.Results) > 0 { + base.ErrorfAt(f.Pos(), "func init must have no arguments and no return values") + } + typecheck.Target.Inits = append(typecheck.Target.Inits, f) + } + + if types.LocalPkg.Name == "main" && name.Name == "main" { + if len(t.Params) > 0 || len(t.Results) > 0 { + base.ErrorfAt(f.Pos(), "func main must have no arguments and no return values") + } + } + } else { + f.Shortname = name + name = ir.BlankNode.Sym() // filled in by tcFunc + } + + f.Nname = ir.NewNameAt(p.pos(fun.Name), name) + f.Nname.Func = f + f.Nname.Defn = f + f.Nname.Ntype = t + + if pragma, ok := fun.Pragma.(*pragmas); ok { + f.Pragma = pragma.Flag & funcPragmas + if pragma.Flag&ir.Systemstack != 0 && pragma.Flag&ir.Nosplit != 0 { + base.ErrorfAt(f.Pos(), "go:nosplit and go:systemstack cannot be combined") + } + pragma.Flag &^= funcPragmas + p.checkUnused(pragma) + } + + if fun.Recv == nil { + typecheck.Declare(f.Nname, ir.PFUNC) + } + + p.funcBody(f, fun.Body) + + if fun.Body != nil { + if f.Pragma&ir.Noescape != 0 { + base.ErrorfAt(f.Pos(), "can only use //go:noescape with external func implementations") + } + } else { + if base.Flag.Complete || strings.HasPrefix(ir.FuncName(f), "init.") { + // Linknamed functions are allowed to have no body. Hopefully + // the linkname target has a body. See issue 23311. + isLinknamed := false + for _, n := range p.linknames { + if ir.FuncName(f) == n.local { + isLinknamed = true + break + } + } + if !isLinknamed { + base.ErrorfAt(f.Pos(), "missing function body") + } + } + } + + return f +} + +func (p *noder) signature(recv *syntax.Field, typ *syntax.FuncType) *ir.FuncType { + var rcvr *ir.Field + if recv != nil { + rcvr = p.param(recv, false, false) + } + return ir.NewFuncType(p.pos(typ), rcvr, + p.params(typ.ParamList, true), + p.params(typ.ResultList, false)) +} + +func (p *noder) params(params []*syntax.Field, dddOk bool) []*ir.Field { + nodes := make([]*ir.Field, 0, len(params)) + for i, param := range params { + p.setlineno(param) + nodes = append(nodes, p.param(param, dddOk, i+1 == len(params))) + if i > 0 && params[i].Type == params[i-1].Type { + nodes[i].Ntype = nodes[i-1].Ntype + } + } + return nodes +} + +func (p *noder) param(param *syntax.Field, dddOk, final bool) *ir.Field { + var name *types.Sym + if param.Name != nil { + name = p.name(param.Name) + } + + typ := p.typeExpr(param.Type) + n := ir.NewField(p.pos(param), name, typ, nil) + + // rewrite ...T parameter + if typ, ok := typ.(*ir.SliceType); ok && typ.DDD { + if !dddOk { + // We mark these as syntax errors to get automatic elimination + // of multiple such errors per line (see ErrorfAt in subr.go). + base.Errorf("syntax error: cannot use ... in receiver or result parameter list") + } else if !final { + if param.Name == nil { + base.Errorf("syntax error: cannot use ... with non-final parameter") + } else { + p.errorAt(param.Name.Pos(), "syntax error: cannot use ... with non-final parameter %s", param.Name.Value) + } + } + typ.DDD = false + n.IsDDD = true + } + + return n +} + +func (p *noder) exprList(expr syntax.Expr) []ir.Node { + switch expr := expr.(type) { + case nil: + return nil + case *syntax.ListExpr: + return p.exprs(expr.ElemList) + default: + return []ir.Node{p.expr(expr)} + } +} + +func (p *noder) exprs(exprs []syntax.Expr) []ir.Node { + nodes := make([]ir.Node, 0, len(exprs)) + for _, expr := range exprs { + nodes = append(nodes, p.expr(expr)) + } + return nodes +} + +func (p *noder) expr(expr syntax.Expr) ir.Node { + p.setlineno(expr) + switch expr := expr.(type) { + case nil, *syntax.BadExpr: + return nil + case *syntax.Name: + return p.mkname(expr) + case *syntax.BasicLit: + n := ir.NewBasicLit(p.pos(expr), p.basicLit(expr)) + if expr.Kind == syntax.RuneLit { + n.SetType(types.UntypedRune) + } + n.SetDiag(expr.Bad || n.Val().Kind() == constant.Unknown) // avoid follow-on errors if there was a syntax error + return n + case *syntax.CompositeLit: + n := ir.NewCompLitExpr(p.pos(expr), ir.OCOMPLIT, p.typeExpr(expr.Type), nil) + l := p.exprs(expr.ElemList) + for i, e := range l { + l[i] = p.wrapname(expr.ElemList[i], e) + } + n.List = l + base.Pos = p.makeXPos(expr.Rbrace) + return n + case *syntax.KeyValueExpr: + // use position of expr.Key rather than of expr (which has position of ':') + return ir.NewKeyExpr(p.pos(expr.Key), p.expr(expr.Key), p.wrapname(expr.Value, p.expr(expr.Value))) + case *syntax.FuncLit: + return p.funcLit(expr) + case *syntax.ParenExpr: + return ir.NewParenExpr(p.pos(expr), p.expr(expr.X)) + case *syntax.SelectorExpr: + // parser.new_dotname + obj := p.expr(expr.X) + if obj.Op() == ir.OPACK { + pack := obj.(*ir.PkgName) + pack.Used = true + return importName(pack.Pkg.Lookup(expr.Sel.Value)) + } + n := ir.NewSelectorExpr(base.Pos, ir.OXDOT, obj, p.name(expr.Sel)) + n.SetPos(p.pos(expr)) // lineno may have been changed by p.expr(expr.X) + return n + case *syntax.IndexExpr: + return ir.NewIndexExpr(p.pos(expr), p.expr(expr.X), p.expr(expr.Index)) + case *syntax.SliceExpr: + op := ir.OSLICE + if expr.Full { + op = ir.OSLICE3 + } + x := p.expr(expr.X) + var index [3]ir.Node + for i, n := range &expr.Index { + if n != nil { + index[i] = p.expr(n) + } + } + return ir.NewSliceExpr(p.pos(expr), op, x, index[0], index[1], index[2]) + case *syntax.AssertExpr: + return ir.NewTypeAssertExpr(p.pos(expr), p.expr(expr.X), p.typeExpr(expr.Type)) + case *syntax.Operation: + if expr.Op == syntax.Add && expr.Y != nil { + return p.sum(expr) + } + x := p.expr(expr.X) + if expr.Y == nil { + pos, op := p.pos(expr), p.unOp(expr.Op) + switch op { + case ir.OADDR: + return typecheck.NodAddrAt(pos, x) + case ir.ODEREF: + return ir.NewStarExpr(pos, x) + } + return ir.NewUnaryExpr(pos, op, x) + } + + pos, op, y := p.pos(expr), p.binOp(expr.Op), p.expr(expr.Y) + switch op { + case ir.OANDAND, ir.OOROR: + return ir.NewLogicalExpr(pos, op, x, y) + } + return ir.NewBinaryExpr(pos, op, x, y) + case *syntax.CallExpr: + n := ir.NewCallExpr(p.pos(expr), ir.OCALL, p.expr(expr.Fun), p.exprs(expr.ArgList)) + n.IsDDD = expr.HasDots + return n + + case *syntax.ArrayType: + var len ir.Node + if expr.Len != nil { + len = p.expr(expr.Len) + } + return ir.NewArrayType(p.pos(expr), len, p.typeExpr(expr.Elem)) + case *syntax.SliceType: + return ir.NewSliceType(p.pos(expr), p.typeExpr(expr.Elem)) + case *syntax.DotsType: + t := ir.NewSliceType(p.pos(expr), p.typeExpr(expr.Elem)) + t.DDD = true + return t + case *syntax.StructType: + return p.structType(expr) + case *syntax.InterfaceType: + return p.interfaceType(expr) + case *syntax.FuncType: + return p.signature(nil, expr) + case *syntax.MapType: + return ir.NewMapType(p.pos(expr), + p.typeExpr(expr.Key), p.typeExpr(expr.Value)) + case *syntax.ChanType: + return ir.NewChanType(p.pos(expr), + p.typeExpr(expr.Elem), p.chanDir(expr.Dir)) + + case *syntax.TypeSwitchGuard: + var tag *ir.Ident + if expr.Lhs != nil { + tag = ir.NewIdent(p.pos(expr.Lhs), p.name(expr.Lhs)) + if ir.IsBlank(tag) { + base.Errorf("invalid variable name %v in type switch", tag) + } + } + return ir.NewTypeSwitchGuard(p.pos(expr), tag, p.expr(expr.X)) + } + panic("unhandled Expr") +} + +// sum efficiently handles very large summation expressions (such as +// in issue #16394). In particular, it avoids left recursion and +// collapses string literals. +func (p *noder) sum(x syntax.Expr) ir.Node { + // While we need to handle long sums with asymptotic + // efficiency, the vast majority of sums are very small: ~95% + // have only 2 or 3 operands, and ~99% of string literals are + // never concatenated. + + adds := make([]*syntax.Operation, 0, 2) + for { + add, ok := x.(*syntax.Operation) + if !ok || add.Op != syntax.Add || add.Y == nil { + break + } + adds = append(adds, add) + x = add.X + } + + // nstr is the current rightmost string literal in the + // summation (if any), and chunks holds its accumulated + // substrings. + // + // Consider the expression x + "a" + "b" + "c" + y. When we + // reach the string literal "a", we assign nstr to point to + // its corresponding Node and initialize chunks to {"a"}. + // Visiting the subsequent string literals "b" and "c", we + // simply append their values to chunks. Finally, when we + // reach the non-constant operand y, we'll join chunks to form + // "abc" and reassign the "a" string literal's value. + // + // N.B., we need to be careful about named string constants + // (indicated by Sym != nil) because 1) we can't modify their + // value, as doing so would affect other uses of the string + // constant, and 2) they may have types, which we need to + // handle correctly. For now, we avoid these problems by + // treating named string constants the same as non-constant + // operands. + var nstr ir.Node + chunks := make([]string, 0, 1) + + n := p.expr(x) + if ir.IsConst(n, constant.String) && n.Sym() == nil { + nstr = n + chunks = append(chunks, ir.StringVal(nstr)) + } + + for i := len(adds) - 1; i >= 0; i-- { + add := adds[i] + + r := p.expr(add.Y) + if ir.IsConst(r, constant.String) && r.Sym() == nil { + if nstr != nil { + // Collapse r into nstr instead of adding to n. + chunks = append(chunks, ir.StringVal(r)) + continue + } + + nstr = r + chunks = append(chunks, ir.StringVal(nstr)) + } else { + if len(chunks) > 1 { + nstr.SetVal(constant.MakeString(strings.Join(chunks, ""))) + } + nstr = nil + chunks = chunks[:0] + } + n = ir.NewBinaryExpr(p.pos(add), ir.OADD, n, r) + } + if len(chunks) > 1 { + nstr.SetVal(constant.MakeString(strings.Join(chunks, ""))) + } + + return n +} + +func (p *noder) typeExpr(typ syntax.Expr) ir.Ntype { + // TODO(mdempsky): Be stricter? typecheck should handle errors anyway. + n := p.expr(typ) + if n == nil { + return nil + } + return n.(ir.Ntype) +} + +func (p *noder) typeExprOrNil(typ syntax.Expr) ir.Ntype { + if typ != nil { + return p.typeExpr(typ) + } + return nil +} + +func (p *noder) chanDir(dir syntax.ChanDir) types.ChanDir { + switch dir { + case 0: + return types.Cboth + case syntax.SendOnly: + return types.Csend + case syntax.RecvOnly: + return types.Crecv + } + panic("unhandled ChanDir") +} + +func (p *noder) structType(expr *syntax.StructType) ir.Node { + l := make([]*ir.Field, 0, len(expr.FieldList)) + for i, field := range expr.FieldList { + p.setlineno(field) + var n *ir.Field + if field.Name == nil { + n = p.embedded(field.Type) + } else { + n = ir.NewField(p.pos(field), p.name(field.Name), p.typeExpr(field.Type), nil) + } + if i > 0 && expr.FieldList[i].Type == expr.FieldList[i-1].Type { + n.Ntype = l[i-1].Ntype + } + if i < len(expr.TagList) && expr.TagList[i] != nil { + n.Note = constant.StringVal(p.basicLit(expr.TagList[i])) + } + l = append(l, n) + } + + p.setlineno(expr) + return ir.NewStructType(p.pos(expr), l) +} + +func (p *noder) interfaceType(expr *syntax.InterfaceType) ir.Node { + l := make([]*ir.Field, 0, len(expr.MethodList)) + for _, method := range expr.MethodList { + p.setlineno(method) + var n *ir.Field + if method.Name == nil { + n = ir.NewField(p.pos(method), nil, importName(p.packname(method.Type)).(ir.Ntype), nil) + } else { + mname := p.name(method.Name) + if mname.IsBlank() { + base.Errorf("methods must have a unique non-blank name") + continue + } + sig := p.typeExpr(method.Type).(*ir.FuncType) + sig.Recv = fakeRecv() + n = ir.NewField(p.pos(method), mname, sig, nil) + } + l = append(l, n) + } + + return ir.NewInterfaceType(p.pos(expr), l) +} + +func (p *noder) packname(expr syntax.Expr) *types.Sym { + switch expr := expr.(type) { + case *syntax.Name: + name := p.name(expr) + if n := oldname(name); n.Name() != nil && n.Name().PkgName != nil { + n.Name().PkgName.Used = true + } + return name + case *syntax.SelectorExpr: + name := p.name(expr.X.(*syntax.Name)) + def := ir.AsNode(name.Def) + if def == nil { + base.Errorf("undefined: %v", name) + return name + } + var pkg *types.Pkg + if def.Op() != ir.OPACK { + base.Errorf("%v is not a package", name) + pkg = types.LocalPkg + } else { + def := def.(*ir.PkgName) + def.Used = true + pkg = def.Pkg + } + return pkg.Lookup(expr.Sel.Value) + } + panic(fmt.Sprintf("unexpected packname: %#v", expr)) +} + +func (p *noder) embedded(typ syntax.Expr) *ir.Field { + pos := p.pos(syntax.StartPos(typ)) + + op, isStar := typ.(*syntax.Operation) + if isStar { + if op.Op != syntax.Mul || op.Y != nil { + panic("unexpected Operation") + } + typ = op.X + } + + sym := p.packname(typ) + n := ir.NewField(pos, typecheck.Lookup(sym.Name), importName(sym).(ir.Ntype), nil) + n.Embedded = true + + if isStar { + n.Ntype = ir.NewStarExpr(pos, n.Ntype) + } + return n +} + +func (p *noder) stmts(stmts []syntax.Stmt) []ir.Node { + return p.stmtsFall(stmts, false) +} + +func (p *noder) stmtsFall(stmts []syntax.Stmt, fallOK bool) []ir.Node { + var nodes []ir.Node + for i, stmt := range stmts { + s := p.stmtFall(stmt, fallOK && i+1 == len(stmts)) + if s == nil { + } else if s.Op() == ir.OBLOCK && len(s.(*ir.BlockStmt).List) > 0 { + // Inline non-empty block. + // Empty blocks must be preserved for CheckReturn. + nodes = append(nodes, s.(*ir.BlockStmt).List...) + } else { + nodes = append(nodes, s) + } + } + return nodes +} + +func (p *noder) stmt(stmt syntax.Stmt) ir.Node { + return p.stmtFall(stmt, false) +} + +func (p *noder) stmtFall(stmt syntax.Stmt, fallOK bool) ir.Node { + p.setlineno(stmt) + switch stmt := stmt.(type) { + case nil, *syntax.EmptyStmt: + return nil + case *syntax.LabeledStmt: + return p.labeledStmt(stmt, fallOK) + case *syntax.BlockStmt: + l := p.blockStmt(stmt) + if len(l) == 0 { + // TODO(mdempsky): Line number? + return ir.NewBlockStmt(base.Pos, nil) + } + return ir.NewBlockStmt(src.NoXPos, l) + case *syntax.ExprStmt: + return p.wrapname(stmt, p.expr(stmt.X)) + case *syntax.SendStmt: + return ir.NewSendStmt(p.pos(stmt), p.expr(stmt.Chan), p.expr(stmt.Value)) + case *syntax.DeclStmt: + return ir.NewBlockStmt(src.NoXPos, p.decls(stmt.DeclList)) + case *syntax.AssignStmt: + if stmt.Rhs == nil { + pos := p.pos(stmt) + n := ir.NewAssignOpStmt(pos, p.binOp(stmt.Op), p.expr(stmt.Lhs), ir.NewBasicLit(pos, one)) + n.IncDec = true + return n + } + + if stmt.Op != 0 && stmt.Op != syntax.Def { + n := ir.NewAssignOpStmt(p.pos(stmt), p.binOp(stmt.Op), p.expr(stmt.Lhs), p.expr(stmt.Rhs)) + return n + } + + rhs := p.exprList(stmt.Rhs) + if list, ok := stmt.Lhs.(*syntax.ListExpr); ok && len(list.ElemList) != 1 || len(rhs) != 1 { + n := ir.NewAssignListStmt(p.pos(stmt), ir.OAS2, nil, nil) + n.Def = stmt.Op == syntax.Def + n.Lhs = p.assignList(stmt.Lhs, n, n.Def) + n.Rhs = rhs + return n + } + + n := ir.NewAssignStmt(p.pos(stmt), nil, nil) + n.Def = stmt.Op == syntax.Def + n.X = p.assignList(stmt.Lhs, n, n.Def)[0] + n.Y = rhs[0] + return n + + case *syntax.BranchStmt: + var op ir.Op + switch stmt.Tok { + case syntax.Break: + op = ir.OBREAK + case syntax.Continue: + op = ir.OCONTINUE + case syntax.Fallthrough: + if !fallOK { + base.Errorf("fallthrough statement out of place") + } + op = ir.OFALL + case syntax.Goto: + op = ir.OGOTO + default: + panic("unhandled BranchStmt") + } + var sym *types.Sym + if stmt.Label != nil { + sym = p.name(stmt.Label) + } + return ir.NewBranchStmt(p.pos(stmt), op, sym) + case *syntax.CallStmt: + var op ir.Op + switch stmt.Tok { + case syntax.Defer: + op = ir.ODEFER + case syntax.Go: + op = ir.OGO + default: + panic("unhandled CallStmt") + } + return ir.NewGoDeferStmt(p.pos(stmt), op, p.expr(stmt.Call)) + case *syntax.ReturnStmt: + n := ir.NewReturnStmt(p.pos(stmt), p.exprList(stmt.Results)) + if len(n.Results) == 0 && ir.CurFunc != nil { + for _, ln := range ir.CurFunc.Dcl { + if ln.Class == ir.PPARAM { + continue + } + if ln.Class != ir.PPARAMOUT { + break + } + if ln.Sym().Def != ln { + base.Errorf("%s is shadowed during return", ln.Sym().Name) + } + } + } + return n + case *syntax.IfStmt: + return p.ifStmt(stmt) + case *syntax.ForStmt: + return p.forStmt(stmt) + case *syntax.SwitchStmt: + return p.switchStmt(stmt) + case *syntax.SelectStmt: + return p.selectStmt(stmt) + } + panic("unhandled Stmt") +} + +func (p *noder) assignList(expr syntax.Expr, defn ir.InitNode, colas bool) []ir.Node { + if !colas { + return p.exprList(expr) + } + + var exprs []syntax.Expr + if list, ok := expr.(*syntax.ListExpr); ok { + exprs = list.ElemList + } else { + exprs = []syntax.Expr{expr} + } + + res := make([]ir.Node, len(exprs)) + seen := make(map[*types.Sym]bool, len(exprs)) + + newOrErr := false + for i, expr := range exprs { + p.setlineno(expr) + res[i] = ir.BlankNode + + name, ok := expr.(*syntax.Name) + if !ok { + p.errorAt(expr.Pos(), "non-name %v on left side of :=", p.expr(expr)) + newOrErr = true + continue + } + + sym := p.name(name) + if sym.IsBlank() { + continue + } + + if seen[sym] { + p.errorAt(expr.Pos(), "%v repeated on left side of :=", sym) + newOrErr = true + continue + } + seen[sym] = true + + if sym.Block == types.Block { + res[i] = oldname(sym) + continue + } + + newOrErr = true + n := typecheck.NewName(sym) + typecheck.Declare(n, typecheck.DeclContext) + n.Defn = defn + defn.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, n)) + res[i] = n + } + + if !newOrErr { + base.ErrorfAt(defn.Pos(), "no new variables on left side of :=") + } + return res +} + +func (p *noder) blockStmt(stmt *syntax.BlockStmt) []ir.Node { + p.openScope(stmt.Pos()) + nodes := p.stmts(stmt.List) + p.closeScope(stmt.Rbrace) + return nodes +} + +func (p *noder) ifStmt(stmt *syntax.IfStmt) ir.Node { + p.openScope(stmt.Pos()) + init := p.stmt(stmt.Init) + n := ir.NewIfStmt(p.pos(stmt), p.expr(stmt.Cond), p.blockStmt(stmt.Then), nil) + if init != nil { + n.SetInit([]ir.Node{init}) + } + if stmt.Else != nil { + e := p.stmt(stmt.Else) + if e.Op() == ir.OBLOCK { + e := e.(*ir.BlockStmt) + n.Else = e.List + } else { + n.Else = []ir.Node{e} + } + } + p.closeAnotherScope() + return n +} + +func (p *noder) forStmt(stmt *syntax.ForStmt) ir.Node { + p.openScope(stmt.Pos()) + if r, ok := stmt.Init.(*syntax.RangeClause); ok { + if stmt.Cond != nil || stmt.Post != nil { + panic("unexpected RangeClause") + } + + n := ir.NewRangeStmt(p.pos(r), nil, nil, p.expr(r.X), nil) + if r.Lhs != nil { + n.Def = r.Def + lhs := p.assignList(r.Lhs, n, n.Def) + n.Key = lhs[0] + if len(lhs) > 1 { + n.Value = lhs[1] + } + } + n.Body = p.blockStmt(stmt.Body) + p.closeAnotherScope() + return n + } + + n := ir.NewForStmt(p.pos(stmt), p.stmt(stmt.Init), p.expr(stmt.Cond), p.stmt(stmt.Post), p.blockStmt(stmt.Body)) + p.closeAnotherScope() + return n +} + +func (p *noder) switchStmt(stmt *syntax.SwitchStmt) ir.Node { + p.openScope(stmt.Pos()) + + init := p.stmt(stmt.Init) + n := ir.NewSwitchStmt(p.pos(stmt), p.expr(stmt.Tag), nil) + if init != nil { + n.SetInit([]ir.Node{init}) + } + + var tswitch *ir.TypeSwitchGuard + if l := n.Tag; l != nil && l.Op() == ir.OTYPESW { + tswitch = l.(*ir.TypeSwitchGuard) + } + n.Cases = p.caseClauses(stmt.Body, tswitch, stmt.Rbrace) + + p.closeScope(stmt.Rbrace) + return n +} + +func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *ir.TypeSwitchGuard, rbrace syntax.Pos) []*ir.CaseClause { + nodes := make([]*ir.CaseClause, 0, len(clauses)) + for i, clause := range clauses { + p.setlineno(clause) + if i > 0 { + p.closeScope(clause.Pos()) + } + p.openScope(clause.Pos()) + + n := ir.NewCaseStmt(p.pos(clause), p.exprList(clause.Cases), nil) + if tswitch != nil && tswitch.Tag != nil { + nn := typecheck.NewName(tswitch.Tag.Sym()) + typecheck.Declare(nn, typecheck.DeclContext) + n.Var = nn + // keep track of the instances for reporting unused + nn.Defn = tswitch + } + + // Trim trailing empty statements. We omit them from + // the Node AST anyway, and it's easier to identify + // out-of-place fallthrough statements without them. + body := clause.Body + for len(body) > 0 { + if _, ok := body[len(body)-1].(*syntax.EmptyStmt); !ok { + break + } + body = body[:len(body)-1] + } + + n.Body = p.stmtsFall(body, true) + if l := len(n.Body); l > 0 && n.Body[l-1].Op() == ir.OFALL { + if tswitch != nil { + base.Errorf("cannot fallthrough in type switch") + } + if i+1 == len(clauses) { + base.Errorf("cannot fallthrough final case in switch") + } + } + + nodes = append(nodes, n) + } + if len(clauses) > 0 { + p.closeScope(rbrace) + } + return nodes +} + +func (p *noder) selectStmt(stmt *syntax.SelectStmt) ir.Node { + return ir.NewSelectStmt(p.pos(stmt), p.commClauses(stmt.Body, stmt.Rbrace)) +} + +func (p *noder) commClauses(clauses []*syntax.CommClause, rbrace syntax.Pos) []*ir.CommClause { + nodes := make([]*ir.CommClause, len(clauses)) + for i, clause := range clauses { + p.setlineno(clause) + if i > 0 { + p.closeScope(clause.Pos()) + } + p.openScope(clause.Pos()) + + nodes[i] = ir.NewCommStmt(p.pos(clause), p.stmt(clause.Comm), p.stmts(clause.Body)) + } + if len(clauses) > 0 { + p.closeScope(rbrace) + } + return nodes +} + +func (p *noder) labeledStmt(label *syntax.LabeledStmt, fallOK bool) ir.Node { + sym := p.name(label.Label) + lhs := ir.NewLabelStmt(p.pos(label), sym) + + var ls ir.Node + if label.Stmt != nil { // TODO(mdempsky): Should always be present. + ls = p.stmtFall(label.Stmt, fallOK) + // Attach label directly to control statement too. + if ls != nil { + switch ls.Op() { + case ir.OFOR: + ls := ls.(*ir.ForStmt) + ls.Label = sym + case ir.ORANGE: + ls := ls.(*ir.RangeStmt) + ls.Label = sym + case ir.OSWITCH: + ls := ls.(*ir.SwitchStmt) + ls.Label = sym + case ir.OSELECT: + ls := ls.(*ir.SelectStmt) + ls.Label = sym + } + } + } + + l := []ir.Node{lhs} + if ls != nil { + if ls.Op() == ir.OBLOCK { + ls := ls.(*ir.BlockStmt) + l = append(l, ls.List...) + } else { + l = append(l, ls) + } + } + return ir.NewBlockStmt(src.NoXPos, l) +} + +var unOps = [...]ir.Op{ + syntax.Recv: ir.ORECV, + syntax.Mul: ir.ODEREF, + syntax.And: ir.OADDR, + + syntax.Not: ir.ONOT, + syntax.Xor: ir.OBITNOT, + syntax.Add: ir.OPLUS, + syntax.Sub: ir.ONEG, +} + +func (p *noder) unOp(op syntax.Operator) ir.Op { + if uint64(op) >= uint64(len(unOps)) || unOps[op] == 0 { + panic("invalid Operator") + } + return unOps[op] +} + +var binOps = [...]ir.Op{ + syntax.OrOr: ir.OOROR, + syntax.AndAnd: ir.OANDAND, + + syntax.Eql: ir.OEQ, + syntax.Neq: ir.ONE, + syntax.Lss: ir.OLT, + syntax.Leq: ir.OLE, + syntax.Gtr: ir.OGT, + syntax.Geq: ir.OGE, + + syntax.Add: ir.OADD, + syntax.Sub: ir.OSUB, + syntax.Or: ir.OOR, + syntax.Xor: ir.OXOR, + + syntax.Mul: ir.OMUL, + syntax.Div: ir.ODIV, + syntax.Rem: ir.OMOD, + syntax.And: ir.OAND, + syntax.AndNot: ir.OANDNOT, + syntax.Shl: ir.OLSH, + syntax.Shr: ir.ORSH, +} + +func (p *noder) binOp(op syntax.Operator) ir.Op { + if uint64(op) >= uint64(len(binOps)) || binOps[op] == 0 { + panic("invalid Operator") + } + return binOps[op] +} + +// checkLangCompat reports an error if the representation of a numeric +// literal is not compatible with the current language version. +func checkLangCompat(lit *syntax.BasicLit) { + s := lit.Value + if len(s) <= 2 || types.AllowsGoVersion(types.LocalPkg, 1, 13) { + return + } + // len(s) > 2 + if strings.Contains(s, "_") { + base.ErrorfVers("go1.13", "underscores in numeric literals") + return + } + if s[0] != '0' { + return + } + radix := s[1] + if radix == 'b' || radix == 'B' { + base.ErrorfVers("go1.13", "binary literals") + return + } + if radix == 'o' || radix == 'O' { + base.ErrorfVers("go1.13", "0o/0O-style octal literals") + return + } + if lit.Kind != syntax.IntLit && (radix == 'x' || radix == 'X') { + base.ErrorfVers("go1.13", "hexadecimal floating-point literals") + } +} + +func (p *noder) basicLit(lit *syntax.BasicLit) constant.Value { + // We don't use the errors of the conversion routines to determine + // if a literal string is valid because the conversion routines may + // accept a wider syntax than the language permits. Rely on lit.Bad + // instead. + if lit.Bad { + return constant.MakeUnknown() + } + + switch lit.Kind { + case syntax.IntLit, syntax.FloatLit, syntax.ImagLit: + checkLangCompat(lit) + // The max. mantissa precision for untyped numeric values + // is 512 bits, or 4048 bits for each of the two integer + // parts of a fraction for floating-point numbers that are + // represented accurately in the go/constant package. + // Constant literals that are longer than this many bits + // are not meaningful; and excessively long constants may + // consume a lot of space and time for a useless conversion. + // Cap constant length with a generous upper limit that also + // allows for separators between all digits. + const limit = 10000 + if len(lit.Value) > limit { + p.errorAt(lit.Pos(), "excessively long constant: %s... (%d chars)", lit.Value[:10], len(lit.Value)) + return constant.MakeUnknown() + } + } + + v := constant.MakeFromLiteral(lit.Value, tokenForLitKind[lit.Kind], 0) + if v.Kind() == constant.Unknown { + // TODO(mdempsky): Better error message? + p.errorAt(lit.Pos(), "malformed constant: %s", lit.Value) + } + + return v +} + +var tokenForLitKind = [...]token.Token{ + syntax.IntLit: token.INT, + syntax.RuneLit: token.CHAR, + syntax.FloatLit: token.FLOAT, + syntax.ImagLit: token.IMAG, + syntax.StringLit: token.STRING, +} + +func (p *noder) name(name *syntax.Name) *types.Sym { + return typecheck.Lookup(name.Value) +} + +func (p *noder) mkname(name *syntax.Name) ir.Node { + // TODO(mdempsky): Set line number? + return mkname(p.name(name)) +} + +func wrapname(pos src.XPos, x ir.Node) ir.Node { + // These nodes do not carry line numbers. + // Introduce a wrapper node to give them the correct line. + switch x.Op() { + case ir.OTYPE, ir.OLITERAL: + if x.Sym() == nil { + break + } + fallthrough + case ir.ONAME, ir.ONONAME, ir.OPACK: + p := ir.NewParenExpr(pos, x) + p.SetImplicit(true) + return p + } + return x +} + +func (p *noder) wrapname(n syntax.Node, x ir.Node) ir.Node { + return wrapname(p.pos(n), x) +} + +func (p *noder) setlineno(n syntax.Node) { + if n != nil { + base.Pos = p.pos(n) + } +} + +// error is called concurrently if files are parsed concurrently. +func (p *noder) error(err error) { + p.err <- err.(syntax.Error) +} + +// pragmas that are allowed in the std lib, but don't have +// a syntax.Pragma value (see lex.go) associated with them. +var allowedStdPragmas = map[string]bool{ + "go:cgo_export_static": true, + "go:cgo_export_dynamic": true, + "go:cgo_import_static": true, + "go:cgo_import_dynamic": true, + "go:cgo_ldflag": true, + "go:cgo_dynamic_linker": true, + "go:embed": true, + "go:generate": true, +} + +// *pragmas is the value stored in a syntax.pragmas during parsing. +type pragmas struct { + Flag ir.PragmaFlag // collected bits + Pos []pragmaPos // position of each individual flag + Embeds []pragmaEmbed +} + +type pragmaPos struct { + Flag ir.PragmaFlag + Pos syntax.Pos +} + +type pragmaEmbed struct { + Pos syntax.Pos + Patterns []string +} + +func (p *noder) checkUnused(pragma *pragmas) { + for _, pos := range pragma.Pos { + if pos.Flag&pragma.Flag != 0 { + p.errorAt(pos.Pos, "misplaced compiler directive") + } + } + if len(pragma.Embeds) > 0 { + for _, e := range pragma.Embeds { + p.errorAt(e.Pos, "misplaced go:embed directive") + } + } +} + +func (p *noder) checkUnusedDuringParse(pragma *pragmas) { + for _, pos := range pragma.Pos { + if pos.Flag&pragma.Flag != 0 { + p.error(syntax.Error{Pos: pos.Pos, Msg: "misplaced compiler directive"}) + } + } + if len(pragma.Embeds) > 0 { + for _, e := range pragma.Embeds { + p.error(syntax.Error{Pos: e.Pos, Msg: "misplaced go:embed directive"}) + } + } +} + +// pragma is called concurrently if files are parsed concurrently. +func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.Pragma) syntax.Pragma { + pragma, _ := old.(*pragmas) + if pragma == nil { + pragma = new(pragmas) + } + + if text == "" { + // unused pragma; only called with old != nil. + p.checkUnusedDuringParse(pragma) + return nil + } + + if strings.HasPrefix(text, "line ") { + // line directives are handled by syntax package + panic("unreachable") + } + + if !blankLine { + // directive must be on line by itself + p.error(syntax.Error{Pos: pos, Msg: "misplaced compiler directive"}) + return pragma + } + + switch { + case strings.HasPrefix(text, "go:linkname "): + f := strings.Fields(text) + if !(2 <= len(f) && len(f) <= 3) { + p.error(syntax.Error{Pos: pos, Msg: "usage: //go:linkname localname [linkname]"}) + break + } + // The second argument is optional. If omitted, we use + // the default object symbol name for this and + // linkname only serves to mark this symbol as + // something that may be referenced via the object + // symbol name from another package. + var target string + if len(f) == 3 { + target = f[2] + } else if base.Ctxt.Pkgpath != "" { + // Use the default object symbol name if the + // user didn't provide one. + target = objabi.PathToPrefix(base.Ctxt.Pkgpath) + "." + f[1] + } else { + p.error(syntax.Error{Pos: pos, Msg: "//go:linkname requires linkname argument or -p compiler flag"}) + break + } + p.linknames = append(p.linknames, linkname{pos, f[1], target}) + + case text == "go:embed", strings.HasPrefix(text, "go:embed "): + args, err := parseGoEmbed(text[len("go:embed"):]) + if err != nil { + p.error(syntax.Error{Pos: pos, Msg: err.Error()}) + } + if len(args) == 0 { + p.error(syntax.Error{Pos: pos, Msg: "usage: //go:embed pattern..."}) + break + } + pragma.Embeds = append(pragma.Embeds, pragmaEmbed{pos, args}) + + case strings.HasPrefix(text, "go:cgo_import_dynamic "): + // This is permitted for general use because Solaris + // code relies on it in golang.org/x/sys/unix and others. + fields := pragmaFields(text) + if len(fields) >= 4 { + lib := strings.Trim(fields[3], `"`) + if lib != "" && !safeArg(lib) && !isCgoGeneratedFile(pos) { + p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("invalid library name %q in cgo_import_dynamic directive", lib)}) + } + p.pragcgo(pos, text) + pragma.Flag |= pragmaFlag("go:cgo_import_dynamic") + break + } + fallthrough + case strings.HasPrefix(text, "go:cgo_"): + // For security, we disallow //go:cgo_* directives other + // than cgo_import_dynamic outside cgo-generated files. + // Exception: they are allowed in the standard library, for runtime and syscall. + if !isCgoGeneratedFile(pos) && !base.Flag.Std { + p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in cgo-generated code", text)}) + } + p.pragcgo(pos, text) + fallthrough // because of //go:cgo_unsafe_args + default: + verb := text + if i := strings.Index(text, " "); i >= 0 { + verb = verb[:i] + } + flag := pragmaFlag(verb) + const runtimePragmas = ir.Systemstack | ir.Nowritebarrier | ir.Nowritebarrierrec | ir.Yeswritebarrierrec + if !base.Flag.CompilingRuntime && flag&runtimePragmas != 0 { + p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in runtime", verb)}) + } + if flag == 0 && !allowedStdPragmas[verb] && base.Flag.Std { + p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is not allowed in the standard library", verb)}) + } + pragma.Flag |= flag + pragma.Pos = append(pragma.Pos, pragmaPos{flag, pos}) + } + + return pragma +} + +// isCgoGeneratedFile reports whether pos is in a file +// generated by cgo, which is to say a file with name +// beginning with "_cgo_". Such files are allowed to +// contain cgo directives, and for security reasons +// (primarily misuse of linker flags), other files are not. +// See golang.org/issue/23672. +func isCgoGeneratedFile(pos syntax.Pos) bool { + return strings.HasPrefix(filepath.Base(trimFilename(pos.Base())), "_cgo_") +} + +// safeArg reports whether arg is a "safe" command-line argument, +// meaning that when it appears in a command-line, it probably +// doesn't have some special meaning other than its own name. +// This is copied from SafeArg in cmd/go/internal/load/pkg.go. +func safeArg(name string) bool { + if name == "" { + return false + } + c := name[0] + return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '.' || c == '_' || c == '/' || c >= utf8.RuneSelf +} + +func mkname(sym *types.Sym) ir.Node { + n := oldname(sym) + if n.Name() != nil && n.Name().PkgName != nil { + n.Name().PkgName.Used = true + } + return n +} + +// parseGoEmbed parses the text following "//go:embed" to extract the glob patterns. +// It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings. +// go/build/read.go also processes these strings and contains similar logic. +func parseGoEmbed(args string) ([]string, error) { + var list []string + for args = strings.TrimSpace(args); args != ""; args = strings.TrimSpace(args) { + var path string + Switch: + switch args[0] { + default: + i := len(args) + for j, c := range args { + if unicode.IsSpace(c) { + i = j + break + } + } + path = args[:i] + args = args[i:] + + case '`': + i := strings.Index(args[1:], "`") + if i < 0 { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) + } + path = args[1 : 1+i] + args = args[1+i+1:] + + case '"': + i := 1 + for ; i < len(args); i++ { + if args[i] == '\\' { + i++ + continue + } + if args[i] == '"' { + q, err := strconv.Unquote(args[:i+1]) + if err != nil { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1]) + } + path = q + args = args[i+1:] + break Switch + } + } + if i >= len(args) { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) + } + } + + if args != "" { + r, _ := utf8.DecodeRuneInString(args) + if !unicode.IsSpace(r) { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) + } + } + list = append(list, path) + } + return list, nil +} + +func fakeRecv() *ir.Field { + return ir.NewField(base.Pos, nil, nil, types.FakeRecvType()) +} + +func (p *noder) funcLit(expr *syntax.FuncLit) ir.Node { + fn := ir.NewClosureFunc(p.pos(expr), ir.CurFunc != nil) + fn.Nname.Ntype = p.typeExpr(expr.Type) + + p.funcBody(fn, expr.Body) + + ir.FinishCaptureNames(base.Pos, ir.CurFunc, fn) + + return fn.OClosure +} + +// A function named init is a special case. +// It is called by the initialization before main is run. +// To make it unique within a package and also uncallable, +// the name, normally "pkg.init", is altered to "pkg.init.0". +var renameinitgen int + +func renameinit() *types.Sym { + s := typecheck.LookupNum("init.", renameinitgen) + renameinitgen++ + return s +} + +// oldname returns the Node that declares symbol s in the current scope. +// If no such Node currently exists, an ONONAME Node is returned instead. +// Automatically creates a new closure variable if the referenced symbol was +// declared in a different (containing) function. +func oldname(s *types.Sym) ir.Node { + if s.Pkg != types.LocalPkg { + return ir.NewIdent(base.Pos, s) + } + + n := ir.AsNode(s.Def) + if n == nil { + // Maybe a top-level declaration will come along later to + // define s. resolve will check s.Def again once all input + // source has been processed. + return ir.NewIdent(base.Pos, s) + } + + if n, ok := n.(*ir.Name); ok { + // TODO(rsc): If there is an outer variable x and we + // are parsing x := 5 inside the closure, until we get to + // the := it looks like a reference to the outer x so we'll + // make x a closure variable unnecessarily. + return ir.CaptureName(base.Pos, ir.CurFunc, n) + } + + return n +} + +func varEmbed(makeXPos func(syntax.Pos) src.XPos, name *ir.Name, decl *syntax.VarDecl, pragma *pragmas, haveEmbed bool) { + pragmaEmbeds := pragma.Embeds + pragma.Embeds = nil + if len(pragmaEmbeds) == 0 { + return + } + + if err := checkEmbed(decl, haveEmbed, typecheck.DeclContext != ir.PEXTERN); err != nil { + base.ErrorfAt(makeXPos(pragmaEmbeds[0].Pos), "%s", err) + return + } + + var embeds []ir.Embed + for _, e := range pragmaEmbeds { + embeds = append(embeds, ir.Embed{Pos: makeXPos(e.Pos), Patterns: e.Patterns}) + } + typecheck.Target.Embeds = append(typecheck.Target.Embeds, name) + name.Embed = &embeds +} + +func checkEmbed(decl *syntax.VarDecl, haveEmbed, withinFunc bool) error { + switch { + case !haveEmbed: + return errors.New("go:embed only allowed in Go files that import \"embed\"") + case len(decl.NameList) > 1: + return errors.New("go:embed cannot apply to multiple vars") + case decl.Values != nil: + return errors.New("go:embed cannot apply to var with initializer") + case decl.Type == nil: + // Should not happen, since Values == nil now. + return errors.New("go:embed cannot apply to var without type") + case withinFunc: + return errors.New("go:embed cannot apply to var inside func") + case !types.AllowsGoVersion(types.LocalPkg, 1, 16): + return fmt.Errorf("go:embed requires go1.16 or later (-lang was set to %s; check go.mod)", base.Flag.Lang) + + default: + return nil + } +} diff --git a/src/cmd/compile/internal/noder/object.go b/src/cmd/compile/internal/noder/object.go new file mode 100644 index 0000000..37a995b --- /dev/null +++ b/src/cmd/compile/internal/noder/object.go @@ -0,0 +1,206 @@ +// Copyright 2021 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 noder + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/syntax" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/compile/internal/types2" + "cmd/internal/src" +) + +func (g *irgen) def(name *syntax.Name) (*ir.Name, types2.Object) { + obj, ok := g.info.Defs[name] + if !ok { + base.FatalfAt(g.pos(name), "unknown name %v", name) + } + return g.obj(obj), obj +} + +// use returns the Name or InstExpr node associated with the use of name, +// possibly instantiated by type arguments. The returned node will have +// the correct type and be marked as typechecked. +func (g *irgen) use(name *syntax.Name) ir.Node { + obj2, ok := g.info.Uses[name] + if !ok { + base.FatalfAt(g.pos(name), "unknown name %v", name) + } + obj := ir.CaptureName(g.pos(name), ir.CurFunc, g.obj(obj2)) + if obj.Defn != nil && obj.Defn.Op() == ir.ONAME { + // If CaptureName created a closure variable, then transfer the + // type of the captured name to the new closure variable. + obj.SetTypecheck(1) + obj.SetType(obj.Defn.Type()) + } + + if obj.Class == ir.PFUNC { + if inst, ok := g.info.Instances[name]; ok { + // This is the case where inferring types required the + // types of the function arguments. + targs := make([]ir.Node, inst.TypeArgs.Len()) + for i := range targs { + targs[i] = ir.TypeNode(g.typ(inst.TypeArgs.At(i))) + } + typ := g.substType(obj.Type(), obj.Type().TParams(), targs) + return typed(typ, ir.NewInstExpr(g.pos(name), ir.OFUNCINST, obj, targs)) + } + } + + return obj +} + +// obj returns the Name that represents the given object. If no such Name exists +// yet, it will be implicitly created. The returned node will have the correct +// type and be marked as typechecked. +// +// For objects declared at function scope, ir.CurFunc must already be +// set to the respective function when the Name is created. +func (g *irgen) obj(obj types2.Object) *ir.Name { + // For imported objects, we use iimport directly instead of mapping + // the types2 representation. + if obj.Pkg() != g.self { + if sig, ok := obj.Type().(*types2.Signature); ok && sig.Recv() != nil { + // We can't import a method by name - must import the type + // and access the method from it. + base.FatalfAt(g.pos(obj), "tried to import a method directly") + } + sym := g.sym(obj) + if sym.Def != nil { + return sym.Def.(*ir.Name) + } + n := typecheck.Resolve(ir.NewIdent(src.NoXPos, sym)) + if n, ok := n.(*ir.Name); ok { + n.SetTypecheck(1) + return n + } + base.FatalfAt(g.pos(obj), "failed to resolve %v", obj) + } + + if name, ok := g.objs[obj]; ok { + return name // previously mapped + } + + var name *ir.Name + pos := g.pos(obj) + + class := typecheck.DeclContext + if obj.Parent() == g.self.Scope() { + class = ir.PEXTERN // forward reference to package-block declaration + } + + // "You are in a maze of twisting little passages, all different." + switch obj := obj.(type) { + case *types2.Const: + name = g.objCommon(pos, ir.OLITERAL, g.sym(obj), class, g.typ(obj.Type())) + + case *types2.Func: + sig := obj.Type().(*types2.Signature) + var sym *types.Sym + var typ *types.Type + if recv := sig.Recv(); recv == nil { + if obj.Name() == "init" { + sym = renameinit() + } else { + sym = g.sym(obj) + } + typ = g.typ(sig) + } else { + sym = g.selector(obj) + if !sym.IsBlank() { + sym = ir.MethodSym(g.typ(recv.Type()), sym) + } + typ = g.signature(g.param(recv), sig) + } + name = g.objCommon(pos, ir.ONAME, sym, ir.PFUNC, typ) + + case *types2.TypeName: + if obj.IsAlias() { + name = g.objCommon(pos, ir.OTYPE, g.sym(obj), class, g.typ(obj.Type())) + name.SetAlias(true) + } else { + name = ir.NewDeclNameAt(pos, ir.OTYPE, g.sym(obj)) + g.objFinish(name, class, types.NewNamed(name)) + } + + case *types2.Var: + sym := g.sym(obj) + if class == ir.PPARAMOUT && (sym == nil || sym.IsBlank()) { + // Backend needs names for result parameters, + // even if they're anonymous or blank. + nresults := 0 + for _, n := range ir.CurFunc.Dcl { + if n.Class == ir.PPARAMOUT { + nresults++ + } + } + if sym == nil { + sym = typecheck.LookupNum("~r", nresults) // 'r' for "result" + } else { + sym = typecheck.LookupNum("~b", nresults) // 'b' for "blank" + } + } + name = g.objCommon(pos, ir.ONAME, sym, class, g.typ(obj.Type())) + + default: + g.unhandled("object", obj) + } + + g.objs[obj] = name + name.SetTypecheck(1) + return name +} + +func (g *irgen) objCommon(pos src.XPos, op ir.Op, sym *types.Sym, class ir.Class, typ *types.Type) *ir.Name { + name := ir.NewDeclNameAt(pos, op, sym) + g.objFinish(name, class, typ) + return name +} + +func (g *irgen) objFinish(name *ir.Name, class ir.Class, typ *types.Type) { + sym := name.Sym() + + name.SetType(typ) + name.Class = class + if name.Class == ir.PFUNC { + sym.SetFunc(true) + } + + name.SetTypecheck(1) + name.SetWalkdef(1) + + if ir.IsBlank(name) { + return + } + + switch class { + case ir.PEXTERN: + g.target.Externs = append(g.target.Externs, name) + fallthrough + case ir.PFUNC: + sym.Def = name + if name.Class == ir.PFUNC && name.Type().Recv() != nil { + break // methods are exported with their receiver type + } + if types.IsExported(sym.Name) { + // Generic functions can be marked for export here, even + // though they will not be compiled until instantiated. + typecheck.Export(name) + } + if base.Flag.AsmHdr != "" && !name.Sym().Asm() { + name.Sym().SetAsm(true) + g.target.Asms = append(g.target.Asms, name) + } + + default: + // Function-scoped declaration. + name.Curfn = ir.CurFunc + if name.Op() == ir.ONAME { + ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, name) + } + } +} diff --git a/src/cmd/compile/internal/noder/posmap.go b/src/cmd/compile/internal/noder/posmap.go new file mode 100644 index 0000000..f22628f --- /dev/null +++ b/src/cmd/compile/internal/noder/posmap.go @@ -0,0 +1,85 @@ +// Copyright 2021 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 noder + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/syntax" + "cmd/internal/src" +) + +// A posMap handles mapping from syntax.Pos to src.XPos. +type posMap struct { + bases map[*syntax.PosBase]*src.PosBase + cache struct { + last *syntax.PosBase + base *src.PosBase + } +} + +type poser interface{ Pos() syntax.Pos } +type ender interface{ End() syntax.Pos } + +func (m *posMap) pos(p poser) src.XPos { return m.makeXPos(p.Pos()) } +func (m *posMap) end(p ender) src.XPos { return m.makeXPos(p.End()) } + +func (m *posMap) makeXPos(pos syntax.Pos) src.XPos { + if !pos.IsKnown() { + // TODO(mdempsky): Investigate restoring base.Fatalf. + return src.NoXPos + } + + posBase := m.makeSrcPosBase(pos.Base()) + return base.Ctxt.PosTable.XPos(src.MakePos(posBase, pos.Line(), pos.Col())) +} + +// makeSrcPosBase translates from a *syntax.PosBase to a *src.PosBase. +func (m *posMap) makeSrcPosBase(b0 *syntax.PosBase) *src.PosBase { + // fast path: most likely PosBase hasn't changed + if m.cache.last == b0 { + return m.cache.base + } + + b1, ok := m.bases[b0] + if !ok { + fn := b0.Filename() + absfn := trimFilename(b0) + + if b0.IsFileBase() { + b1 = src.NewFileBase(fn, absfn) + } else { + // line directive base + p0 := b0.Pos() + p0b := p0.Base() + if p0b == b0 { + panic("infinite recursion in makeSrcPosBase") + } + p1 := src.MakePos(m.makeSrcPosBase(p0b), p0.Line(), p0.Col()) + b1 = src.NewLinePragmaBase(p1, fn, absfn, b0.Line(), b0.Col()) + } + if m.bases == nil { + m.bases = make(map[*syntax.PosBase]*src.PosBase) + } + m.bases[b0] = b1 + } + + // update cache + m.cache.last = b0 + m.cache.base = b1 + + return b1 +} + +func (m *posMap) join(other *posMap) { + if m.bases == nil { + m.bases = make(map[*syntax.PosBase]*src.PosBase) + } + for k, v := range other.bases { + if m.bases[k] != nil { + base.Fatalf("duplicate posmap bases") + } + m.bases[k] = v + } +} diff --git a/src/cmd/compile/internal/noder/quirks.go b/src/cmd/compile/internal/noder/quirks.go new file mode 100644 index 0000000..914c5d2 --- /dev/null +++ b/src/cmd/compile/internal/noder/quirks.go @@ -0,0 +1,450 @@ +// UNREVIEWED + +// Copyright 2021 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 noder + +import ( + "fmt" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/syntax" + "cmd/compile/internal/types2" + "cmd/internal/src" +) + +// This file defines helper functions useful for satisfying toolstash +// -cmp when compared against the legacy frontend behavior, but can be +// removed after that's no longer a concern. + +// quirksMode controls whether behavior specific to satisfying +// toolstash -cmp is used. +func quirksMode() bool { + return base.Debug.UnifiedQuirks != 0 +} + +// posBasesOf returns all of the position bases in the source files, +// as seen in a straightforward traversal. +// +// This is necessary to ensure position bases (and thus file names) +// get registered in the same order as noder would visit them. +func posBasesOf(noders []*noder) []*syntax.PosBase { + seen := make(map[*syntax.PosBase]bool) + var bases []*syntax.PosBase + + for _, p := range noders { + syntax.Crawl(p.file, func(n syntax.Node) bool { + if b := n.Pos().Base(); !seen[b] { + bases = append(bases, b) + seen[b] = true + } + return false + }) + } + + return bases +} + +// importedObjsOf returns the imported objects (i.e., referenced +// objects not declared by curpkg) from the parsed source files, in +// the order that typecheck used to load their definitions. +// +// This is needed because loading the definitions for imported objects +// can also add file names. +func importedObjsOf(curpkg *types2.Package, info *types2.Info, noders []*noder) []types2.Object { + // This code is complex because it matches the precise order that + // typecheck recursively and repeatedly traverses the IR. It's meant + // to be thrown away eventually anyway. + + seen := make(map[types2.Object]bool) + var objs []types2.Object + + var phase int + + decls := make(map[types2.Object]syntax.Decl) + assoc := func(decl syntax.Decl, names ...*syntax.Name) { + for _, name := range names { + obj, ok := info.Defs[name] + assert(ok) + decls[obj] = decl + } + } + + for _, p := range noders { + syntax.Crawl(p.file, func(n syntax.Node) bool { + switch n := n.(type) { + case *syntax.ConstDecl: + assoc(n, n.NameList...) + case *syntax.FuncDecl: + assoc(n, n.Name) + case *syntax.TypeDecl: + assoc(n, n.Name) + case *syntax.VarDecl: + assoc(n, n.NameList...) + case *syntax.BlockStmt: + return true + } + return false + }) + } + + var visited map[syntax.Decl]bool + + var resolveDecl func(n syntax.Decl) + var resolveNode func(n syntax.Node, top bool) + + resolveDecl = func(n syntax.Decl) { + if visited[n] { + return + } + visited[n] = true + + switch n := n.(type) { + case *syntax.ConstDecl: + resolveNode(n.Type, true) + resolveNode(n.Values, true) + + case *syntax.FuncDecl: + if n.Recv != nil { + resolveNode(n.Recv, true) + } + resolveNode(n.Type, true) + + case *syntax.TypeDecl: + resolveNode(n.Type, true) + + case *syntax.VarDecl: + if n.Type != nil { + resolveNode(n.Type, true) + } else { + resolveNode(n.Values, true) + } + } + } + + resolveObj := func(pos syntax.Pos, obj types2.Object) { + switch obj.Pkg() { + case nil: + // builtin; nothing to do + + case curpkg: + if decl, ok := decls[obj]; ok { + resolveDecl(decl) + } + + default: + if obj.Parent() == obj.Pkg().Scope() && !seen[obj] { + seen[obj] = true + objs = append(objs, obj) + } + } + } + + checkdefat := func(pos syntax.Pos, n *syntax.Name) { + if n.Value == "_" { + return + } + obj, ok := info.Uses[n] + if !ok { + obj, ok = info.Defs[n] + if !ok { + return + } + } + if obj == nil { + return + } + resolveObj(pos, obj) + } + checkdef := func(n *syntax.Name) { checkdefat(n.Pos(), n) } + + var later []syntax.Node + + resolveNode = func(n syntax.Node, top bool) { + if n == nil { + return + } + syntax.Crawl(n, func(n syntax.Node) bool { + switch n := n.(type) { + case *syntax.Name: + checkdef(n) + + case *syntax.SelectorExpr: + if name, ok := n.X.(*syntax.Name); ok { + if _, isPkg := info.Uses[name].(*types2.PkgName); isPkg { + checkdefat(n.X.Pos(), n.Sel) + return true + } + } + + case *syntax.AssignStmt: + resolveNode(n.Rhs, top) + resolveNode(n.Lhs, top) + return true + + case *syntax.VarDecl: + resolveNode(n.Values, top) + + case *syntax.FuncLit: + if top { + resolveNode(n.Type, top) + later = append(later, n.Body) + return true + } + + case *syntax.BlockStmt: + if phase >= 3 { + for _, stmt := range n.List { + resolveNode(stmt, false) + } + } + return true + } + + return false + }) + } + + for phase = 1; phase <= 5; phase++ { + visited = map[syntax.Decl]bool{} + + for _, p := range noders { + for _, decl := range p.file.DeclList { + switch decl := decl.(type) { + case *syntax.ConstDecl: + resolveDecl(decl) + + case *syntax.FuncDecl: + resolveDecl(decl) + if phase >= 3 && decl.Body != nil { + resolveNode(decl.Body, true) + } + + case *syntax.TypeDecl: + if !decl.Alias || phase >= 2 { + resolveDecl(decl) + } + + case *syntax.VarDecl: + if phase >= 2 { + resolveNode(decl.Values, true) + resolveDecl(decl) + } + } + } + + if phase >= 5 { + syntax.Crawl(p.file, func(n syntax.Node) bool { + if name, ok := n.(*syntax.Name); ok { + if obj, ok := info.Uses[name]; ok { + resolveObj(name.Pos(), obj) + } + } + return false + }) + } + } + + for i := 0; i < len(later); i++ { + resolveNode(later[i], true) + } + later = nil + } + + return objs +} + +// typeExprEndPos returns the position that noder would leave base.Pos +// after parsing the given type expression. +func typeExprEndPos(expr0 syntax.Expr) syntax.Pos { + for { + switch expr := expr0.(type) { + case *syntax.Name: + return expr.Pos() + case *syntax.SelectorExpr: + return expr.X.Pos() + + case *syntax.ParenExpr: + expr0 = expr.X + + case *syntax.Operation: + assert(expr.Op == syntax.Mul) + assert(expr.Y == nil) + expr0 = expr.X + + case *syntax.ArrayType: + expr0 = expr.Elem + case *syntax.ChanType: + expr0 = expr.Elem + case *syntax.DotsType: + expr0 = expr.Elem + case *syntax.MapType: + expr0 = expr.Value + case *syntax.SliceType: + expr0 = expr.Elem + + case *syntax.StructType: + return expr.Pos() + + case *syntax.InterfaceType: + expr0 = lastFieldType(expr.MethodList) + if expr0 == nil { + return expr.Pos() + } + + case *syntax.FuncType: + expr0 = lastFieldType(expr.ResultList) + if expr0 == nil { + expr0 = lastFieldType(expr.ParamList) + if expr0 == nil { + return expr.Pos() + } + } + + case *syntax.IndexExpr: // explicit type instantiation + targs := unpackListExpr(expr.Index) + expr0 = targs[len(targs)-1] + + default: + panic(fmt.Sprintf("%s: unexpected type expression %v", expr.Pos(), syntax.String(expr))) + } + } +} + +func lastFieldType(fields []*syntax.Field) syntax.Expr { + if len(fields) == 0 { + return nil + } + return fields[len(fields)-1].Type +} + +// sumPos returns the position that noder.sum would produce for +// constant expression x. +func sumPos(x syntax.Expr) syntax.Pos { + orig := x + for { + switch x1 := x.(type) { + case *syntax.BasicLit: + assert(x1.Kind == syntax.StringLit) + return x1.Pos() + case *syntax.Operation: + assert(x1.Op == syntax.Add && x1.Y != nil) + if r, ok := x1.Y.(*syntax.BasicLit); ok { + assert(r.Kind == syntax.StringLit) + x = x1.X + continue + } + } + return orig.Pos() + } +} + +// funcParamsEndPos returns the value of base.Pos left by noder after +// processing a function signature. +func funcParamsEndPos(fn *ir.Func) src.XPos { + sig := fn.Nname.Type() + + fields := sig.Results().FieldSlice() + if len(fields) == 0 { + fields = sig.Params().FieldSlice() + if len(fields) == 0 { + fields = sig.Recvs().FieldSlice() + if len(fields) == 0 { + if fn.OClosure != nil { + return fn.Nname.Ntype.Pos() + } + return fn.Pos() + } + } + } + + return fields[len(fields)-1].Pos +} + +type dupTypes struct { + origs map[types2.Type]types2.Type +} + +func (d *dupTypes) orig(t types2.Type) types2.Type { + if orig, ok := d.origs[t]; ok { + return orig + } + return t +} + +func (d *dupTypes) add(t, orig types2.Type) { + if t == orig { + return + } + + if d.origs == nil { + d.origs = make(map[types2.Type]types2.Type) + } + assert(d.origs[t] == nil) + d.origs[t] = orig + + switch t := t.(type) { + case *types2.Pointer: + orig := orig.(*types2.Pointer) + d.add(t.Elem(), orig.Elem()) + + case *types2.Slice: + orig := orig.(*types2.Slice) + d.add(t.Elem(), orig.Elem()) + + case *types2.Map: + orig := orig.(*types2.Map) + d.add(t.Key(), orig.Key()) + d.add(t.Elem(), orig.Elem()) + + case *types2.Array: + orig := orig.(*types2.Array) + assert(t.Len() == orig.Len()) + d.add(t.Elem(), orig.Elem()) + + case *types2.Chan: + orig := orig.(*types2.Chan) + assert(t.Dir() == orig.Dir()) + d.add(t.Elem(), orig.Elem()) + + case *types2.Struct: + orig := orig.(*types2.Struct) + assert(t.NumFields() == orig.NumFields()) + for i := 0; i < t.NumFields(); i++ { + d.add(t.Field(i).Type(), orig.Field(i).Type()) + } + + case *types2.Interface: + orig := orig.(*types2.Interface) + assert(t.NumExplicitMethods() == orig.NumExplicitMethods()) + assert(t.NumEmbeddeds() == orig.NumEmbeddeds()) + for i := 0; i < t.NumExplicitMethods(); i++ { + d.add(t.ExplicitMethod(i).Type(), orig.ExplicitMethod(i).Type()) + } + for i := 0; i < t.NumEmbeddeds(); i++ { + d.add(t.EmbeddedType(i), orig.EmbeddedType(i)) + } + + case *types2.Signature: + orig := orig.(*types2.Signature) + assert((t.Recv() == nil) == (orig.Recv() == nil)) + if t.Recv() != nil { + d.add(t.Recv().Type(), orig.Recv().Type()) + } + d.add(t.Params(), orig.Params()) + d.add(t.Results(), orig.Results()) + + case *types2.Tuple: + orig := orig.(*types2.Tuple) + assert(t.Len() == orig.Len()) + for i := 0; i < t.Len(); i++ { + d.add(t.At(i).Type(), orig.At(i).Type()) + } + + default: + assert(types2.Identical(t, orig)) + } +} diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go new file mode 100644 index 0000000..5d17c53 --- /dev/null +++ b/src/cmd/compile/internal/noder/reader.go @@ -0,0 +1,2460 @@ +// UNREVIEWED + +// Copyright 2021 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 noder + +import ( + "bytes" + "fmt" + "go/constant" + "internal/buildcfg" + "strings" + + "cmd/compile/internal/base" + "cmd/compile/internal/deadcode" + "cmd/compile/internal/dwarfgen" + "cmd/compile/internal/inline" + "cmd/compile/internal/ir" + "cmd/compile/internal/reflectdata" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/internal/obj" + "cmd/internal/src" +) + +// TODO(mdempsky): Suppress duplicate type/const errors that can arise +// during typecheck due to naive type substitution (e.g., see #42758). +// I anticipate these will be handled as a consequence of adding +// dictionaries support, so it's probably not important to focus on +// this until after that's done. + +type pkgReader struct { + pkgDecoder + + posBases []*src.PosBase + pkgs []*types.Pkg + typs []*types.Type + + // offset for rewriting the given index into the output, + // but bitwise inverted so we can detect if we're missing the entry or not. + newindex []int +} + +func newPkgReader(pr pkgDecoder) *pkgReader { + return &pkgReader{ + pkgDecoder: pr, + + posBases: make([]*src.PosBase, pr.numElems(relocPosBase)), + pkgs: make([]*types.Pkg, pr.numElems(relocPkg)), + typs: make([]*types.Type, pr.numElems(relocType)), + + newindex: make([]int, pr.totalElems()), + } +} + +type pkgReaderIndex struct { + pr *pkgReader + idx int + dict *readerDict +} + +func (pri pkgReaderIndex) asReader(k reloc, marker syncMarker) *reader { + r := pri.pr.newReader(k, pri.idx, marker) + r.dict = pri.dict + return r +} + +func (pr *pkgReader) newReader(k reloc, idx int, marker syncMarker) *reader { + return &reader{ + decoder: pr.newDecoder(k, idx, marker), + p: pr, + } +} + +type reader struct { + decoder + + p *pkgReader + + dict *readerDict + + // TODO(mdempsky): The state below is all specific to reading + // function bodies. It probably makes sense to split it out + // separately so that it doesn't take up space in every reader + // instance. + + curfn *ir.Func + locals []*ir.Name + closureVars []*ir.Name + + funarghack bool + + // scopeVars is a stack tracking the number of variables declared in + // the current function at the moment each open scope was opened. + scopeVars []int + marker dwarfgen.ScopeMarker + lastCloseScopePos src.XPos + + // === details for handling inline body expansion === + + // If we're reading in a function body because of inlining, this is + // the call that we're inlining for. + inlCaller *ir.Func + inlCall *ir.CallExpr + inlFunc *ir.Func + inlTreeIndex int + inlPosBases map[*src.PosBase]*src.PosBase + + delayResults bool + + // Label to return to. + retlabel *types.Sym + + inlvars, retvars ir.Nodes +} + +type readerDict struct { + // targs holds the implicit and explicit type arguments in use for + // reading the current object. For example: + // + // func F[T any]() { + // type X[U any] struct { t T; u U } + // var _ X[string] + // } + // + // var _ = F[int] + // + // While instantiating F[int], we need to in turn instantiate + // X[string]. [int] and [string] are explicit type arguments for F + // and X, respectively; but [int] is also the implicit type + // arguments for X. + // + // (As an analogy to function literals, explicits are the function + // literal's formal parameters, while implicits are variables + // captured by the function literal.) + targs []*types.Type + + // implicits counts how many of types within targs are implicit type + // arguments; the rest are explicit. + implicits int + + derived []derivedInfo // reloc index of the derived type's descriptor + derivedTypes []*types.Type // slice of previously computed derived types + + funcs []objInfo + funcsObj []ir.Node +} + +func setType(n ir.Node, typ *types.Type) { + n.SetType(typ) + n.SetTypecheck(1) + + if name, ok := n.(*ir.Name); ok { + name.SetWalkdef(1) + name.Ntype = ir.TypeNode(name.Type()) + } +} + +func setValue(name *ir.Name, val constant.Value) { + name.SetVal(val) + name.Defn = nil +} + +// @@@ Positions + +func (r *reader) pos() src.XPos { + return base.Ctxt.PosTable.XPos(r.pos0()) +} + +func (r *reader) pos0() src.Pos { + r.sync(syncPos) + if !r.bool() { + return src.NoPos + } + + posBase := r.posBase() + line := r.uint() + col := r.uint() + return src.MakePos(posBase, line, col) +} + +func (r *reader) posBase() *src.PosBase { + return r.inlPosBase(r.p.posBaseIdx(r.reloc(relocPosBase))) +} + +func (pr *pkgReader) posBaseIdx(idx int) *src.PosBase { + if b := pr.posBases[idx]; b != nil { + return b + } + + r := pr.newReader(relocPosBase, idx, syncPosBase) + var b *src.PosBase + + absFilename := r.string() + filename := absFilename + + // For build artifact stability, the export data format only + // contains the "absolute" filename as returned by objabi.AbsFile. + // However, some tests (e.g., test/run.go's asmcheck tests) expect + // to see the full, original filename printed out. Re-expanding + // "$GOROOT" to buildcfg.GOROOT is a close-enough approximation to + // satisfy this. + // + // TODO(mdempsky): De-duplicate this logic with similar logic in + // cmd/link/internal/ld's expandGoroot. However, this will probably + // require being more consistent about when we use native vs UNIX + // file paths. + const dollarGOROOT = "$GOROOT" + if strings.HasPrefix(filename, dollarGOROOT) { + filename = buildcfg.GOROOT + filename[len(dollarGOROOT):] + } + + if r.bool() { + b = src.NewFileBase(filename, absFilename) + } else { + pos := r.pos0() + line := r.uint() + col := r.uint() + b = src.NewLinePragmaBase(pos, filename, absFilename, line, col) + } + + pr.posBases[idx] = b + return b +} + +func (r *reader) inlPosBase(oldBase *src.PosBase) *src.PosBase { + if r.inlCall == nil { + return oldBase + } + + if newBase, ok := r.inlPosBases[oldBase]; ok { + return newBase + } + + newBase := src.NewInliningBase(oldBase, r.inlTreeIndex) + r.inlPosBases[oldBase] = newBase + return newBase +} + +func (r *reader) updatePos(xpos src.XPos) src.XPos { + pos := base.Ctxt.PosTable.Pos(xpos) + pos.SetBase(r.inlPosBase(pos.Base())) + return base.Ctxt.PosTable.XPos(pos) +} + +func (r *reader) origPos(xpos src.XPos) src.XPos { + if r.inlCall == nil { + return xpos + } + + pos := base.Ctxt.PosTable.Pos(xpos) + for old, new := range r.inlPosBases { + if pos.Base() == new { + pos.SetBase(old) + return base.Ctxt.PosTable.XPos(pos) + } + } + + base.FatalfAt(xpos, "pos base missing from inlPosBases") + panic("unreachable") +} + +// @@@ Packages + +func (r *reader) pkg() *types.Pkg { + r.sync(syncPkg) + return r.p.pkgIdx(r.reloc(relocPkg)) +} + +func (pr *pkgReader) pkgIdx(idx int) *types.Pkg { + if pkg := pr.pkgs[idx]; pkg != nil { + return pkg + } + + pkg := pr.newReader(relocPkg, idx, syncPkgDef).doPkg() + pr.pkgs[idx] = pkg + return pkg +} + +func (r *reader) doPkg() *types.Pkg { + path := r.string() + if path == "builtin" { + return types.BuiltinPkg + } + if path == "" { + path = r.p.pkgPath + } + + name := r.string() + height := r.len() + + pkg := types.NewPkg(path, "") + + if pkg.Name == "" { + pkg.Name = name + } else { + assert(pkg.Name == name) + } + + if pkg.Height == 0 { + pkg.Height = height + } else { + assert(pkg.Height == height) + } + + return pkg +} + +// @@@ Types + +func (r *reader) typ() *types.Type { + return r.typWrapped(true) +} + +// typWrapped is like typ, but allows suppressing generation of +// unnecessary wrappers as a compile-time optimization. +func (r *reader) typWrapped(wrapped bool) *types.Type { + return r.p.typIdx(r.typInfo(), r.dict, wrapped) +} + +func (r *reader) typInfo() typeInfo { + r.sync(syncType) + if r.bool() { + return typeInfo{idx: r.len(), derived: true} + } + return typeInfo{idx: r.reloc(relocType), derived: false} +} + +func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict, wrapped bool) *types.Type { + idx := info.idx + var where **types.Type + if info.derived { + where = &dict.derivedTypes[idx] + idx = dict.derived[idx].idx + } else { + where = &pr.typs[idx] + } + + if typ := *where; typ != nil { + return typ + } + + r := pr.newReader(relocType, idx, syncTypeIdx) + r.dict = dict + + typ := r.doTyp() + assert(typ != nil) + + // For recursive type declarations involving interfaces and aliases, + // above r.doTyp() call may have already set pr.typs[idx], so just + // double check and return the type. + // + // Example: + // + // type F = func(I) + // + // type I interface { + // m(F) + // } + // + // The writer writes data types in following index order: + // + // 0: func(I) + // 1: I + // 2: interface{m(func(I))} + // + // The reader resolves it in following index order: + // + // 0 -> 1 -> 2 -> 0 -> 1 + // + // and can divide in logically 2 steps: + // + // - 0 -> 1 : first time the reader reach type I, + // it creates new named type with symbol I. + // + // - 2 -> 0 -> 1: the reader ends up reaching symbol I again, + // now the symbol I was setup in above step, so + // the reader just return the named type. + // + // Now, the functions called return, the pr.typs looks like below: + // + // - 0 -> 1 -> 2 -> 0 : [<T> I <T>] + // - 0 -> 1 -> 2 : [func(I) I <T>] + // - 0 -> 1 : [func(I) I interface { "".m(func("".I)) }] + // + // The idx 1, corresponding with type I was resolved successfully + // after r.doTyp() call. + + if prev := *where; prev != nil { + return prev + } + + if wrapped { + // Only cache if we're adding wrappers, so that other callers that + // find a cached type know it was wrapped. + *where = typ + + r.needWrapper(typ) + } + + if !typ.IsUntyped() { + types.CheckSize(typ) + } + + return typ +} + +func (r *reader) doTyp() *types.Type { + switch tag := codeType(r.code(syncType)); tag { + default: + panic(fmt.Sprintf("unexpected type: %v", tag)) + + case typeBasic: + return *basics[r.len()] + + case typeNamed: + obj := r.obj() + assert(obj.Op() == ir.OTYPE) + return obj.Type() + + case typeTypeParam: + return r.dict.targs[r.len()] + + case typeArray: + len := int64(r.uint64()) + return types.NewArray(r.typ(), len) + case typeChan: + dir := dirs[r.len()] + return types.NewChan(r.typ(), dir) + case typeMap: + return types.NewMap(r.typ(), r.typ()) + case typePointer: + return types.NewPtr(r.typ()) + case typeSignature: + return r.signature(types.LocalPkg, nil) + case typeSlice: + return types.NewSlice(r.typ()) + case typeStruct: + return r.structType() + case typeInterface: + return r.interfaceType() + } +} + +func (r *reader) interfaceType() *types.Type { + tpkg := types.LocalPkg // TODO(mdempsky): Remove after iexport is gone. + + nmethods, nembeddeds := r.len(), r.len() + + fields := make([]*types.Field, nmethods+nembeddeds) + methods, embeddeds := fields[:nmethods], fields[nmethods:] + + for i := range methods { + pos := r.pos() + pkg, sym := r.selector() + tpkg = pkg + mtyp := r.signature(pkg, types.FakeRecv()) + methods[i] = types.NewField(pos, sym, mtyp) + } + for i := range embeddeds { + embeddeds[i] = types.NewField(src.NoXPos, nil, r.typ()) + } + + if len(fields) == 0 { + return types.Types[types.TINTER] // empty interface + } + return types.NewInterface(tpkg, fields, false) +} + +func (r *reader) structType() *types.Type { + tpkg := types.LocalPkg // TODO(mdempsky): Remove after iexport is gone. + fields := make([]*types.Field, r.len()) + for i := range fields { + pos := r.pos() + pkg, sym := r.selector() + tpkg = pkg + ftyp := r.typ() + tag := r.string() + embedded := r.bool() + + f := types.NewField(pos, sym, ftyp) + f.Note = tag + if embedded { + f.Embedded = 1 + } + fields[i] = f + } + return types.NewStruct(tpkg, fields) +} + +func (r *reader) signature(tpkg *types.Pkg, recv *types.Field) *types.Type { + r.sync(syncSignature) + + params := r.params(&tpkg) + results := r.params(&tpkg) + if r.bool() { // variadic + params[len(params)-1].SetIsDDD(true) + } + + return types.NewSignature(tpkg, recv, nil, params, results) +} + +func (r *reader) params(tpkg **types.Pkg) []*types.Field { + r.sync(syncParams) + fields := make([]*types.Field, r.len()) + for i := range fields { + *tpkg, fields[i] = r.param() + } + return fields +} + +func (r *reader) param() (*types.Pkg, *types.Field) { + r.sync(syncParam) + + pos := r.pos() + pkg, sym := r.localIdent() + typ := r.typ() + + return pkg, types.NewField(pos, sym, typ) +} + +// @@@ Objects + +var objReader = map[*types.Sym]pkgReaderIndex{} + +func (r *reader) obj() ir.Node { + r.sync(syncObject) + + if r.bool() { + idx := r.len() + obj := r.dict.funcsObj[idx] + if obj == nil { + fn := r.dict.funcs[idx] + targs := make([]*types.Type, len(fn.explicits)) + for i, targ := range fn.explicits { + targs[i] = r.p.typIdx(targ, r.dict, true) + } + + obj = r.p.objIdx(fn.idx, nil, targs) + assert(r.dict.funcsObj[idx] == nil) + r.dict.funcsObj[idx] = obj + } + return obj + } + + idx := r.reloc(relocObj) + + explicits := make([]*types.Type, r.len()) + for i := range explicits { + explicits[i] = r.typ() + } + + var implicits []*types.Type + if r.dict != nil { + implicits = r.dict.targs + } + + return r.p.objIdx(idx, implicits, explicits) +} + +func (pr *pkgReader) objIdx(idx int, implicits, explicits []*types.Type) ir.Node { + rname := pr.newReader(relocName, idx, syncObject1) + _, sym := rname.qualifiedIdent() + tag := codeObj(rname.code(syncCodeObj)) + + if tag == objStub { + assert(!sym.IsBlank()) + switch sym.Pkg { + case types.BuiltinPkg, types.UnsafePkg: + return sym.Def.(ir.Node) + } + if pri, ok := objReader[sym]; ok { + return pri.pr.objIdx(pri.idx, nil, explicits) + } + if haveLegacyImports { + assert(len(explicits) == 0) + return typecheck.Resolve(ir.NewIdent(src.NoXPos, sym)) + } + base.Fatalf("unresolved stub: %v", sym) + } + + dict := pr.objDictIdx(sym, idx, implicits, explicits) + + r := pr.newReader(relocObj, idx, syncObject1) + rext := pr.newReader(relocObjExt, idx, syncObject1) + + r.dict = dict + rext.dict = dict + + sym = r.mangle(sym) + if !sym.IsBlank() && sym.Def != nil { + return sym.Def.(*ir.Name) + } + + do := func(op ir.Op, hasTParams bool) *ir.Name { + pos := r.pos() + if hasTParams { + r.typeParamNames() + } + + name := ir.NewDeclNameAt(pos, op, sym) + name.Class = ir.PEXTERN // may be overridden later + if !sym.IsBlank() { + if sym.Def != nil { + base.FatalfAt(name.Pos(), "already have a definition for %v", name) + } + assert(sym.Def == nil) + sym.Def = name + } + return name + } + + switch tag { + default: + panic("unexpected object") + + case objAlias: + name := do(ir.OTYPE, false) + setType(name, r.typ()) + name.SetAlias(true) + return name + + case objConst: + name := do(ir.OLITERAL, false) + typ := r.typ() + val := FixValue(typ, r.value()) + setType(name, typ) + setValue(name, val) + return name + + case objFunc: + if sym.Name == "init" { + sym = renameinit() + } + name := do(ir.ONAME, true) + setType(name, r.signature(sym.Pkg, nil)) + + name.Func = ir.NewFunc(r.pos()) + name.Func.Nname = name + + rext.funcExt(name) + return name + + case objType: + name := do(ir.OTYPE, true) + typ := types.NewNamed(name) + setType(name, typ) + + // Important: We need to do this before SetUnderlying. + rext.typeExt(name) + + // We need to defer CheckSize until we've called SetUnderlying to + // handle recursive types. + types.DeferCheckSize() + typ.SetUnderlying(r.typWrapped(false)) + types.ResumeCheckSize() + + methods := make([]*types.Field, r.len()) + for i := range methods { + methods[i] = r.method(rext) + } + if len(methods) != 0 { + typ.Methods().Set(methods) + } + + r.needWrapper(typ) + + return name + + case objVar: + name := do(ir.ONAME, false) + setType(name, r.typ()) + rext.varExt(name) + return name + } +} + +func (r *reader) mangle(sym *types.Sym) *types.Sym { + if !r.hasTypeParams() { + return sym + } + + var buf bytes.Buffer + buf.WriteString(sym.Name) + buf.WriteByte('[') + for i, targ := range r.dict.targs { + if i > 0 { + if i == r.dict.implicits { + buf.WriteByte(';') + } else { + buf.WriteByte(',') + } + } + buf.WriteString(targ.LinkString()) + } + buf.WriteByte(']') + return sym.Pkg.Lookup(buf.String()) +} + +func (pr *pkgReader) objDictIdx(sym *types.Sym, idx int, implicits, explicits []*types.Type) *readerDict { + r := pr.newReader(relocObjDict, idx, syncObject1) + + var dict readerDict + + nimplicits := r.len() + nexplicits := r.len() + + if nimplicits > len(implicits) || nexplicits != len(explicits) { + base.Fatalf("%v has %v+%v params, but instantiated with %v+%v args", sym, nimplicits, nexplicits, len(implicits), len(explicits)) + } + + dict.targs = append(implicits[:nimplicits:nimplicits], explicits...) + dict.implicits = nimplicits + + // For stenciling, we can just skip over the type parameters. + for range dict.targs[dict.implicits:] { + // Skip past bounds without actually evaluating them. + r.sync(syncType) + if r.bool() { + r.len() + } else { + r.reloc(relocType) + } + } + + dict.derived = make([]derivedInfo, r.len()) + dict.derivedTypes = make([]*types.Type, len(dict.derived)) + for i := range dict.derived { + dict.derived[i] = derivedInfo{r.reloc(relocType), r.bool()} + } + + dict.funcs = make([]objInfo, r.len()) + dict.funcsObj = make([]ir.Node, len(dict.funcs)) + for i := range dict.funcs { + objIdx := r.reloc(relocObj) + targs := make([]typeInfo, r.len()) + for j := range targs { + targs[j] = r.typInfo() + } + dict.funcs[i] = objInfo{idx: objIdx, explicits: targs} + } + + return &dict +} + +func (r *reader) typeParamNames() { + r.sync(syncTypeParamNames) + + for range r.dict.targs[r.dict.implicits:] { + r.pos() + r.localIdent() + } +} + +func (r *reader) method(rext *reader) *types.Field { + r.sync(syncMethod) + pos := r.pos() + pkg, sym := r.selector() + r.typeParamNames() + _, recv := r.param() + typ := r.signature(pkg, recv) + + fnsym := sym + fnsym = ir.MethodSym(recv.Type, fnsym) + name := ir.NewNameAt(pos, fnsym) + setType(name, typ) + + name.Func = ir.NewFunc(r.pos()) + name.Func.Nname = name + + rext.funcExt(name) + + meth := types.NewField(name.Func.Pos(), sym, typ) + meth.Nname = name + meth.SetNointerface(name.Func.Pragma&ir.Nointerface != 0) + + return meth +} + +func (r *reader) qualifiedIdent() (pkg *types.Pkg, sym *types.Sym) { + r.sync(syncSym) + pkg = r.pkg() + if name := r.string(); name != "" { + sym = pkg.Lookup(name) + } + return +} + +func (r *reader) localIdent() (pkg *types.Pkg, sym *types.Sym) { + r.sync(syncLocalIdent) + pkg = r.pkg() + if name := r.string(); name != "" { + sym = pkg.Lookup(name) + } + return +} + +func (r *reader) selector() (origPkg *types.Pkg, sym *types.Sym) { + r.sync(syncSelector) + origPkg = r.pkg() + name := r.string() + pkg := origPkg + if types.IsExported(name) { + pkg = types.LocalPkg + } + sym = pkg.Lookup(name) + return +} + +func (r *reader) hasTypeParams() bool { + return r.dict.hasTypeParams() +} + +func (dict *readerDict) hasTypeParams() bool { + return dict != nil && len(dict.targs) != 0 +} + +// @@@ Compiler extensions + +func (r *reader) funcExt(name *ir.Name) { + r.sync(syncFuncExt) + + name.Class = 0 // so MarkFunc doesn't complain + ir.MarkFunc(name) + + fn := name.Func + + // XXX: Workaround because linker doesn't know how to copy Pos. + if !fn.Pos().IsKnown() { + fn.SetPos(name.Pos()) + } + + // Normally, we only compile local functions, which saves redundant compilation work. + // n.Defn is not nil for local functions, and is nil for imported function. But for + // generic functions, we might have an instantiation that no other package has seen before. + // So we need to be conservative and compile it again. + // + // That's why name.Defn is set here, so ir.VisitFuncsBottomUp can analyze function. + // TODO(mdempsky,cuonglm): find a cleaner way to handle this. + if name.Sym().Pkg == types.LocalPkg || r.hasTypeParams() { + name.Defn = fn + } + + fn.Pragma = r.pragmaFlag() + r.linkname(name) + + typecheck.Func(fn) + + if r.bool() { + fn.ABI = obj.ABI(r.uint64()) + + // Escape analysis. + for _, fs := range &types.RecvsParams { + for _, f := range fs(name.Type()).FieldSlice() { + f.Note = r.string() + } + } + + if r.bool() { + fn.Inl = &ir.Inline{ + Cost: int32(r.len()), + CanDelayResults: r.bool(), + } + r.addBody(name.Func) + } + } else { + r.addBody(name.Func) + } + r.sync(syncEOF) +} + +func (r *reader) typeExt(name *ir.Name) { + r.sync(syncTypeExt) + + typ := name.Type() + + if r.hasTypeParams() { + // Set "RParams" (really type arguments here, not parameters) so + // this type is treated as "fully instantiated". This ensures the + // type descriptor is written out as DUPOK and method wrappers are + // generated even for imported types. + var targs []*types.Type + targs = append(targs, r.dict.targs...) + typ.SetRParams(targs) + } + + name.SetPragma(r.pragmaFlag()) + if name.Pragma()&ir.NotInHeap != 0 { + typ.SetNotInHeap(true) + } + + typecheck.SetBaseTypeIndex(typ, r.int64(), r.int64()) +} + +func (r *reader) varExt(name *ir.Name) { + r.sync(syncVarExt) + r.linkname(name) +} + +func (r *reader) linkname(name *ir.Name) { + assert(name.Op() == ir.ONAME) + r.sync(syncLinkname) + + if idx := r.int64(); idx >= 0 { + lsym := name.Linksym() + lsym.SymIdx = int32(idx) + lsym.Set(obj.AttrIndexed, true) + } else { + name.Sym().Linkname = r.string() + } +} + +func (r *reader) pragmaFlag() ir.PragmaFlag { + r.sync(syncPragma) + return ir.PragmaFlag(r.int()) +} + +// @@@ Function bodies + +// bodyReader tracks where the serialized IR for a function's body can +// be found. +var bodyReader = map[*ir.Func]pkgReaderIndex{} + +// todoBodies holds the list of function bodies that still need to be +// constructed. +var todoBodies []*ir.Func + +// todoBodiesDone signals that we constructed all function in todoBodies. +// This is necessary to prevent reader.addBody adds thing to todoBodies +// when nested inlining happens. +var todoBodiesDone = false + +func (r *reader) addBody(fn *ir.Func) { + pri := pkgReaderIndex{r.p, r.reloc(relocBody), r.dict} + bodyReader[fn] = pri + + if fn.Nname.Defn == nil { + // Don't read in function body for imported functions. + // See comment in funcExt. + return + } + + if r.curfn == nil && !todoBodiesDone { + todoBodies = append(todoBodies, fn) + return + } + + pri.funcBody(fn) +} + +func (pri pkgReaderIndex) funcBody(fn *ir.Func) { + r := pri.asReader(relocBody, syncFuncBody) + r.funcBody(fn) +} + +func (r *reader) funcBody(fn *ir.Func) { + r.curfn = fn + r.closureVars = fn.ClosureVars + + ir.WithFunc(fn, func() { + r.funcargs(fn) + + if !r.bool() { + return + } + + body := r.stmts() + if body == nil { + pos := src.NoXPos + if quirksMode() { + pos = funcParamsEndPos(fn) + } + body = []ir.Node{typecheck.Stmt(ir.NewBlockStmt(pos, nil))} + } + fn.Body = body + fn.Endlineno = r.pos() + }) + + r.marker.WriteTo(fn) +} + +func (r *reader) funcargs(fn *ir.Func) { + sig := fn.Nname.Type() + + if recv := sig.Recv(); recv != nil { + r.funcarg(recv, recv.Sym, ir.PPARAM) + } + for _, param := range sig.Params().FieldSlice() { + r.funcarg(param, param.Sym, ir.PPARAM) + } + + for i, param := range sig.Results().FieldSlice() { + sym := types.OrigSym(param.Sym) + + if sym == nil || sym.IsBlank() { + prefix := "~r" + if r.inlCall != nil { + prefix = "~R" + } else if sym != nil { + prefix = "~b" + } + sym = typecheck.LookupNum(prefix, i) + } + + r.funcarg(param, sym, ir.PPARAMOUT) + } +} + +func (r *reader) funcarg(param *types.Field, sym *types.Sym, ctxt ir.Class) { + if sym == nil { + assert(ctxt == ir.PPARAM) + if r.inlCall != nil { + r.inlvars.Append(ir.BlankNode) + } + return + } + + name := ir.NewNameAt(r.updatePos(param.Pos), sym) + setType(name, param.Type) + r.addLocal(name, ctxt) + + if r.inlCall == nil { + if !r.funarghack { + param.Sym = sym + param.Nname = name + } + } else { + if ctxt == ir.PPARAMOUT { + r.retvars.Append(name) + } else { + r.inlvars.Append(name) + } + } +} + +func (r *reader) addLocal(name *ir.Name, ctxt ir.Class) { + assert(ctxt == ir.PAUTO || ctxt == ir.PPARAM || ctxt == ir.PPARAMOUT) + + r.sync(syncAddLocal) + if enableSync { + want := r.int() + if have := len(r.locals); have != want { + base.FatalfAt(name.Pos(), "locals table has desynced") + } + } + + name.SetUsed(true) + r.locals = append(r.locals, name) + + // TODO(mdempsky): Move earlier. + if ir.IsBlank(name) { + return + } + + if r.inlCall != nil { + if ctxt == ir.PAUTO { + name.SetInlLocal(true) + } else { + name.SetInlFormal(true) + ctxt = ir.PAUTO + } + + // TODO(mdempsky): Rethink this hack. + if strings.HasPrefix(name.Sym().Name, "~") || base.Flag.GenDwarfInl == 0 { + name.SetPos(r.inlCall.Pos()) + name.SetInlFormal(false) + name.SetInlLocal(false) + } + } + + name.Class = ctxt + name.Curfn = r.curfn + + r.curfn.Dcl = append(r.curfn.Dcl, name) + + if ctxt == ir.PAUTO { + name.SetFrameOffset(0) + } +} + +func (r *reader) useLocal() *ir.Name { + r.sync(syncUseObjLocal) + if r.bool() { + return r.locals[r.len()] + } + return r.closureVars[r.len()] +} + +func (r *reader) openScope() { + r.sync(syncOpenScope) + pos := r.pos() + + if base.Flag.Dwarf { + r.scopeVars = append(r.scopeVars, len(r.curfn.Dcl)) + r.marker.Push(pos) + } +} + +func (r *reader) closeScope() { + r.sync(syncCloseScope) + r.lastCloseScopePos = r.pos() + + r.closeAnotherScope() +} + +// closeAnotherScope is like closeScope, but it reuses the same mark +// position as the last closeScope call. This is useful for "for" and +// "if" statements, as their implicit blocks always end at the same +// position as an explicit block. +func (r *reader) closeAnotherScope() { + r.sync(syncCloseAnotherScope) + + if base.Flag.Dwarf { + scopeVars := r.scopeVars[len(r.scopeVars)-1] + r.scopeVars = r.scopeVars[:len(r.scopeVars)-1] + + // Quirkish: noder decides which scopes to keep before + // typechecking, whereas incremental typechecking during IR + // construction can result in new autotemps being allocated. To + // produce identical output, we ignore autotemps here for the + // purpose of deciding whether to retract the scope. + // + // This is important for net/http/fcgi, because it contains: + // + // var body io.ReadCloser + // if len(content) > 0 { + // body, req.pw = io.Pipe() + // } else { … } + // + // Notably, io.Pipe is inlinable, and inlining it introduces a ~R0 + // variable at the call site. + // + // Noder does not preserve the scope where the io.Pipe() call + // resides, because it doesn't contain any declared variables in + // source. So the ~R0 variable ends up being assigned to the + // enclosing scope instead. + // + // However, typechecking this assignment also introduces + // autotemps, because io.Pipe's results need conversion before + // they can be assigned to their respective destination variables. + // + // TODO(mdempsky): We should probably just keep all scopes, and + // let dwarfgen take care of pruning them instead. + retract := true + for _, n := range r.curfn.Dcl[scopeVars:] { + if !n.AutoTemp() { + retract = false + break + } + } + + if retract { + // no variables were declared in this scope, so we can retract it. + r.marker.Unpush() + } else { + r.marker.Pop(r.lastCloseScopePos) + } + } +} + +// @@@ Statements + +func (r *reader) stmt() ir.Node { + switch stmts := r.stmts(); len(stmts) { + case 0: + return nil + case 1: + return stmts[0] + default: + return ir.NewBlockStmt(stmts[0].Pos(), stmts) + } +} + +func (r *reader) stmts() []ir.Node { + assert(ir.CurFunc == r.curfn) + var res ir.Nodes + + r.sync(syncStmts) + for { + tag := codeStmt(r.code(syncStmt1)) + if tag == stmtEnd { + r.sync(syncStmtsEnd) + return res + } + + if n := r.stmt1(tag, &res); n != nil { + res.Append(typecheck.Stmt(n)) + } + } +} + +func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node { + var label *types.Sym + if n := len(*out); n > 0 { + if ls, ok := (*out)[n-1].(*ir.LabelStmt); ok { + label = ls.Label + } + } + + switch tag { + default: + panic("unexpected statement") + + case stmtAssign: + pos := r.pos() + + // TODO(mdempsky): After quirks mode is gone, swap these + // statements so we visit LHS before RHS again. + rhs := r.exprList() + names, lhs := r.assignList() + + if len(rhs) == 0 { + for _, name := range names { + as := ir.NewAssignStmt(pos, name, nil) + as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, name)) + out.Append(typecheck.Stmt(as)) + } + return nil + } + + if len(lhs) == 1 && len(rhs) == 1 { + n := ir.NewAssignStmt(pos, lhs[0], rhs[0]) + n.Def = r.initDefn(n, names) + return n + } + + n := ir.NewAssignListStmt(pos, ir.OAS2, lhs, rhs) + n.Def = r.initDefn(n, names) + return n + + case stmtAssignOp: + op := r.op() + lhs := r.expr() + pos := r.pos() + rhs := r.expr() + return ir.NewAssignOpStmt(pos, op, lhs, rhs) + + case stmtIncDec: + op := r.op() + lhs := r.expr() + pos := r.pos() + n := ir.NewAssignOpStmt(pos, op, lhs, ir.NewBasicLit(pos, one)) + n.IncDec = true + return n + + case stmtBlock: + out.Append(r.blockStmt()...) + return nil + + case stmtBranch: + pos := r.pos() + op := r.op() + sym := r.optLabel() + return ir.NewBranchStmt(pos, op, sym) + + case stmtCall: + pos := r.pos() + op := r.op() + call := r.expr() + return ir.NewGoDeferStmt(pos, op, call) + + case stmtExpr: + return r.expr() + + case stmtFor: + return r.forStmt(label) + + case stmtIf: + return r.ifStmt() + + case stmtLabel: + pos := r.pos() + sym := r.label() + return ir.NewLabelStmt(pos, sym) + + case stmtReturn: + pos := r.pos() + results := r.exprList() + return ir.NewReturnStmt(pos, results) + + case stmtSelect: + return r.selectStmt(label) + + case stmtSend: + pos := r.pos() + ch := r.expr() + value := r.expr() + return ir.NewSendStmt(pos, ch, value) + + case stmtSwitch: + return r.switchStmt(label) + + case stmtTypeDeclHack: + // fake "type _ = int" declaration to prevent inlining in quirks mode. + assert(quirksMode()) + + name := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, ir.BlankNode.Sym()) + name.SetAlias(true) + setType(name, types.Types[types.TINT]) + + n := ir.NewDecl(src.NoXPos, ir.ODCLTYPE, name) + n.SetTypecheck(1) + return n + } +} + +func (r *reader) assignList() ([]*ir.Name, []ir.Node) { + lhs := make([]ir.Node, r.len()) + var names []*ir.Name + + for i := range lhs { + if r.bool() { + pos := r.pos() + _, sym := r.localIdent() + typ := r.typ() + + name := ir.NewNameAt(pos, sym) + lhs[i] = name + names = append(names, name) + setType(name, typ) + r.addLocal(name, ir.PAUTO) + continue + } + + lhs[i] = r.expr() + } + + return names, lhs +} + +func (r *reader) blockStmt() []ir.Node { + r.sync(syncBlockStmt) + r.openScope() + stmts := r.stmts() + r.closeScope() + return stmts +} + +func (r *reader) forStmt(label *types.Sym) ir.Node { + r.sync(syncForStmt) + + r.openScope() + + if r.bool() { + pos := r.pos() + + // TODO(mdempsky): After quirks mode is gone, swap these + // statements so we read LHS before X again. + x := r.expr() + names, lhs := r.assignList() + + body := r.blockStmt() + r.closeAnotherScope() + + rang := ir.NewRangeStmt(pos, nil, nil, x, body) + if len(lhs) >= 1 { + rang.Key = lhs[0] + if len(lhs) >= 2 { + rang.Value = lhs[1] + } + } + rang.Def = r.initDefn(rang, names) + rang.Label = label + return rang + } + + pos := r.pos() + init := r.stmt() + cond := r.expr() + post := r.stmt() + body := r.blockStmt() + r.closeAnotherScope() + + stmt := ir.NewForStmt(pos, init, cond, post, body) + stmt.Label = label + return stmt +} + +func (r *reader) ifStmt() ir.Node { + r.sync(syncIfStmt) + r.openScope() + pos := r.pos() + init := r.stmts() + cond := r.expr() + then := r.blockStmt() + els := r.stmts() + n := ir.NewIfStmt(pos, cond, then, els) + n.SetInit(init) + r.closeAnotherScope() + return n +} + +func (r *reader) selectStmt(label *types.Sym) ir.Node { + r.sync(syncSelectStmt) + + pos := r.pos() + clauses := make([]*ir.CommClause, r.len()) + for i := range clauses { + if i > 0 { + r.closeScope() + } + r.openScope() + + pos := r.pos() + comm := r.stmt() + body := r.stmts() + + clauses[i] = ir.NewCommStmt(pos, comm, body) + } + if len(clauses) > 0 { + r.closeScope() + } + n := ir.NewSelectStmt(pos, clauses) + n.Label = label + return n +} + +func (r *reader) switchStmt(label *types.Sym) ir.Node { + r.sync(syncSwitchStmt) + + r.openScope() + pos := r.pos() + init := r.stmt() + + var tag ir.Node + if r.bool() { + pos := r.pos() + var ident *ir.Ident + if r.bool() { + pos := r.pos() + sym := typecheck.Lookup(r.string()) + ident = ir.NewIdent(pos, sym) + } + x := r.expr() + tag = ir.NewTypeSwitchGuard(pos, ident, x) + } else { + tag = r.expr() + } + + tswitch, ok := tag.(*ir.TypeSwitchGuard) + if ok && tswitch.Tag == nil { + tswitch = nil + } + + clauses := make([]*ir.CaseClause, r.len()) + for i := range clauses { + if i > 0 { + r.closeScope() + } + r.openScope() + + pos := r.pos() + cases := r.exprList() + + clause := ir.NewCaseStmt(pos, cases, nil) + if tswitch != nil { + pos := r.pos() + typ := r.typ() + + name := ir.NewNameAt(pos, tswitch.Tag.Sym()) + setType(name, typ) + r.addLocal(name, ir.PAUTO) + clause.Var = name + name.Defn = tswitch + } + + clause.Body = r.stmts() + clauses[i] = clause + } + if len(clauses) > 0 { + r.closeScope() + } + r.closeScope() + + n := ir.NewSwitchStmt(pos, tag, clauses) + n.Label = label + if init != nil { + n.SetInit([]ir.Node{init}) + } + return n +} + +func (r *reader) label() *types.Sym { + r.sync(syncLabel) + name := r.string() + if r.inlCall != nil { + name = fmt.Sprintf("~%s·%d", name, inlgen) + } + return typecheck.Lookup(name) +} + +func (r *reader) optLabel() *types.Sym { + r.sync(syncOptLabel) + if r.bool() { + return r.label() + } + return nil +} + +// initDefn marks the given names as declared by defn and populates +// its Init field with ODCL nodes. It then reports whether any names +// were so declared, which can be used to initialize defn.Def. +func (r *reader) initDefn(defn ir.InitNode, names []*ir.Name) bool { + if len(names) == 0 { + return false + } + + init := make([]ir.Node, len(names)) + for i, name := range names { + name.Defn = defn + init[i] = ir.NewDecl(name.Pos(), ir.ODCL, name) + } + defn.SetInit(init) + return true +} + +// @@@ Expressions + +// expr reads and returns a typechecked expression. +func (r *reader) expr() (res ir.Node) { + defer func() { + if res != nil && res.Typecheck() == 0 { + base.FatalfAt(res.Pos(), "%v missed typecheck", res) + } + }() + + switch tag := codeExpr(r.code(syncExpr)); tag { + default: + panic("unhandled expression") + + case exprNone: + return nil + + case exprBlank: + // blank only allowed in LHS of assignments + // TODO(mdempsky): Handle directly in assignList instead? + return typecheck.AssignExpr(ir.BlankNode) + + case exprLocal: + return typecheck.Expr(r.useLocal()) + + case exprName: + // Callee instead of Expr allows builtins + // TODO(mdempsky): Handle builtins directly in exprCall, like method calls? + return typecheck.Callee(r.obj()) + + case exprType: + // TODO(mdempsky): ir.TypeNode should probably return a typecheck'd node. + n := ir.TypeNode(r.typ()) + n.SetTypecheck(1) + return n + + case exprConst: + pos := r.pos() + typ := r.typ() + val := FixValue(typ, r.value()) + op := r.op() + orig := r.string() + return typecheck.Expr(OrigConst(pos, typ, val, op, orig)) + + case exprCompLit: + return r.compLit() + + case exprFuncLit: + return r.funcLit() + + case exprSelector: + x := r.expr() + pos := r.pos() + _, sym := r.selector() + n := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)).(*ir.SelectorExpr) + if n.Op() == ir.OMETHVALUE { + wrapper := methodValueWrapper{ + rcvr: n.X.Type(), + method: n.Selection, + } + if r.importedDef() { + haveMethodValueWrappers = append(haveMethodValueWrappers, wrapper) + } else { + needMethodValueWrappers = append(needMethodValueWrappers, wrapper) + } + } + return n + + case exprIndex: + x := r.expr() + pos := r.pos() + index := r.expr() + return typecheck.Expr(ir.NewIndexExpr(pos, x, index)) + + case exprSlice: + x := r.expr() + pos := r.pos() + var index [3]ir.Node + for i := range index { + index[i] = r.expr() + } + op := ir.OSLICE + if index[2] != nil { + op = ir.OSLICE3 + } + return typecheck.Expr(ir.NewSliceExpr(pos, op, x, index[0], index[1], index[2])) + + case exprAssert: + x := r.expr() + pos := r.pos() + typ := r.expr().(ir.Ntype) + return typecheck.Expr(ir.NewTypeAssertExpr(pos, x, typ)) + + case exprUnaryOp: + op := r.op() + pos := r.pos() + x := r.expr() + + switch op { + case ir.OADDR: + return typecheck.Expr(typecheck.NodAddrAt(pos, x)) + case ir.ODEREF: + return typecheck.Expr(ir.NewStarExpr(pos, x)) + } + return typecheck.Expr(ir.NewUnaryExpr(pos, op, x)) + + case exprBinaryOp: + op := r.op() + x := r.expr() + pos := r.pos() + y := r.expr() + + switch op { + case ir.OANDAND, ir.OOROR: + return typecheck.Expr(ir.NewLogicalExpr(pos, op, x, y)) + } + return typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y)) + + case exprCall: + fun := r.expr() + if r.bool() { // method call + pos := r.pos() + _, sym := r.selector() + fun = typecheck.Callee(ir.NewSelectorExpr(pos, ir.OXDOT, fun, sym)) + } + pos := r.pos() + args := r.exprs() + dots := r.bool() + return typecheck.Call(pos, fun, args, dots) + + case exprConvert: + typ := r.typ() + pos := r.pos() + x := r.expr() + return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONV, typ, x)) + } +} + +func (r *reader) compLit() ir.Node { + r.sync(syncCompLit) + pos := r.pos() + typ0 := r.typ() + + typ := typ0 + if typ.IsPtr() { + typ = typ.Elem() + } + if typ.Kind() == types.TFORW { + base.FatalfAt(pos, "unresolved composite literal type: %v", typ) + } + isStruct := typ.Kind() == types.TSTRUCT + + elems := make([]ir.Node, r.len()) + for i := range elems { + elemp := &elems[i] + + if isStruct { + sk := ir.NewStructKeyExpr(r.pos(), typ.Field(r.len()), nil) + *elemp, elemp = sk, &sk.Value + } else if r.bool() { + kv := ir.NewKeyExpr(r.pos(), r.expr(), nil) + *elemp, elemp = kv, &kv.Value + } + + *elemp = wrapName(r.pos(), r.expr()) + } + + lit := typecheck.Expr(ir.NewCompLitExpr(pos, ir.OCOMPLIT, ir.TypeNode(typ), elems)) + if typ0.IsPtr() { + lit = typecheck.Expr(typecheck.NodAddrAt(pos, lit)) + lit.SetType(typ0) + } + return lit +} + +func wrapName(pos src.XPos, x ir.Node) ir.Node { + // These nodes do not carry line numbers. + // Introduce a wrapper node to give them the correct line. + switch ir.Orig(x).Op() { + case ir.OTYPE, ir.OLITERAL: + if x.Sym() == nil { + break + } + fallthrough + case ir.ONAME, ir.ONONAME, ir.OPACK, ir.ONIL: + p := ir.NewParenExpr(pos, x) + p.SetImplicit(true) + return p + } + return x +} + +func (r *reader) funcLit() ir.Node { + r.sync(syncFuncLit) + + pos := r.pos() + typPos := r.pos() + xtype2 := r.signature(types.LocalPkg, nil) + + opos := pos + if quirksMode() { + opos = r.origPos(pos) + } + + fn := ir.NewClosureFunc(opos, r.curfn != nil) + clo := fn.OClosure + ir.NameClosure(clo, r.curfn) + + setType(fn.Nname, xtype2) + if quirksMode() { + fn.Nname.Ntype = ir.TypeNodeAt(typPos, xtype2) + } + typecheck.Func(fn) + setType(clo, fn.Type()) + + fn.ClosureVars = make([]*ir.Name, 0, r.len()) + for len(fn.ClosureVars) < cap(fn.ClosureVars) { + ir.NewClosureVar(r.pos(), fn, r.useLocal()) + } + + r.addBody(fn) + + // TODO(mdempsky): Remove hard-coding of typecheck.Target. + return ir.UseClosure(clo, typecheck.Target) +} + +func (r *reader) exprList() []ir.Node { + r.sync(syncExprList) + return r.exprs() +} + +func (r *reader) exprs() []ir.Node { + r.sync(syncExprs) + nodes := make([]ir.Node, r.len()) + if len(nodes) == 0 { + return nil // TODO(mdempsky): Unclear if this matters. + } + for i := range nodes { + nodes[i] = r.expr() + } + return nodes +} + +func (r *reader) op() ir.Op { + r.sync(syncOp) + return ir.Op(r.len()) +} + +// @@@ Package initialization + +func (r *reader) pkgInit(self *types.Pkg, target *ir.Package) { + if quirksMode() { + for i, n := 0, r.len(); i < n; i++ { + // Eagerly register position bases, so their filenames are + // assigned stable indices. + posBase := r.posBase() + _ = base.Ctxt.PosTable.XPos(src.MakePos(posBase, 0, 0)) + } + + for i, n := 0, r.len(); i < n; i++ { + // Eagerly resolve imported objects, so any filenames registered + // in the process are assigned stable indices too. + _, sym := r.qualifiedIdent() + typecheck.Resolve(ir.NewIdent(src.NoXPos, sym)) + assert(sym.Def != nil) + } + } + + cgoPragmas := make([][]string, r.len()) + for i := range cgoPragmas { + cgoPragmas[i] = r.strings() + } + target.CgoPragmas = cgoPragmas + + r.pkgDecls(target) + + r.sync(syncEOF) +} + +func (r *reader) pkgDecls(target *ir.Package) { + r.sync(syncDecls) + for { + switch code := codeDecl(r.code(syncDecl)); code { + default: + panic(fmt.Sprintf("unhandled decl: %v", code)) + + case declEnd: + return + + case declFunc: + names := r.pkgObjs(target) + assert(len(names) == 1) + target.Decls = append(target.Decls, names[0].Func) + + case declMethod: + typ := r.typ() + _, sym := r.selector() + + method := typecheck.Lookdot1(nil, sym, typ, typ.Methods(), 0) + target.Decls = append(target.Decls, method.Nname.(*ir.Name).Func) + + case declVar: + pos := r.pos() + names := r.pkgObjs(target) + values := r.exprList() + + if len(names) > 1 && len(values) == 1 { + as := ir.NewAssignListStmt(pos, ir.OAS2, nil, values) + for _, name := range names { + as.Lhs.Append(name) + name.Defn = as + } + target.Decls = append(target.Decls, as) + } else { + for i, name := range names { + as := ir.NewAssignStmt(pos, name, nil) + if i < len(values) { + as.Y = values[i] + } + name.Defn = as + target.Decls = append(target.Decls, as) + } + } + + if n := r.len(); n > 0 { + assert(len(names) == 1) + embeds := make([]ir.Embed, n) + for i := range embeds { + embeds[i] = ir.Embed{Pos: r.pos(), Patterns: r.strings()} + } + names[0].Embed = &embeds + target.Embeds = append(target.Embeds, names[0]) + } + + case declOther: + r.pkgObjs(target) + } + } +} + +func (r *reader) pkgObjs(target *ir.Package) []*ir.Name { + r.sync(syncDeclNames) + nodes := make([]*ir.Name, r.len()) + for i := range nodes { + r.sync(syncDeclName) + + name := r.obj().(*ir.Name) + nodes[i] = name + + sym := name.Sym() + if sym.IsBlank() { + continue + } + + switch name.Class { + default: + base.FatalfAt(name.Pos(), "unexpected class: %v", name.Class) + + case ir.PEXTERN: + target.Externs = append(target.Externs, name) + + case ir.PFUNC: + assert(name.Type().Recv() == nil) + + // TODO(mdempsky): Cleaner way to recognize init? + if strings.HasPrefix(sym.Name, "init.") { + target.Inits = append(target.Inits, name.Func) + } + } + + if types.IsExported(sym.Name) { + assert(!sym.OnExportList()) + target.Exports = append(target.Exports, name) + sym.SetOnExportList(true) + } + + if base.Flag.AsmHdr != "" { + assert(!sym.Asm()) + target.Asms = append(target.Asms, name) + sym.SetAsm(true) + } + } + + return nodes +} + +// @@@ Inlining + +var inlgen = 0 + +func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr { + // TODO(mdempsky): Turn callerfn into an explicit parameter. + callerfn := ir.CurFunc + + pri, ok := bodyReader[fn] + if !ok { + // Assume it's an imported function or something that we don't + // have access to in quirks mode. + if haveLegacyImports { + return nil + } + + base.FatalfAt(call.Pos(), "missing function body for call to %v", fn) + } + + if fn.Inl.Body == nil { + expandInline(fn, pri) + } + + r := pri.asReader(relocBody, syncFuncBody) + + // TODO(mdempsky): This still feels clumsy. Can we do better? + tmpfn := ir.NewFunc(fn.Pos()) + tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), callerfn.Sym()) + tmpfn.Closgen = callerfn.Closgen + defer func() { callerfn.Closgen = tmpfn.Closgen }() + + setType(tmpfn.Nname, fn.Type()) + r.curfn = tmpfn + + r.inlCaller = callerfn + r.inlCall = call + r.inlFunc = fn + r.inlTreeIndex = inlIndex + r.inlPosBases = make(map[*src.PosBase]*src.PosBase) + + r.closureVars = make([]*ir.Name, len(r.inlFunc.ClosureVars)) + for i, cv := range r.inlFunc.ClosureVars { + r.closureVars[i] = cv.Outer + } + + r.funcargs(fn) + + assert(r.bool()) // have body + r.delayResults = fn.Inl.CanDelayResults + + r.retlabel = typecheck.AutoLabel(".i") + inlgen++ + + init := ir.TakeInit(call) + + // For normal function calls, the function callee expression + // may contain side effects. Make sure to preserve these, + // if necessary (#42703). + if call.Op() == ir.OCALLFUNC { + inline.CalleeEffects(&init, call.X) + } + + var args ir.Nodes + if call.Op() == ir.OCALLMETH { + base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck") + } + args.Append(call.Args...) + + // Create assignment to declare and initialize inlvars. + as2 := ir.NewAssignListStmt(call.Pos(), ir.OAS2, r.inlvars, args) + as2.Def = true + var as2init ir.Nodes + for _, name := range r.inlvars { + if ir.IsBlank(name) { + continue + } + // TODO(mdempsky): Use inlined position of name.Pos() instead? + name := name.(*ir.Name) + as2init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name)) + name.Defn = as2 + } + as2.SetInit(as2init) + init.Append(typecheck.Stmt(as2)) + + if !r.delayResults { + // If not delaying retvars, declare and zero initialize the + // result variables now. + for _, name := range r.retvars { + // TODO(mdempsky): Use inlined position of name.Pos() instead? + name := name.(*ir.Name) + init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name)) + ras := ir.NewAssignStmt(call.Pos(), name, nil) + init.Append(typecheck.Stmt(ras)) + } + } + + // Add an inline mark just before the inlined body. + // This mark is inline in the code so that it's a reasonable spot + // to put a breakpoint. Not sure if that's really necessary or not + // (in which case it could go at the end of the function instead). + // Note issue 28603. + init.Append(ir.NewInlineMarkStmt(call.Pos().WithIsStmt(), int64(r.inlTreeIndex))) + + nparams := len(r.curfn.Dcl) + + ir.WithFunc(r.curfn, func() { + r.curfn.Body = r.stmts() + r.curfn.Endlineno = r.pos() + + deadcode.Func(r.curfn) + + // Replace any "return" statements within the function body. + var edit func(ir.Node) ir.Node + edit = func(n ir.Node) ir.Node { + if ret, ok := n.(*ir.ReturnStmt); ok { + n = typecheck.Stmt(r.inlReturn(ret)) + } + ir.EditChildren(n, edit) + return n + } + edit(r.curfn) + }) + + body := ir.Nodes(r.curfn.Body) + + // Quirk: If deadcode elimination turned a non-empty function into + // an empty one, we need to set the position for the empty block + // left behind to the inlined position for src.NoXPos, so that + // an empty string gets added into the DWARF file name listing at + // the appropriate index. + if quirksMode() && len(body) == 1 { + if block, ok := body[0].(*ir.BlockStmt); ok && len(block.List) == 0 { + block.SetPos(r.updatePos(src.NoXPos)) + } + } + + // Quirkish: We need to eagerly prune variables added during + // inlining, but removed by deadcode.FuncBody above. Unused + // variables will get removed during stack frame layout anyway, but + // len(fn.Dcl) ends up influencing things like autotmp naming. + + used := usedLocals(body) + + for i, name := range r.curfn.Dcl { + if i < nparams || used.Has(name) { + name.Curfn = callerfn + callerfn.Dcl = append(callerfn.Dcl, name) + + // Quirkish. TODO(mdempsky): Document why. + if name.AutoTemp() { + name.SetEsc(ir.EscUnknown) + + if base.Flag.GenDwarfInl != 0 { + name.SetInlLocal(true) + } else { + name.SetPos(r.inlCall.Pos()) + } + } + } + } + + body.Append(ir.NewLabelStmt(call.Pos(), r.retlabel)) + + res := ir.NewInlinedCallExpr(call.Pos(), body, append([]ir.Node(nil), r.retvars...)) + res.SetInit(init) + res.SetType(call.Type()) + res.SetTypecheck(1) + + // Inlining shouldn't add any functions to todoBodies. + assert(len(todoBodies) == 0) + + return res +} + +// inlReturn returns a statement that can substitute for the given +// return statement when inlining. +func (r *reader) inlReturn(ret *ir.ReturnStmt) *ir.BlockStmt { + pos := r.inlCall.Pos() + + block := ir.TakeInit(ret) + + if results := ret.Results; len(results) != 0 { + assert(len(r.retvars) == len(results)) + + as2 := ir.NewAssignListStmt(pos, ir.OAS2, append([]ir.Node(nil), r.retvars...), ret.Results) + + if r.delayResults { + for _, name := range r.retvars { + // TODO(mdempsky): Use inlined position of name.Pos() instead? + name := name.(*ir.Name) + block.Append(ir.NewDecl(pos, ir.ODCL, name)) + name.Defn = as2 + } + } + + block.Append(as2) + } + + block.Append(ir.NewBranchStmt(pos, ir.OGOTO, r.retlabel)) + return ir.NewBlockStmt(pos, block) +} + +// expandInline reads in an extra copy of IR to populate +// fn.Inl.{Dcl,Body}. +func expandInline(fn *ir.Func, pri pkgReaderIndex) { + // TODO(mdempsky): Remove this function. It's currently needed by + // dwarfgen/dwarf.go:preInliningDcls, which requires fn.Inl.Dcl to + // create abstract function DIEs. But we should be able to provide it + // with the same information some other way. + + fndcls := len(fn.Dcl) + topdcls := len(typecheck.Target.Decls) + + tmpfn := ir.NewFunc(fn.Pos()) + tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), fn.Sym()) + tmpfn.ClosureVars = fn.ClosureVars + + { + r := pri.asReader(relocBody, syncFuncBody) + setType(tmpfn.Nname, fn.Type()) + + // Don't change parameter's Sym/Nname fields. + r.funarghack = true + + r.funcBody(tmpfn) + + ir.WithFunc(tmpfn, func() { + deadcode.Func(tmpfn) + }) + } + + used := usedLocals(tmpfn.Body) + + for _, name := range tmpfn.Dcl { + if name.Class != ir.PAUTO || used.Has(name) { + name.Curfn = fn + fn.Inl.Dcl = append(fn.Inl.Dcl, name) + } + } + fn.Inl.Body = tmpfn.Body + + // Double check that we didn't change fn.Dcl by accident. + assert(fndcls == len(fn.Dcl)) + + // typecheck.Stmts may have added function literals to + // typecheck.Target.Decls. Remove them again so we don't risk trying + // to compile them multiple times. + typecheck.Target.Decls = typecheck.Target.Decls[:topdcls] +} + +// usedLocals returns a set of local variables that are used within body. +func usedLocals(body []ir.Node) ir.NameSet { + var used ir.NameSet + ir.VisitList(body, func(n ir.Node) { + if n, ok := n.(*ir.Name); ok && n.Op() == ir.ONAME && n.Class == ir.PAUTO { + used.Add(n) + } + }) + return used +} + +// @@@ Method wrappers + +// needWrapperTypes lists types for which we may need to generate +// method wrappers. +var needWrapperTypes []*types.Type + +// haveWrapperTypes lists types for which we know we already have +// method wrappers, because we found the type in an imported package. +var haveWrapperTypes []*types.Type + +// needMethodValueWrappers lists methods for which we may need to +// generate method value wrappers. +var needMethodValueWrappers []methodValueWrapper + +// haveMethodValueWrappers lists methods for which we know we already +// have method value wrappers, because we found it in an imported +// package. +var haveMethodValueWrappers []methodValueWrapper + +type methodValueWrapper struct { + rcvr *types.Type + method *types.Field +} + +func (r *reader) needWrapper(typ *types.Type) { + if typ.IsPtr() { + return + } + + // If a type was found in an imported package, then we can assume + // that package (or one of its transitive dependencies) already + // generated method wrappers for it. + if r.importedDef() { + haveWrapperTypes = append(haveWrapperTypes, typ) + } else { + needWrapperTypes = append(needWrapperTypes, typ) + } +} + +func (r *reader) importedDef() bool { + // If a type was found in an imported package, then we can assume + // that package (or one of its transitive dependencies) already + // generated method wrappers for it. + // + // Exception: If we're instantiating an imported generic type or + // function, we might be instantiating it with type arguments not + // previously seen before. + // + // TODO(mdempsky): Distinguish when a generic function or type was + // instantiated in an imported package so that we can add types to + // haveWrapperTypes instead. + return r.p != localPkgReader && !r.hasTypeParams() +} + +func MakeWrappers(target *ir.Package) { + // Only unified IR in non-quirks mode emits its own wrappers. + if base.Debug.Unified == 0 || quirksMode() { + return + } + + // always generate a wrapper for error.Error (#29304) + needWrapperTypes = append(needWrapperTypes, types.ErrorType) + + seen := make(map[string]*types.Type) + + for _, typ := range haveWrapperTypes { + wrapType(typ, target, seen, false) + } + haveWrapperTypes = nil + + for _, typ := range needWrapperTypes { + wrapType(typ, target, seen, true) + } + needWrapperTypes = nil + + for _, wrapper := range haveMethodValueWrappers { + wrapMethodValue(wrapper.rcvr, wrapper.method, target, false) + } + haveMethodValueWrappers = nil + + for _, wrapper := range needMethodValueWrappers { + wrapMethodValue(wrapper.rcvr, wrapper.method, target, true) + } + needMethodValueWrappers = nil +} + +func wrapType(typ *types.Type, target *ir.Package, seen map[string]*types.Type, needed bool) { + key := typ.LinkString() + if prev := seen[key]; prev != nil { + if !types.Identical(typ, prev) { + base.Fatalf("collision: types %v and %v have link string %q", typ, prev, key) + } + return + } + seen[key] = typ + + if !needed { + // Only called to add to 'seen'. + return + } + + if !typ.IsInterface() { + typecheck.CalcMethods(typ) + } + for _, meth := range typ.AllMethods().Slice() { + if meth.Sym.IsBlank() || !meth.IsMethod() { + base.FatalfAt(meth.Pos, "invalid method: %v", meth) + } + + methodWrapper(0, typ, meth, target) + + // For non-interface types, we also want *T wrappers. + if !typ.IsInterface() { + methodWrapper(1, typ, meth, target) + + // For not-in-heap types, *T is a scalar, not pointer shaped, + // so the interface wrappers use **T. + if typ.NotInHeap() { + methodWrapper(2, typ, meth, target) + } + } + } +} + +func methodWrapper(derefs int, tbase *types.Type, method *types.Field, target *ir.Package) { + wrapper := tbase + for i := 0; i < derefs; i++ { + wrapper = types.NewPtr(wrapper) + } + + sym := ir.MethodSym(wrapper, method.Sym) + base.Assertf(!sym.Siggen(), "already generated wrapper %v", sym) + sym.SetSiggen(true) + + wrappee := method.Type.Recv().Type + if types.Identical(wrapper, wrappee) || + !types.IsMethodApplicable(wrapper, method) || + !reflectdata.NeedEmit(tbase) { + return + } + + // TODO(mdempsky): Use method.Pos instead? + pos := base.AutogeneratedPos + + fn := newWrapperFunc(pos, sym, wrapper, method) + + var recv ir.Node = fn.Nname.Type().Recv().Nname.(*ir.Name) + + // For simple *T wrappers around T methods, panicwrap produces a + // nicer panic message. + if wrapper.IsPtr() && types.Identical(wrapper.Elem(), wrappee) { + cond := ir.NewBinaryExpr(pos, ir.OEQ, recv, types.BuiltinPkg.Lookup("nil").Def.(ir.Node)) + then := []ir.Node{ir.NewCallExpr(pos, ir.OCALL, typecheck.LookupRuntime("panicwrap"), nil)} + fn.Body.Append(ir.NewIfStmt(pos, cond, then, nil)) + } + + // typecheck will add one implicit deref, if necessary, + // but not-in-heap types require more for their **T wrappers. + for i := 1; i < derefs; i++ { + recv = Implicit(ir.NewStarExpr(pos, recv)) + } + + addTailCall(pos, fn, recv, method) + + finishWrapperFunc(fn, target) +} + +func wrapMethodValue(recvType *types.Type, method *types.Field, target *ir.Package, needed bool) { + sym := ir.MethodSymSuffix(recvType, method.Sym, "-fm") + if sym.Uniq() { + return + } + sym.SetUniq(true) + + // TODO(mdempsky): Use method.Pos instead? + pos := base.AutogeneratedPos + + fn := newWrapperFunc(pos, sym, nil, method) + sym.Def = fn.Nname + + // Declare and initialize variable holding receiver. + recv := ir.NewHiddenParam(pos, fn, typecheck.Lookup(".this"), recvType) + + if !needed { + typecheck.Func(fn) + return + } + + addTailCall(pos, fn, recv, method) + + finishWrapperFunc(fn, target) +} + +func newWrapperFunc(pos src.XPos, sym *types.Sym, wrapper *types.Type, method *types.Field) *ir.Func { + fn := ir.NewFunc(pos) + fn.SetDupok(true) // TODO(mdempsky): Leave unset for local, non-generic wrappers? + + name := ir.NewNameAt(pos, sym) + ir.MarkFunc(name) + name.Func = fn + name.Defn = fn + fn.Nname = name + + sig := newWrapperType(wrapper, method) + setType(name, sig) + + // TODO(mdempsky): De-duplicate with similar logic in funcargs. + defParams := func(class ir.Class, params *types.Type) { + for _, param := range params.FieldSlice() { + name := ir.NewNameAt(param.Pos, param.Sym) + name.Class = class + setType(name, param.Type) + + name.Curfn = fn + fn.Dcl = append(fn.Dcl, name) + + param.Nname = name + } + } + + defParams(ir.PPARAM, sig.Recvs()) + defParams(ir.PPARAM, sig.Params()) + defParams(ir.PPARAMOUT, sig.Results()) + + return fn +} + +func finishWrapperFunc(fn *ir.Func, target *ir.Package) { + typecheck.Func(fn) + + ir.WithFunc(fn, func() { + typecheck.Stmts(fn.Body) + }) + + // We generate wrappers after the global inlining pass, + // so we're responsible for applying inlining ourselves here. + inline.InlineCalls(fn) + + target.Decls = append(target.Decls, fn) +} + +// newWrapperType returns a copy of the given signature type, but with +// the receiver parameter type substituted with recvType. +// If recvType is nil, newWrapperType returns a signature +// without a receiver parameter. +func newWrapperType(recvType *types.Type, method *types.Field) *types.Type { + clone := func(params []*types.Field) []*types.Field { + res := make([]*types.Field, len(params)) + for i, param := range params { + sym := param.Sym + if sym == nil || sym.Name == "_" { + sym = typecheck.LookupNum(".anon", i) + } + res[i] = types.NewField(param.Pos, sym, param.Type) + res[i].SetIsDDD(param.IsDDD()) + } + return res + } + + sig := method.Type + + var recv *types.Field + if recvType != nil { + recv = types.NewField(sig.Recv().Pos, typecheck.Lookup(".this"), recvType) + } + params := clone(sig.Params().FieldSlice()) + results := clone(sig.Results().FieldSlice()) + + return types.NewSignature(types.NoPkg, recv, nil, params, results) +} + +func addTailCall(pos src.XPos, fn *ir.Func, recv ir.Node, method *types.Field) { + sig := fn.Nname.Type() + args := make([]ir.Node, sig.NumParams()) + for i, param := range sig.Params().FieldSlice() { + args[i] = param.Nname.(*ir.Name) + } + + // TODO(mdempsky): Support creating OTAILCALL, when possible. See reflectdata.methodWrapper. + // Not urgent though, because tail calls are currently incompatible with regabi anyway. + + fn.SetWrapper(true) // TODO(mdempsky): Leave unset for tail calls? + + dot := ir.NewSelectorExpr(pos, ir.OXDOT, recv, method.Sym) + call := typecheck.Call(pos, dot, args, method.Type.IsVariadic()).(*ir.CallExpr) + + if method.Type.NumResults() == 0 { + fn.Body.Append(call) + return + } + + ret := ir.NewReturnStmt(pos, nil) + ret.Results = []ir.Node{call} + fn.Body.Append(ret) +} diff --git a/src/cmd/compile/internal/noder/reader2.go b/src/cmd/compile/internal/noder/reader2.go new file mode 100644 index 0000000..c028d21 --- /dev/null +++ b/src/cmd/compile/internal/noder/reader2.go @@ -0,0 +1,506 @@ +// UNREVIEWED + +// Copyright 2021 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 noder + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/syntax" + "cmd/compile/internal/types2" + "cmd/internal/src" +) + +type pkgReader2 struct { + pkgDecoder + + ctxt *types2.Context + imports map[string]*types2.Package + + posBases []*syntax.PosBase + pkgs []*types2.Package + typs []types2.Type +} + +func readPackage2(ctxt *types2.Context, imports map[string]*types2.Package, input pkgDecoder) *types2.Package { + pr := pkgReader2{ + pkgDecoder: input, + + ctxt: ctxt, + imports: imports, + + posBases: make([]*syntax.PosBase, input.numElems(relocPosBase)), + pkgs: make([]*types2.Package, input.numElems(relocPkg)), + typs: make([]types2.Type, input.numElems(relocType)), + } + + r := pr.newReader(relocMeta, publicRootIdx, syncPublic) + pkg := r.pkg() + r.bool() // has init + + for i, n := 0, r.len(); i < n; i++ { + // As if r.obj(), but avoiding the Scope.Lookup call, + // to avoid eager loading of imports. + r.sync(syncObject) + assert(!r.bool()) + r.p.objIdx(r.reloc(relocObj)) + assert(r.len() == 0) + } + + r.sync(syncEOF) + + pkg.MarkComplete() + return pkg +} + +type reader2 struct { + decoder + + p *pkgReader2 + + dict *reader2Dict +} + +type reader2Dict struct { + bounds []typeInfo + + tparams []*types2.TypeParam + + derived []derivedInfo + derivedTypes []types2.Type +} + +type reader2TypeBound struct { + derived bool + boundIdx int +} + +func (pr *pkgReader2) newReader(k reloc, idx int, marker syncMarker) *reader2 { + return &reader2{ + decoder: pr.newDecoder(k, idx, marker), + p: pr, + } +} + +// @@@ Positions + +func (r *reader2) pos() syntax.Pos { + r.sync(syncPos) + if !r.bool() { + return syntax.Pos{} + } + + // TODO(mdempsky): Delta encoding. + posBase := r.posBase() + line := r.uint() + col := r.uint() + return syntax.MakePos(posBase, line, col) +} + +func (r *reader2) posBase() *syntax.PosBase { + return r.p.posBaseIdx(r.reloc(relocPosBase)) +} + +func (pr *pkgReader2) posBaseIdx(idx int) *syntax.PosBase { + if b := pr.posBases[idx]; b != nil { + return b + } + + r := pr.newReader(relocPosBase, idx, syncPosBase) + var b *syntax.PosBase + + filename := r.string() + + if r.bool() { + b = syntax.NewTrimmedFileBase(filename, true) + } else { + pos := r.pos() + line := r.uint() + col := r.uint() + b = syntax.NewLineBase(pos, filename, true, line, col) + } + + pr.posBases[idx] = b + return b +} + +// @@@ Packages + +func (r *reader2) pkg() *types2.Package { + r.sync(syncPkg) + return r.p.pkgIdx(r.reloc(relocPkg)) +} + +func (pr *pkgReader2) pkgIdx(idx int) *types2.Package { + // TODO(mdempsky): Consider using some non-nil pointer to indicate + // the universe scope, so we don't need to keep re-reading it. + if pkg := pr.pkgs[idx]; pkg != nil { + return pkg + } + + pkg := pr.newReader(relocPkg, idx, syncPkgDef).doPkg() + pr.pkgs[idx] = pkg + return pkg +} + +func (r *reader2) doPkg() *types2.Package { + path := r.string() + if path == "builtin" { + return nil // universe + } + if path == "" { + path = r.p.pkgPath + } + + if pkg := r.p.imports[path]; pkg != nil { + return pkg + } + + name := r.string() + height := r.len() + + pkg := types2.NewPackageHeight(path, name, height) + r.p.imports[path] = pkg + + // TODO(mdempsky): The list of imported packages is important for + // go/types, but we could probably skip populating it for types2. + imports := make([]*types2.Package, r.len()) + for i := range imports { + imports[i] = r.pkg() + } + pkg.SetImports(imports) + + return pkg +} + +// @@@ Types + +func (r *reader2) typ() types2.Type { + return r.p.typIdx(r.typInfo(), r.dict) +} + +func (r *reader2) typInfo() typeInfo { + r.sync(syncType) + if r.bool() { + return typeInfo{idx: r.len(), derived: true} + } + return typeInfo{idx: r.reloc(relocType), derived: false} +} + +func (pr *pkgReader2) typIdx(info typeInfo, dict *reader2Dict) types2.Type { + idx := info.idx + var where *types2.Type + if info.derived { + where = &dict.derivedTypes[idx] + idx = dict.derived[idx].idx + } else { + where = &pr.typs[idx] + } + + if typ := *where; typ != nil { + return typ + } + + r := pr.newReader(relocType, idx, syncTypeIdx) + r.dict = dict + + typ := r.doTyp() + assert(typ != nil) + + // See comment in pkgReader.typIdx explaining how this happens. + if prev := *where; prev != nil { + return prev + } + + *where = typ + return typ +} + +func (r *reader2) doTyp() (res types2.Type) { + switch tag := codeType(r.code(syncType)); tag { + default: + base.FatalfAt(src.NoXPos, "unhandled type tag: %v", tag) + panic("unreachable") + + case typeBasic: + return types2.Typ[r.len()] + + case typeNamed: + obj, targs := r.obj() + name := obj.(*types2.TypeName) + if len(targs) != 0 { + t, _ := types2.Instantiate(r.p.ctxt, name.Type(), targs, false) + return t + } + return name.Type() + + case typeTypeParam: + return r.dict.tparams[r.len()] + + case typeArray: + len := int64(r.uint64()) + return types2.NewArray(r.typ(), len) + case typeChan: + dir := types2.ChanDir(r.len()) + return types2.NewChan(dir, r.typ()) + case typeMap: + return types2.NewMap(r.typ(), r.typ()) + case typePointer: + return types2.NewPointer(r.typ()) + case typeSignature: + return r.signature(nil, nil, nil) + case typeSlice: + return types2.NewSlice(r.typ()) + case typeStruct: + return r.structType() + case typeInterface: + return r.interfaceType() + case typeUnion: + return r.unionType() + } +} + +func (r *reader2) structType() *types2.Struct { + fields := make([]*types2.Var, r.len()) + var tags []string + for i := range fields { + pos := r.pos() + pkg, name := r.selector() + ftyp := r.typ() + tag := r.string() + embedded := r.bool() + + fields[i] = types2.NewField(pos, pkg, name, ftyp, embedded) + if tag != "" { + for len(tags) < i { + tags = append(tags, "") + } + tags = append(tags, tag) + } + } + return types2.NewStruct(fields, tags) +} + +func (r *reader2) unionType() *types2.Union { + terms := make([]*types2.Term, r.len()) + for i := range terms { + terms[i] = types2.NewTerm(r.bool(), r.typ()) + } + return types2.NewUnion(terms) +} + +func (r *reader2) interfaceType() *types2.Interface { + methods := make([]*types2.Func, r.len()) + embeddeds := make([]types2.Type, r.len()) + + for i := range methods { + pos := r.pos() + pkg, name := r.selector() + mtyp := r.signature(nil, nil, nil) + methods[i] = types2.NewFunc(pos, pkg, name, mtyp) + } + + for i := range embeddeds { + embeddeds[i] = r.typ() + } + + return types2.NewInterfaceType(methods, embeddeds) +} + +func (r *reader2) signature(recv *types2.Var, rtparams, tparams []*types2.TypeParam) *types2.Signature { + r.sync(syncSignature) + + params := r.params() + results := r.params() + variadic := r.bool() + + return types2.NewSignatureType(recv, rtparams, tparams, params, results, variadic) +} + +func (r *reader2) params() *types2.Tuple { + r.sync(syncParams) + params := make([]*types2.Var, r.len()) + for i := range params { + params[i] = r.param() + } + return types2.NewTuple(params...) +} + +func (r *reader2) param() *types2.Var { + r.sync(syncParam) + + pos := r.pos() + pkg, name := r.localIdent() + typ := r.typ() + + return types2.NewParam(pos, pkg, name, typ) +} + +// @@@ Objects + +func (r *reader2) obj() (types2.Object, []types2.Type) { + r.sync(syncObject) + + assert(!r.bool()) + + pkg, name := r.p.objIdx(r.reloc(relocObj)) + obj := pkg.Scope().Lookup(name) + + targs := make([]types2.Type, r.len()) + for i := range targs { + targs[i] = r.typ() + } + + return obj, targs +} + +func (pr *pkgReader2) objIdx(idx int) (*types2.Package, string) { + rname := pr.newReader(relocName, idx, syncObject1) + + objPkg, objName := rname.qualifiedIdent() + assert(objName != "") + + tag := codeObj(rname.code(syncCodeObj)) + + if tag == objStub { + assert(objPkg == nil || objPkg == types2.Unsafe) + return objPkg, objName + } + + dict := pr.objDictIdx(idx) + + r := pr.newReader(relocObj, idx, syncObject1) + r.dict = dict + + objPkg.Scope().InsertLazy(objName, func() types2.Object { + switch tag { + default: + panic("weird") + + case objAlias: + pos := r.pos() + typ := r.typ() + return types2.NewTypeName(pos, objPkg, objName, typ) + + case objConst: + pos := r.pos() + typ := r.typ() + val := r.value() + return types2.NewConst(pos, objPkg, objName, typ, val) + + case objFunc: + pos := r.pos() + tparams := r.typeParamNames() + sig := r.signature(nil, nil, tparams) + return types2.NewFunc(pos, objPkg, objName, sig) + + case objType: + pos := r.pos() + + return types2.NewTypeNameLazy(pos, objPkg, objName, func(named *types2.Named) (tparams []*types2.TypeParam, underlying types2.Type, methods []*types2.Func) { + tparams = r.typeParamNames() + + // TODO(mdempsky): Rewrite receiver types to underlying is an + // Interface? The go/types importer does this (I think because + // unit tests expected that), but cmd/compile doesn't care + // about it, so maybe we can avoid worrying about that here. + underlying = r.typ().Underlying() + + methods = make([]*types2.Func, r.len()) + for i := range methods { + methods[i] = r.method() + } + + return + }) + + case objVar: + pos := r.pos() + typ := r.typ() + return types2.NewVar(pos, objPkg, objName, typ) + } + }) + + return objPkg, objName +} + +func (pr *pkgReader2) objDictIdx(idx int) *reader2Dict { + r := pr.newReader(relocObjDict, idx, syncObject1) + + var dict reader2Dict + + if implicits := r.len(); implicits != 0 { + base.Fatalf("unexpected object with %v implicit type parameter(s)", implicits) + } + + dict.bounds = make([]typeInfo, r.len()) + for i := range dict.bounds { + dict.bounds[i] = r.typInfo() + } + + dict.derived = make([]derivedInfo, r.len()) + dict.derivedTypes = make([]types2.Type, len(dict.derived)) + for i := range dict.derived { + dict.derived[i] = derivedInfo{r.reloc(relocType), r.bool()} + } + + // function references follow, but reader2 doesn't need those + + return &dict +} + +func (r *reader2) typeParamNames() []*types2.TypeParam { + r.sync(syncTypeParamNames) + + // Note: This code assumes it only processes objects without + // implement type parameters. This is currently fine, because + // reader2 is only used to read in exported declarations, which are + // always package scoped. + + if len(r.dict.bounds) == 0 { + return nil + } + + // Careful: Type parameter lists may have cycles. To allow for this, + // we construct the type parameter list in two passes: first we + // create all the TypeNames and TypeParams, then we construct and + // set the bound type. + + r.dict.tparams = make([]*types2.TypeParam, len(r.dict.bounds)) + for i := range r.dict.bounds { + pos := r.pos() + pkg, name := r.localIdent() + + tname := types2.NewTypeName(pos, pkg, name, nil) + r.dict.tparams[i] = types2.NewTypeParam(tname, nil) + } + + for i, bound := range r.dict.bounds { + r.dict.tparams[i].SetConstraint(r.p.typIdx(bound, r.dict)) + } + + return r.dict.tparams +} + +func (r *reader2) method() *types2.Func { + r.sync(syncMethod) + pos := r.pos() + pkg, name := r.selector() + + rtparams := r.typeParamNames() + sig := r.signature(r.param(), rtparams, nil) + + _ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go. + return types2.NewFunc(pos, pkg, name, sig) +} + +func (r *reader2) qualifiedIdent() (*types2.Package, string) { return r.ident(syncSym) } +func (r *reader2) localIdent() (*types2.Package, string) { return r.ident(syncLocalIdent) } +func (r *reader2) selector() (*types2.Package, string) { return r.ident(syncSelector) } + +func (r *reader2) ident(marker syncMarker) (*types2.Package, string) { + r.sync(marker) + return r.pkg(), r.string() +} diff --git a/src/cmd/compile/internal/noder/reloc.go b/src/cmd/compile/internal/noder/reloc.go new file mode 100644 index 0000000..669a618 --- /dev/null +++ b/src/cmd/compile/internal/noder/reloc.go @@ -0,0 +1,42 @@ +// UNREVIEWED + +// Copyright 2021 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 noder + +// A reloc indicates a particular section within a unified IR export. +// +// TODO(mdempsky): Rename to "section" or something similar? +type reloc int + +// A relocEnt (relocation entry) is an entry in an atom's local +// reference table. +// +// TODO(mdempsky): Rename this too. +type relocEnt struct { + kind reloc + idx int +} + +// Reserved indices within the meta relocation section. +const ( + publicRootIdx = 0 + privateRootIdx = 1 +) + +const ( + relocString reloc = iota + relocMeta + relocPosBase + relocPkg + relocName + relocType + relocObj + relocObjExt + relocObjDict + relocBody + + numRelocs = iota +) diff --git a/src/cmd/compile/internal/noder/scopes.go b/src/cmd/compile/internal/noder/scopes.go new file mode 100644 index 0000000..eb51847 --- /dev/null +++ b/src/cmd/compile/internal/noder/scopes.go @@ -0,0 +1,64 @@ +// Copyright 2021 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 noder + +import ( + "strings" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/syntax" + "cmd/compile/internal/types2" +) + +// recordScopes populates fn.Parents and fn.Marks based on the scoping +// information provided by types2. +func (g *irgen) recordScopes(fn *ir.Func, sig *syntax.FuncType) { + scope, ok := g.info.Scopes[sig] + if !ok { + base.FatalfAt(fn.Pos(), "missing scope for %v", fn) + } + + for i, n := 0, scope.NumChildren(); i < n; i++ { + g.walkScope(scope.Child(i)) + } + + g.marker.WriteTo(fn) +} + +func (g *irgen) walkScope(scope *types2.Scope) bool { + // types2 doesn't provide a proper API for determining the + // lexical element a scope represents, so we have to resort to + // string matching. Conveniently though, this allows us to + // skip both function types and function literals, neither of + // which are interesting to us here. + if strings.HasPrefix(scope.String(), "function scope ") { + return false + } + + g.marker.Push(g.pos(scope)) + + haveVars := false + for _, name := range scope.Names() { + if obj, ok := scope.Lookup(name).(*types2.Var); ok && obj.Name() != "_" { + haveVars = true + break + } + } + + for i, n := 0, scope.NumChildren(); i < n; i++ { + if g.walkScope(scope.Child(i)) { + haveVars = true + } + } + + if haveVars { + g.marker.Pop(g.end(scope)) + } else { + g.marker.Unpush() + } + + return haveVars +} diff --git a/src/cmd/compile/internal/noder/sizes.go b/src/cmd/compile/internal/noder/sizes.go new file mode 100644 index 0000000..23f2062 --- /dev/null +++ b/src/cmd/compile/internal/noder/sizes.go @@ -0,0 +1,150 @@ +// Copyright 2021 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 noder + +import ( + "fmt" + + "cmd/compile/internal/types" + "cmd/compile/internal/types2" +) + +// Code below based on go/types.StdSizes. +// Intentional differences are marked with "gc:". + +type gcSizes struct{} + +func (s *gcSizes) Alignof(T types2.Type) int64 { + // For arrays and structs, alignment is defined in terms + // of alignment of the elements and fields, respectively. + switch t := T.Underlying().(type) { + case *types2.Array: + // spec: "For a variable x of array type: unsafe.Alignof(x) + // is the same as unsafe.Alignof(x[0]), but at least 1." + return s.Alignof(t.Elem()) + case *types2.Struct: + // spec: "For a variable x of struct type: unsafe.Alignof(x) + // is the largest of the values unsafe.Alignof(x.f) for each + // field f of x, but at least 1." + max := int64(1) + for i, nf := 0, t.NumFields(); i < nf; i++ { + if a := s.Alignof(t.Field(i).Type()); a > max { + max = a + } + } + return max + case *types2.Slice, *types2.Interface: + // Multiword data structures are effectively structs + // in which each element has size PtrSize. + return int64(types.PtrSize) + case *types2.Basic: + // Strings are like slices and interfaces. + if t.Info()&types2.IsString != 0 { + return int64(types.PtrSize) + } + } + a := s.Sizeof(T) // may be 0 + // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1." + if a < 1 { + return 1 + } + // complex{64,128} are aligned like [2]float{32,64}. + if isComplex(T) { + a /= 2 + } + if a > int64(types.RegSize) { + return int64(types.RegSize) + } + return a +} + +func isComplex(T types2.Type) bool { + basic, ok := T.Underlying().(*types2.Basic) + return ok && basic.Info()&types2.IsComplex != 0 +} + +func (s *gcSizes) Offsetsof(fields []*types2.Var) []int64 { + offsets := make([]int64, len(fields)) + var o int64 + for i, f := range fields { + typ := f.Type() + a := s.Alignof(typ) + o = types.Rnd(o, a) + offsets[i] = o + o += s.Sizeof(typ) + } + return offsets +} + +func (s *gcSizes) Sizeof(T types2.Type) int64 { + switch t := T.Underlying().(type) { + case *types2.Basic: + k := t.Kind() + if int(k) < len(basicSizes) { + if s := basicSizes[k]; s > 0 { + return int64(s) + } + } + switch k { + case types2.String: + return int64(types.PtrSize) * 2 + case types2.Int, types2.Uint, types2.Uintptr, types2.UnsafePointer: + return int64(types.PtrSize) + } + panic(fmt.Sprintf("unimplemented basic: %v (kind %v)", T, k)) + case *types2.Array: + n := t.Len() + if n <= 0 { + return 0 + } + // n > 0 + // gc: Size includes alignment padding. + return s.Sizeof(t.Elem()) * n + case *types2.Slice: + return int64(types.PtrSize) * 3 + case *types2.Struct: + n := t.NumFields() + if n == 0 { + return 0 + } + fields := make([]*types2.Var, n) + for i := range fields { + fields[i] = t.Field(i) + } + offsets := s.Offsetsof(fields) + + // gc: The last field of a non-zero-sized struct is not allowed to + // have size 0. + last := s.Sizeof(fields[n-1].Type()) + if last == 0 && offsets[n-1] > 0 { + last = 1 + } + + // gc: Size includes alignment padding. + return types.Rnd(offsets[n-1]+last, s.Alignof(t)) + case *types2.Interface: + return int64(types.PtrSize) * 2 + case *types2.Chan, *types2.Map, *types2.Pointer, *types2.Signature: + return int64(types.PtrSize) + default: + panic(fmt.Sprintf("unimplemented type: %T", t)) + } +} + +var basicSizes = [...]byte{ + types2.Bool: 1, + types2.Int8: 1, + types2.Int16: 2, + types2.Int32: 4, + types2.Int64: 8, + types2.Uint8: 1, + types2.Uint16: 2, + types2.Uint32: 4, + types2.Uint64: 8, + types2.Float32: 4, + types2.Float64: 8, + types2.Complex64: 8, + types2.Complex128: 16, +} diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go new file mode 100644 index 0000000..a41b35a --- /dev/null +++ b/src/cmd/compile/internal/noder/stencil.go @@ -0,0 +1,2235 @@ +// Copyright 2021 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. + +// This file will evolve, since we plan to do a mix of stenciling and passing +// around dictionaries. + +package noder + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/objw" + "cmd/compile/internal/reflectdata" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/internal/obj" + "cmd/internal/src" + "fmt" + "go/constant" +) + +// Enable extra consistency checks. +const doubleCheck = false + +func assert(p bool) { + base.Assert(p) +} + +// For outputting debug information on dictionary format and instantiated dictionaries +// (type arg, derived types, sub-dictionary, and itab entries). +var infoPrintMode = false + +func infoPrint(format string, a ...interface{}) { + if infoPrintMode { + fmt.Printf(format, a...) + } +} + +var geninst genInst + +func BuildInstantiations() { + geninst.instInfoMap = make(map[*types.Sym]*instInfo) + geninst.buildInstantiations() + geninst.instInfoMap = nil +} + +// buildInstantiations scans functions for generic function calls and methods, and +// creates the required instantiations. It also creates instantiated methods for all +// fully-instantiated generic types that have been encountered already or new ones +// that are encountered during the instantiation process. It scans all declarations +// in typecheck.Target.Decls first, before scanning any new instantiations created. +func (g *genInst) buildInstantiations() { + // Instantiate the methods of instantiated generic types that we have seen so far. + g.instantiateMethods() + + // Scan all currentdecls for call to generic functions/methods. + n := len(typecheck.Target.Decls) + for i := 0; i < n; i++ { + g.scanForGenCalls(typecheck.Target.Decls[i]) + } + + // Scan all new instantiations created due to g.instantiateMethods() and the + // scan of current decls. This loop purposely runs until no new + // instantiations are created. + for i := 0; i < len(g.newInsts); i++ { + g.scanForGenCalls(g.newInsts[i]) + } + + g.finalizeSyms() + + // All the instantiations and dictionaries have been created. Now go through + // each new instantiation and transform the various operations that need to make + // use of their dictionary. + l := len(g.newInsts) + for _, fun := range g.newInsts { + info := g.instInfoMap[fun.Sym()] + g.dictPass(info) + if doubleCheck { + ir.Visit(info.fun, func(n ir.Node) { + if n.Op() != ir.OCONVIFACE { + return + } + c := n.(*ir.ConvExpr) + if c.X.Type().HasShape() && !c.X.Type().IsInterface() { + ir.Dump("BAD FUNCTION", info.fun) + ir.Dump("BAD CONVERSION", c) + base.Fatalf("converting shape type to interface") + } + }) + } + if base.Flag.W > 1 { + ir.Dump(fmt.Sprintf("\ndictpass %v", info.fun), info.fun) + } + } + assert(l == len(g.newInsts)) + g.newInsts = nil +} + +// scanForGenCalls scans a single function (or global assignment), looking for +// references to generic functions/methods. At each such reference, it creates any +// required instantiation and transforms the reference. +func (g *genInst) scanForGenCalls(decl ir.Node) { + switch decl.Op() { + case ir.ODCLFUNC: + if decl.Type().HasTParam() { + // Skip any generic functions + return + } + // transformCall() below depends on CurFunc being set. + ir.CurFunc = decl.(*ir.Func) + + case ir.OAS, ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV, ir.OASOP: + // These are all the various kinds of global assignments, + // whose right-hand-sides might contain a function + // instantiation. + + default: + // The other possible ops at the top level are ODCLCONST + // and ODCLTYPE, which don't have any function + // instantiations. + return + } + + // Search for any function references using generic function/methods. Then + // create the needed instantiated function if it hasn't been created yet, and + // change to calling that function directly. + modified := false + closureRequired := false + // declInfo will be non-nil exactly if we are scanning an instantiated function + declInfo := g.instInfoMap[decl.Sym()] + + ir.Visit(decl, func(n ir.Node) { + if n.Op() == ir.OFUNCINST { + // generic F, not immediately called + closureRequired = true + } + if (n.Op() == ir.OMETHEXPR || n.Op() == ir.OMETHVALUE) && len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) { + // T.M or x.M, where T or x is generic, but not immediately + // called. Not necessary if the method selected is + // actually for an embedded interface field. + closureRequired = true + } + if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OFUNCINST { + // We have found a function call using a generic function + // instantiation. + call := n.(*ir.CallExpr) + inst := call.X.(*ir.InstExpr) + nameNode, isMeth := g.getInstNameNode(inst) + targs := typecheck.TypesOf(inst.Targs) + st := g.getInstantiation(nameNode, targs, isMeth).fun + dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, nameNode, targs, isMeth) + if infoPrintMode { + dictkind := "Main dictionary" + if usingSubdict { + dictkind = "Sub-dictionary" + } + if inst.X.Op() == ir.OMETHVALUE { + fmt.Printf("%s in %v at generic method call: %v - %v\n", dictkind, decl, inst.X, call) + } else { + fmt.Printf("%s in %v at generic function call: %v - %v\n", dictkind, decl, inst.X, call) + } + } + + // Transform the Call now, which changes OCALL to + // OCALLFUNC and does typecheckaste/assignconvfn. Do + // it before installing the instantiation, so we are + // checking against non-shape param types in + // typecheckaste. + transformCall(call) + + // Replace the OFUNCINST with a direct reference to the + // new stenciled function + call.X = st.Nname + if inst.X.Op() == ir.OMETHVALUE { + // When we create an instantiation of a method + // call, we make it a function. So, move the + // receiver to be the first arg of the function + // call. + call.Args.Prepend(inst.X.(*ir.SelectorExpr).X) + } + + // Add dictionary to argument list. + call.Args.Prepend(dictValue) + modified = true + } + if n.Op() == ir.OCALLMETH && n.(*ir.CallExpr).X.Op() == ir.ODOTMETH && len(deref(n.(*ir.CallExpr).X.Type().Recv().Type).RParams()) > 0 { + // Method call on a generic type, which was instantiated by stenciling. + // Method calls on explicitly instantiated types will have an OFUNCINST + // and are handled above. + call := n.(*ir.CallExpr) + meth := call.X.(*ir.SelectorExpr) + targs := deref(meth.Type().Recv().Type).RParams() + + t := meth.X.Type() + baseType := deref(t).OrigType() + var gf *ir.Name + for _, m := range baseType.Methods().Slice() { + if meth.Sel == m.Sym { + gf = m.Nname.(*ir.Name) + break + } + } + + // Transform the Call now, which changes OCALL + // to OCALLFUNC and does typecheckaste/assignconvfn. + transformCall(call) + + st := g.getInstantiation(gf, targs, true).fun + dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, gf, targs, true) + // We have to be using a subdictionary, since this is + // a generic method call. + assert(usingSubdict) + + // Transform to a function call, by appending the + // dictionary and the receiver to the args. + call.SetOp(ir.OCALLFUNC) + call.X = st.Nname + call.Args.Prepend(dictValue, meth.X) + modified = true + } + }) + + // If we found a reference to a generic instantiation that wasn't an + // immediate call, then traverse the nodes of decl again (with + // EditChildren rather than Visit), where we actually change the + // reference to the instantiation to a closure that captures the + // dictionary, then does a direct call. + // EditChildren is more expensive than Visit, so we only do this + // in the infrequent case of an OFUNCINST without a corresponding + // call. + if closureRequired { + modified = true + var edit func(ir.Node) ir.Node + var outer *ir.Func + if f, ok := decl.(*ir.Func); ok { + outer = f + } + edit = func(x ir.Node) ir.Node { + if x.Op() == ir.OFUNCINST { + child := x.(*ir.InstExpr).X + if child.Op() == ir.OMETHEXPR || child.Op() == ir.OMETHVALUE { + // Call EditChildren on child (x.X), + // not x, so that we don't do + // buildClosure() on the + // METHEXPR/METHVALUE nodes as well. + ir.EditChildren(child, edit) + return g.buildClosure(outer, x) + } + } + ir.EditChildren(x, edit) + switch { + case x.Op() == ir.OFUNCINST: + return g.buildClosure(outer, x) + case (x.Op() == ir.OMETHEXPR || x.Op() == ir.OMETHVALUE) && + len(deref(x.(*ir.SelectorExpr).X.Type()).RParams()) > 0 && + !types.IsInterfaceMethod(x.(*ir.SelectorExpr).Selection.Type): + return g.buildClosure(outer, x) + } + return x + } + edit(decl) + } + if base.Flag.W > 1 && modified { + ir.Dump(fmt.Sprintf("\nmodified %v", decl), decl) + } + ir.CurFunc = nil + // We may have seen new fully-instantiated generic types while + // instantiating any needed functions/methods in the above + // function. If so, instantiate all the methods of those types + // (which will then lead to more function/methods to scan in the loop). + g.instantiateMethods() +} + +// buildClosure makes a closure to implement x, a OFUNCINST or OMETHEXPR/OMETHVALUE +// of generic type. outer is the containing function (or nil if closure is +// in a global assignment instead of a function). +func (g *genInst) buildClosure(outer *ir.Func, x ir.Node) ir.Node { + pos := x.Pos() + var target *ir.Func // target instantiated function/method + var dictValue ir.Node // dictionary to use + var rcvrValue ir.Node // receiver, if a method value + typ := x.Type() // type of the closure + var outerInfo *instInfo + if outer != nil { + outerInfo = g.instInfoMap[outer.Sym()] + } + usingSubdict := false + valueMethod := false + if x.Op() == ir.OFUNCINST { + inst := x.(*ir.InstExpr) + + // Type arguments we're instantiating with. + targs := typecheck.TypesOf(inst.Targs) + + // Find the generic function/method. + var gf *ir.Name + if inst.X.Op() == ir.ONAME { + // Instantiating a generic function call. + gf = inst.X.(*ir.Name) + } else if inst.X.Op() == ir.OMETHVALUE { + // Instantiating a method value x.M. + se := inst.X.(*ir.SelectorExpr) + rcvrValue = se.X + gf = se.Selection.Nname.(*ir.Name) + } else { + panic("unhandled") + } + + // target is the instantiated function we're trying to call. + // For functions, the target expects a dictionary as its first argument. + // For method values, the target expects a dictionary and the receiver + // as its first two arguments. + // dictValue is the value to use for the dictionary argument. + target = g.getInstantiation(gf, targs, rcvrValue != nil).fun + dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, rcvrValue != nil) + if infoPrintMode { + dictkind := "Main dictionary" + if usingSubdict { + dictkind = "Sub-dictionary" + } + if rcvrValue == nil { + fmt.Printf("%s in %v for generic function value %v\n", dictkind, outer, inst.X) + } else { + fmt.Printf("%s in %v for generic method value %v\n", dictkind, outer, inst.X) + } + } + } else { // ir.OMETHEXPR or ir.METHVALUE + // Method expression T.M where T is a generic type. + se := x.(*ir.SelectorExpr) + targs := deref(se.X.Type()).RParams() + if len(targs) == 0 { + panic("bad") + } + if x.Op() == ir.OMETHVALUE { + rcvrValue = se.X + } + + // se.X.Type() is the top-level type of the method expression. To + // correctly handle method expressions involving embedded fields, + // look up the generic method below using the type of the receiver + // of se.Selection, since that will be the type that actually has + // the method. + recv := deref(se.Selection.Type.Recv().Type) + if len(recv.RParams()) == 0 { + // The embedded type that actually has the method is not + // actually generic, so no need to build a closure. + return x + } + baseType := recv.OrigType() + var gf *ir.Name + for _, m := range baseType.Methods().Slice() { + if se.Sel == m.Sym { + gf = m.Nname.(*ir.Name) + break + } + } + if !gf.Type().Recv().Type.IsPtr() { + // Remember if value method, so we can detect (*T).M case. + valueMethod = true + } + target = g.getInstantiation(gf, targs, true).fun + dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, true) + if infoPrintMode { + dictkind := "Main dictionary" + if usingSubdict { + dictkind = "Sub-dictionary" + } + fmt.Printf("%s in %v for method expression %v\n", dictkind, outer, x) + } + } + + // Build a closure to implement a function instantiation. + // + // func f[T any] (int, int) (int, int) { ...whatever... } + // + // Then any reference to f[int] not directly called gets rewritten to + // + // .dictN := ... dictionary to use ... + // func(a0, a1 int) (r0, r1 int) { + // return .inst.f[int](.dictN, a0, a1) + // } + // + // Similarly for method expressions, + // + // type g[T any] .... + // func (rcvr g[T]) f(a0, a1 int) (r0, r1 int) { ... } + // + // Any reference to g[int].f not directly called gets rewritten to + // + // .dictN := ... dictionary to use ... + // func(rcvr g[int], a0, a1 int) (r0, r1 int) { + // return .inst.g[int].f(.dictN, rcvr, a0, a1) + // } + // + // Also method values + // + // var x g[int] + // + // Any reference to x.f not directly called gets rewritten to + // + // .dictN := ... dictionary to use ... + // x2 := x + // func(a0, a1 int) (r0, r1 int) { + // return .inst.g[int].f(.dictN, x2, a0, a1) + // } + + // Make a new internal function. + fn, formalParams, formalResults := startClosure(pos, outer, typ) + + // This is the dictionary we want to use. + // It may be a constant, it may be the outer functions's dictionary, or it may be + // a subdictionary acquired from the outer function's dictionary. + // For the latter, dictVar is a variable in the outer function's scope, set to the subdictionary + // read from the outer function's dictionary. + var dictVar *ir.Name + var dictAssign *ir.AssignStmt + if outer != nil { + dictVar = ir.NewNameAt(pos, typecheck.LookupNum(typecheck.LocalDictName, g.dnum)) + dictVar.SetSym(outer.Sym().Pkg.Lookup(dictVar.Sym().Name)) + g.dnum++ + dictVar.Class = ir.PAUTO + typed(types.Types[types.TUINTPTR], dictVar) + dictVar.Curfn = outer + dictAssign = ir.NewAssignStmt(pos, dictVar, dictValue) + dictAssign.SetTypecheck(1) + dictVar.Defn = dictAssign + outer.Dcl = append(outer.Dcl, dictVar) + } + // assign the receiver to a temporary. + var rcvrVar *ir.Name + var rcvrAssign ir.Node + if rcvrValue != nil { + rcvrVar = ir.NewNameAt(pos, typecheck.LookupNum(".rcvr", g.dnum)) + if outer != nil { + rcvrVar.SetSym(outer.Sym().Pkg.Lookup(rcvrVar.Sym().Name)) + } + g.dnum++ + typed(rcvrValue.Type(), rcvrVar) + rcvrAssign = ir.NewAssignStmt(pos, rcvrVar, rcvrValue) + rcvrAssign.SetTypecheck(1) + rcvrVar.Defn = rcvrAssign + if outer == nil { + rcvrVar.Class = ir.PEXTERN + typecheck.Target.Decls = append(typecheck.Target.Decls, rcvrAssign) + typecheck.Target.Externs = append(typecheck.Target.Externs, rcvrVar) + } else { + rcvrVar.Class = ir.PAUTO + rcvrVar.Curfn = outer + outer.Dcl = append(outer.Dcl, rcvrVar) + } + } + + // Build body of closure. This involves just calling the wrapped function directly + // with the additional dictionary argument. + + // First, figure out the dictionary argument. + var dict2Var ir.Node + if usingSubdict { + // Capture sub-dictionary calculated in the outer function + dict2Var = ir.CaptureName(pos, fn, dictVar) + typed(types.Types[types.TUINTPTR], dict2Var) + } else { + // Static dictionary, so can be used directly in the closure + dict2Var = dictValue + } + // Also capture the receiver variable. + var rcvr2Var *ir.Name + if rcvrValue != nil { + rcvr2Var = ir.CaptureName(pos, fn, rcvrVar) + } + + // Build arguments to call inside the closure. + var args []ir.Node + + // First the dictionary argument. + args = append(args, dict2Var) + // Then the receiver. + if rcvrValue != nil { + args = append(args, rcvr2Var) + } + // Then all the other arguments (including receiver for method expressions). + for i := 0; i < typ.NumParams(); i++ { + if x.Op() == ir.OMETHEXPR && i == 0 { + // If we are doing a method expression, we need to + // explicitly traverse any embedded fields in the receiver + // argument in order to call the method instantiation. + arg0 := formalParams[0].Nname.(ir.Node) + arg0 = typecheck.AddImplicitDots(ir.NewSelectorExpr(x.Pos(), ir.OXDOT, arg0, x.(*ir.SelectorExpr).Sel)).X + if valueMethod && arg0.Type().IsPtr() { + // For handling the (*T).M case: if we have a pointer + // receiver after following all the embedded fields, + // but it's a value method, add a star operator. + arg0 = ir.NewStarExpr(arg0.Pos(), arg0) + } + args = append(args, arg0) + } else { + args = append(args, formalParams[i].Nname.(*ir.Name)) + } + } + + // Build call itself. + var innerCall ir.Node = ir.NewCallExpr(pos, ir.OCALL, target.Nname, args) + innerCall.(*ir.CallExpr).IsDDD = typ.IsVariadic() + if len(formalResults) > 0 { + innerCall = ir.NewReturnStmt(pos, []ir.Node{innerCall}) + } + // Finish building body of closure. + ir.CurFunc = fn + // TODO: set types directly here instead of using typecheck.Stmt + typecheck.Stmt(innerCall) + ir.CurFunc = nil + fn.Body = []ir.Node{innerCall} + + // We're all done with the captured dictionary (and receiver, for method values). + ir.FinishCaptureNames(pos, outer, fn) + + // Make a closure referencing our new internal function. + c := ir.UseClosure(fn.OClosure, typecheck.Target) + var init []ir.Node + if outer != nil { + init = append(init, dictAssign) + } + if rcvrValue != nil { + init = append(init, rcvrAssign) + } + return ir.InitExpr(init, c) +} + +// instantiateMethods instantiates all the methods (and associated dictionaries) of +// all fully-instantiated generic types that have been added to typecheck.instTypeList. +// It continues until no more types are added to typecheck.instTypeList. +func (g *genInst) instantiateMethods() { + for { + instTypeList := typecheck.GetInstTypeList() + if len(instTypeList) == 0 { + break + } + typecheck.ClearInstTypeList() + for _, typ := range instTypeList { + assert(!typ.HasShape()) + // Mark runtime type as needed, since this ensures that the + // compiler puts out the needed DWARF symbols, when this + // instantiated type has a different package from the local + // package. + typecheck.NeedRuntimeType(typ) + // Lookup the method on the base generic type, since methods may + // not be set on imported instantiated types. + baseType := typ.OrigType() + for j, _ := range typ.Methods().Slice() { + if baseType.Methods().Slice()[j].Nointerface() { + typ.Methods().Slice()[j].SetNointerface(true) + } + baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name) + // Eagerly generate the instantiations and dictionaries that implement these methods. + // We don't use the instantiations here, just generate them (and any + // further instantiations those generate, etc.). + // Note that we don't set the Func for any methods on instantiated + // types. Their signatures don't match so that would be confusing. + // Direct method calls go directly to the instantiations, implemented above. + // Indirect method calls use wrappers generated in reflectcall. Those wrappers + // will use these instantiations if they are needed (for interface tables or reflection). + _ = g.getInstantiation(baseNname, typ.RParams(), true) + _ = g.getDictionarySym(baseNname, typ.RParams(), true) + } + } + } +} + +// getInstNameNode returns the name node for the method or function being instantiated, and a bool which is true if a method is being instantiated. +func (g *genInst) getInstNameNode(inst *ir.InstExpr) (*ir.Name, bool) { + if meth, ok := inst.X.(*ir.SelectorExpr); ok { + return meth.Selection.Nname.(*ir.Name), true + } else { + return inst.X.(*ir.Name), false + } +} + +// getDictOrSubdict returns, for a method/function call or reference (node n) in an +// instantiation (described by instInfo), a node which is accessing a sub-dictionary +// or main/static dictionary, as needed, and also returns a boolean indicating if a +// sub-dictionary was accessed. nameNode is the particular function or method being +// called/referenced, and targs are the type arguments. +func (g *genInst) getDictOrSubdict(declInfo *instInfo, n ir.Node, nameNode *ir.Name, targs []*types.Type, isMeth bool) (ir.Node, bool) { + var dict ir.Node + usingSubdict := false + if declInfo != nil { + entry := -1 + for i, de := range declInfo.dictInfo.subDictCalls { + if n == de.callNode { + entry = declInfo.dictInfo.startSubDict + i + break + } + } + // If the entry is not found, it may be that this node did not have + // any type args that depend on type params, so we need a main + // dictionary, not a sub-dictionary. + if entry >= 0 { + dict = getDictionaryEntry(n.Pos(), declInfo.dictParam, entry, declInfo.dictInfo.dictLen) + usingSubdict = true + } + } + if !usingSubdict { + dict = g.getDictionaryValue(n.Pos(), nameNode, targs, isMeth) + } + return dict, usingSubdict +} + +// checkFetchBody checks if a generic body can be fetched, but hasn't been loaded +// yet. If so, it imports the body. +func checkFetchBody(nameNode *ir.Name) { + if nameNode.Func.Body == nil && nameNode.Func.Inl != nil { + // If there is no body yet but Func.Inl exists, then we can + // import the whole generic body. + assert(nameNode.Func.Inl.Cost == 1 && nameNode.Sym().Pkg != types.LocalPkg) + typecheck.ImportBody(nameNode.Func) + assert(nameNode.Func.Inl.Body != nil) + nameNode.Func.Body = nameNode.Func.Inl.Body + nameNode.Func.Dcl = nameNode.Func.Inl.Dcl + } +} + +// getInstantiation gets the instantiantion and dictionary of the function or method nameNode +// with the type arguments shapes. If the instantiated function is not already +// cached, then it calls genericSubst to create the new instantiation. +func (g *genInst) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth bool) *instInfo { + if nameNode.Func == nil { + // If nameNode.Func is nil, this must be a reference to a method of + // an imported instantiated type. We will have already called + // g.instantiateMethods() on the fully-instantiated type, so + // g.instInfoMap[sym] will be non-nil below. + rcvr := nameNode.Type().Recv() + if rcvr == nil || !deref(rcvr.Type).IsFullyInstantiated() { + base.FatalfAt(nameNode.Pos(), "Unexpected function instantiation %v with no body", nameNode) + } + } else { + checkFetchBody(nameNode) + } + + var tparams []*types.Type + if isMeth { + // Get the type params from the method receiver (after skipping + // over any pointer) + recvType := nameNode.Type().Recv().Type + recvType = deref(recvType) + if recvType.IsFullyInstantiated() { + // Get the type of the base generic type, so we get + // its original typeparams. + recvType = recvType.OrigType() + } + tparams = recvType.RParams() + } else { + fields := nameNode.Type().TParams().Fields().Slice() + tparams = make([]*types.Type, len(fields)) + for i, f := range fields { + tparams[i] = f.Type + } + } + + // Convert any non-shape type arguments to their shape, so we can reduce the + // number of instantiations we have to generate. You can actually have a mix + // of shape and non-shape arguments, because of inferred or explicitly + // specified concrete type args. + s1 := make([]*types.Type, len(shapes)) + for i, t := range shapes { + var tparam *types.Type + // Shapes are grouped differently for structural types, so we + // pass the type param to Shapify(), so we can distinguish. + tparam = tparams[i] + if !t.IsShape() { + s1[i] = typecheck.Shapify(t, i, tparam) + } else { + // Already a shape, but make sure it has the correct index. + s1[i] = typecheck.Shapify(shapes[i].Underlying(), i, tparam) + } + } + shapes = s1 + + sym := typecheck.MakeFuncInstSym(nameNode.Sym(), shapes, false, isMeth) + info := g.instInfoMap[sym] + if info == nil { + // If instantiation doesn't exist yet, create it and add + // to the list of decls. + info = &instInfo{ + dictInfo: &dictInfo{}, + } + info.dictInfo.shapeToBound = make(map[*types.Type]*types.Type) + + if sym.Def != nil { + // This instantiation must have been imported from another + // package (because it was needed for inlining), so we should + // not re-generate it and have conflicting definitions for the + // symbol (issue #50121). It will have already gone through the + // dictionary transformations of dictPass, so we don't actually + // need the info.dictParam and info.shapeToBound info filled in + // below. We just set the imported instantiation as info.fun. + assert(sym.Pkg != types.LocalPkg) + info.fun = sym.Def.(*ir.Name).Func + assert(info.fun != nil) + g.instInfoMap[sym] = info + return info + } + + // genericSubst fills in info.dictParam and info.shapeToBound. + st := g.genericSubst(sym, nameNode, tparams, shapes, isMeth, info) + info.fun = st + g.instInfoMap[sym] = info + + // getInstInfo fills in info.dictInfo. + g.getInstInfo(st, shapes, info) + if base.Flag.W > 1 { + ir.Dump(fmt.Sprintf("\nstenciled %v", st), st) + } + + // This ensures that the linker drops duplicates of this instantiation. + // All just works! + st.SetDupok(true) + typecheck.Target.Decls = append(typecheck.Target.Decls, st) + g.newInsts = append(g.newInsts, st) + } + return info +} + +// Struct containing info needed for doing the substitution as we create the +// instantiation of a generic function with specified type arguments. +type subster struct { + g *genInst + isMethod bool // If a method is being instantiated + newf *ir.Func // Func node for the new stenciled function + ts typecheck.Tsubster + info *instInfo // Place to put extra info in the instantiation + + // Map from non-nil, non-ONAME node n to slice of all m, where m.Defn = n + defnMap map[ir.Node][]**ir.Name +} + +// genericSubst returns a new function with name newsym. The function is an +// instantiation of a generic function or method specified by namedNode with type +// args shapes. For a method with a generic receiver, it returns an instantiated +// function type where the receiver becomes the first parameter. For either a generic +// method or function, a dictionary parameter is the added as the very first +// parameter. genericSubst fills in info.dictParam and info.shapeToBound. +func (g *genInst) genericSubst(newsym *types.Sym, nameNode *ir.Name, tparams []*types.Type, shapes []*types.Type, isMethod bool, info *instInfo) *ir.Func { + gf := nameNode.Func + // Pos of the instantiated function is same as the generic function + newf := ir.NewFunc(gf.Pos()) + newf.Pragma = gf.Pragma // copy over pragmas from generic function to stenciled implementation. + newf.Nname = ir.NewNameAt(gf.Pos(), newsym) + newf.Nname.Func = newf + newf.Nname.Defn = newf + newsym.Def = newf.Nname + savef := ir.CurFunc + // transformCall/transformReturn (called during stenciling of the body) + // depend on ir.CurFunc being set. + ir.CurFunc = newf + + assert(len(tparams) == len(shapes)) + + subst := &subster{ + g: g, + isMethod: isMethod, + newf: newf, + info: info, + ts: typecheck.Tsubster{ + Tparams: tparams, + Targs: shapes, + Vars: make(map[*ir.Name]*ir.Name), + }, + defnMap: make(map[ir.Node][]**ir.Name), + } + + newf.Dcl = make([]*ir.Name, 0, len(gf.Dcl)+1) + + // Create the needed dictionary param + dictionarySym := newsym.Pkg.Lookup(typecheck.LocalDictName) + dictionaryType := types.Types[types.TUINTPTR] + dictionaryName := ir.NewNameAt(gf.Pos(), dictionarySym) + typed(dictionaryType, dictionaryName) + dictionaryName.Class = ir.PPARAM + dictionaryName.Curfn = newf + newf.Dcl = append(newf.Dcl, dictionaryName) + for _, n := range gf.Dcl { + if n.Sym().Name == typecheck.LocalDictName { + panic("already has dictionary") + } + newf.Dcl = append(newf.Dcl, subst.localvar(n)) + } + dictionaryArg := types.NewField(gf.Pos(), dictionarySym, dictionaryType) + dictionaryArg.Nname = dictionaryName + info.dictParam = dictionaryName + + // We add the dictionary as the first parameter in the function signature. + // We also transform a method type to the corresponding function type + // (make the receiver be the next parameter after the dictionary). + oldt := nameNode.Type() + var args []*types.Field + args = append(args, dictionaryArg) + args = append(args, oldt.Recvs().FieldSlice()...) + args = append(args, oldt.Params().FieldSlice()...) + + // Replace the types in the function signature via subst.fields. + // Ugly: also, we have to insert the Name nodes of the parameters/results into + // the function type. The current function type has no Nname fields set, + // because it came via conversion from the types2 type. + newt := types.NewSignature(oldt.Pkg(), nil, nil, + subst.fields(ir.PPARAM, args, newf.Dcl), + subst.fields(ir.PPARAMOUT, oldt.Results().FieldSlice(), newf.Dcl)) + + typed(newt, newf.Nname) + ir.MarkFunc(newf.Nname) + newf.SetTypecheck(1) + + // Make sure name/type of newf is set before substituting the body. + newf.Body = subst.list(gf.Body) + if len(newf.Body) == 0 { + // Ensure the body is nonempty, for issue 49524. + // TODO: have some other way to detect the difference between + // a function declared with no body, vs. one with an empty body? + newf.Body = append(newf.Body, ir.NewBlockStmt(gf.Pos(), nil)) + } + + if len(subst.defnMap) > 0 { + base.Fatalf("defnMap is not empty") + } + + for i, tp := range tparams { + info.dictInfo.shapeToBound[shapes[i]] = subst.ts.Typ(tp.Bound()) + } + + ir.CurFunc = savef + + return subst.newf +} + +// localvar creates a new name node for the specified local variable and enters it +// in subst.vars. It substitutes type arguments for type parameters in the type of +// name as needed. +func (subst *subster) localvar(name *ir.Name) *ir.Name { + m := ir.NewNameAt(name.Pos(), name.Sym()) + if name.IsClosureVar() { + m.SetIsClosureVar(true) + } + m.SetType(subst.ts.Typ(name.Type())) + m.BuiltinOp = name.BuiltinOp + m.Curfn = subst.newf + m.Class = name.Class + assert(name.Class != ir.PEXTERN && name.Class != ir.PFUNC) + m.Func = name.Func + subst.ts.Vars[name] = m + m.SetTypecheck(1) + m.DictIndex = name.DictIndex + if name.Defn != nil { + if name.Defn.Op() == ir.ONAME { + // This is a closure variable, so its Defn is the outer + // captured variable, which has already been substituted. + m.Defn = subst.node(name.Defn) + } else { + // The other values of Defn are nodes in the body of the + // function, so just remember the mapping so we can set Defn + // properly in node() when we create the new body node. We + // always call localvar() on all the local variables before + // we substitute the body. + slice := subst.defnMap[name.Defn] + subst.defnMap[name.Defn] = append(slice, &m) + } + } + if name.Outer != nil { + m.Outer = subst.node(name.Outer).(*ir.Name) + } + + return m +} + +// getDictionaryEntry gets the i'th entry in the dictionary dict. +func getDictionaryEntry(pos src.XPos, dict *ir.Name, i int, size int) ir.Node { + // Convert dictionary to *[N]uintptr + // All entries in the dictionary are pointers. They all point to static data, though, so we + // treat them as uintptrs so the GC doesn't need to keep track of them. + d := ir.NewConvExpr(pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], dict) + d.SetTypecheck(1) + d = ir.NewConvExpr(pos, ir.OCONVNOP, types.NewArray(types.Types[types.TUINTPTR], int64(size)).PtrTo(), d) + d.SetTypecheck(1) + types.CheckSize(d.Type().Elem()) + + // Load entry i out of the dictionary. + deref := ir.NewStarExpr(pos, d) + typed(d.Type().Elem(), deref) + idx := ir.NewConstExpr(constant.MakeUint64(uint64(i)), dict) // TODO: what to set orig to? + typed(types.Types[types.TUINTPTR], idx) + r := ir.NewIndexExpr(pos, deref, idx) + typed(types.Types[types.TUINTPTR], r) + return r +} + +// getDictionaryType returns a *runtime._type from the dictionary entry i (which +// refers to a type param or a derived type that uses type params). It uses the +// specified dictionary dictParam, rather than the one in info.dictParam. +func getDictionaryType(info *instInfo, dictParam *ir.Name, pos src.XPos, i int) ir.Node { + if i < 0 || i >= info.dictInfo.startSubDict { + base.Fatalf(fmt.Sprintf("bad dict index %d", i)) + } + + r := getDictionaryEntry(pos, dictParam, i, info.dictInfo.startSubDict) + // change type of retrieved dictionary entry to *byte, which is the + // standard typing of a *runtime._type in the compiler + typed(types.Types[types.TUINT8].PtrTo(), r) + return r +} + +// node is like DeepCopy(), but substitutes ONAME nodes based on subst.ts.vars, and +// also descends into closures. It substitutes type arguments for type parameters in +// all the new nodes and does the transformations that were delayed on the generic +// function. +func (subst *subster) node(n ir.Node) ir.Node { + // Use closure to capture all state needed by the ir.EditChildren argument. + var edit func(ir.Node) ir.Node + edit = func(x ir.Node) ir.Node { + // Analogous to ir.SetPos() at beginning of typecheck.typecheck() - + // allows using base.Pos during the transform functions, just like + // the tc*() functions. + ir.SetPos(x) + switch x.Op() { + case ir.OTYPE: + return ir.TypeNode(subst.ts.Typ(x.Type())) + + case ir.ONAME: + if v := subst.ts.Vars[x.(*ir.Name)]; v != nil { + return v + } + if ir.IsBlank(x) { + // Special case, because a blank local variable is + // not in the fn.Dcl list. + m := ir.NewNameAt(x.Pos(), ir.BlankNode.Sym()) + return typed(subst.ts.Typ(x.Type()), m) + } + return x + case ir.ONONAME: + // This handles the identifier in a type switch guard + fallthrough + case ir.OLITERAL, ir.ONIL: + if x.Sym() != nil { + return x + } + } + m := ir.Copy(x) + + slice, ok := subst.defnMap[x] + if ok { + // We just copied a non-ONAME node which was the Defn value + // of a local variable. Set the Defn value of the copied + // local variable to this new Defn node. + for _, ptr := range slice { + (*ptr).Defn = m + } + delete(subst.defnMap, x) + } + + if _, isExpr := m.(ir.Expr); isExpr { + t := x.Type() + if t == nil { + // Check for known cases where t can be nil (call + // that has no return values, and key expressions) + // and otherwise cause a fatal error. + _, isCallExpr := m.(*ir.CallExpr) + _, isStructKeyExpr := m.(*ir.StructKeyExpr) + _, isKeyExpr := m.(*ir.KeyExpr) + if !isCallExpr && !isStructKeyExpr && !isKeyExpr && x.Op() != ir.OPANIC && + x.Op() != ir.OCLOSE { + base.FatalfAt(m.Pos(), "Nil type for %v", x) + } + } else if x.Op() != ir.OCLOSURE { + m.SetType(subst.ts.Typ(x.Type())) + } + } + + ir.EditChildren(m, edit) + + m.SetTypecheck(1) + + // Do the transformations that we delayed on the generic function + // node, now that we have substituted in the type args. + switch x.Op() { + case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE: + transformCompare(m.(*ir.BinaryExpr)) + + case ir.OSLICE, ir.OSLICE3: + transformSlice(m.(*ir.SliceExpr)) + + case ir.OADD: + m = transformAdd(m.(*ir.BinaryExpr)) + + case ir.OINDEX: + transformIndex(m.(*ir.IndexExpr)) + + case ir.OAS2: + as2 := m.(*ir.AssignListStmt) + transformAssign(as2, as2.Lhs, as2.Rhs) + + case ir.OAS: + as := m.(*ir.AssignStmt) + if as.Y != nil { + // transformAssign doesn't handle the case + // of zeroing assignment of a dcl (rhs[0] is nil). + lhs, rhs := []ir.Node{as.X}, []ir.Node{as.Y} + transformAssign(as, lhs, rhs) + as.X, as.Y = lhs[0], rhs[0] + } + + case ir.OASOP: + as := m.(*ir.AssignOpStmt) + transformCheckAssign(as, as.X) + + case ir.ORETURN: + transformReturn(m.(*ir.ReturnStmt)) + + case ir.OSEND: + transformSend(m.(*ir.SendStmt)) + + case ir.OSELECT: + transformSelect(m.(*ir.SelectStmt)) + + case ir.OCOMPLIT: + transformCompLit(m.(*ir.CompLitExpr)) + + case ir.OADDR: + transformAddr(m.(*ir.AddrExpr)) + + case ir.OLITERAL: + t := m.Type() + if t != x.Type() { + // types2 will give us a constant with a type T, + // if an untyped constant is used with another + // operand of type T (in a provably correct way). + // When we substitute in the type args during + // stenciling, we now know the real type of the + // constant. We may then need to change the + // BasicLit.val to be the correct type (e.g. + // convert an int64Val constant to a floatVal + // constant). + m.SetType(types.UntypedInt) // use any untyped type for DefaultLit to work + m = typecheck.DefaultLit(m, t) + } + + case ir.OXDOT: + // Finish the transformation of an OXDOT, unless this is + // bound call or field access on a type param. A bound call + // or field access on a type param will be transformed during + // the dictPass. Otherwise, m will be transformed to an + // OMETHVALUE node. It will be transformed to an ODOTMETH or + // ODOTINTER node if we find in the OCALL case below that the + // method value is actually called. + mse := m.(*ir.SelectorExpr) + if src := mse.X.Type(); !src.IsShape() { + transformDot(mse, false) + } + + case ir.OCALL: + call := m.(*ir.CallExpr) + switch call.X.Op() { + case ir.OTYPE: + // Transform the conversion, now that we know the + // type argument. + m = transformConvCall(call) + + case ir.OMETHVALUE, ir.OMETHEXPR: + // Redo the transformation of OXDOT, now that we + // know the method value is being called. Then + // transform the call. + call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT) + transformDot(call.X.(*ir.SelectorExpr), true) + transformCall(call) + + case ir.ODOT, ir.ODOTPTR: + // An OXDOT for a generic receiver was resolved to + // an access to a field which has a function + // value. Transform the call to that function, now + // that the OXDOT was resolved. + transformCall(call) + + case ir.ONAME: + name := call.X.Name() + if name.BuiltinOp != ir.OXXX { + m = transformBuiltin(call) + } else { + // This is the case of a function value that was a + // type parameter (implied to be a function via a + // structural constraint) which is now resolved. + transformCall(call) + } + + case ir.OFUNCINST: + // A call with an OFUNCINST will get transformed + // in stencil() once we have created & attached the + // instantiation to be called. + // We must transform the arguments of the call now, though, + // so that any needed CONVIFACE nodes are exposed, + // so the dictionary format is correct. + transformEarlyCall(call) + + case ir.OXDOT: + // This is the case of a bound call or a field access + // on a typeparam, which will be handled in the + // dictPass. As with OFUNCINST, we must transform the + // arguments of the call now, so any needed CONVIFACE + // nodes are exposed. + transformEarlyCall(call) + + case ir.ODOTTYPE, ir.ODOTTYPE2: + // These are DOTTYPEs that could get transformed into + // ODYNAMIC DOTTYPEs by the dict pass. + + default: + // Transform a call for all other values of + // call.X.Op() that don't require any special + // handling. + transformCall(call) + + } + + case ir.OCLOSURE: + // We're going to create a new closure from scratch, so clear m + // to avoid using the ir.Copy by accident until we reassign it. + m = nil + + x := x.(*ir.ClosureExpr) + // Need to duplicate x.Func.Nname, x.Func.Dcl, x.Func.ClosureVars, and + // x.Func.Body. + oldfn := x.Func + newfn := ir.NewClosureFunc(oldfn.Pos(), subst.newf != nil) + ir.NameClosure(newfn.OClosure, subst.newf) + + saveNewf := subst.newf + ir.CurFunc = newfn + subst.newf = newfn + newfn.Dcl = subst.namelist(oldfn.Dcl) + + // Make a closure variable for the dictionary of the + // containing function. + cdict := ir.CaptureName(oldfn.Pos(), newfn, subst.info.dictParam) + typed(types.Types[types.TUINTPTR], cdict) + ir.FinishCaptureNames(oldfn.Pos(), saveNewf, newfn) + newfn.ClosureVars = append(newfn.ClosureVars, subst.namelist(oldfn.ClosureVars)...) + + // Copy that closure variable to a local one. + // Note: this allows the dictionary to be captured by child closures. + // See issue 47723. + ldict := ir.NewNameAt(x.Pos(), newfn.Sym().Pkg.Lookup(typecheck.LocalDictName)) + typed(types.Types[types.TUINTPTR], ldict) + ldict.Class = ir.PAUTO + ldict.Curfn = newfn + newfn.Dcl = append(newfn.Dcl, ldict) + as := ir.NewAssignStmt(x.Pos(), ldict, cdict) + as.SetTypecheck(1) + ldict.Defn = as + newfn.Body.Append(as) + + // Create inst info for the instantiated closure. The dict + // param is the closure variable for the dictionary of the + // outer function. Since the dictionary is shared, use the + // same dictInfo. + cinfo := &instInfo{ + fun: newfn, + dictParam: ldict, + dictInfo: subst.info.dictInfo, + } + subst.g.instInfoMap[newfn.Nname.Sym()] = cinfo + + typed(subst.ts.Typ(oldfn.Nname.Type()), newfn.Nname) + typed(newfn.Nname.Type(), newfn.OClosure) + newfn.SetTypecheck(1) + + outerinfo := subst.info + subst.info = cinfo + // Make sure type of closure function is set before doing body. + newfn.Body.Append(subst.list(oldfn.Body)...) + subst.info = outerinfo + subst.newf = saveNewf + ir.CurFunc = saveNewf + + m = ir.UseClosure(newfn.OClosure, typecheck.Target) + subst.g.newInsts = append(subst.g.newInsts, m.(*ir.ClosureExpr).Func) + m.(*ir.ClosureExpr).SetInit(subst.list(x.Init())) + + case ir.OSWITCH: + m := m.(*ir.SwitchStmt) + if m.Tag != nil && m.Tag.Op() == ir.OTYPESW { + break // Nothing to do here for type switches. + } + if m.Tag != nil && !m.Tag.Type().IsEmptyInterface() && m.Tag.Type().HasShape() { + // To implement a switch on a value that is or has a type parameter, we first convert + // that thing we're switching on to an interface{}. + m.Tag = assignconvfn(m.Tag, types.Types[types.TINTER]) + } + for _, c := range m.Cases { + for i, x := range c.List { + // If we have a case that is or has a type parameter, convert that case + // to an interface{}. + if !x.Type().IsEmptyInterface() && x.Type().HasShape() { + c.List[i] = assignconvfn(x, types.Types[types.TINTER]) + } + } + } + + } + return m + } + + return edit(n) +} + +// dictPass takes a function instantiation and does the transformations on the +// operations that need to make use of the dictionary param. +func (g *genInst) dictPass(info *instInfo) { + savef := ir.CurFunc + ir.CurFunc = info.fun + + var edit func(ir.Node) ir.Node + edit = func(m ir.Node) ir.Node { + ir.EditChildren(m, edit) + + switch m.Op() { + case ir.OCLOSURE: + newf := m.(*ir.ClosureExpr).Func + ir.CurFunc = newf + outerinfo := info + info = g.instInfoMap[newf.Nname.Sym()] + + body := newf.Body + for i, n := range body { + body[i] = edit(n) + } + + info = outerinfo + ir.CurFunc = info.fun + + case ir.OXDOT: + // This is the case of a dot access on a type param. This is + // typically a bound call on the type param, but could be a + // field access, if the constraint has a single structural type. + mse := m.(*ir.SelectorExpr) + src := mse.X.Type() + assert(src.IsShape()) + + if mse.X.Op() == ir.OTYPE { + // Method expression T.M + m = g.buildClosure2(info, m) + // No need for transformDot - buildClosure2 has already + // transformed to OCALLINTER/ODOTINTER. + } else { + // If we can't find the selected method in the + // AllMethods of the bound, then this must be an access + // to a field of a structural type. If so, we skip the + // dictionary lookups - transformDot() will convert to + // the desired direct field access. + if isBoundMethod(info.dictInfo, mse) { + dst := info.dictInfo.shapeToBound[mse.X.Type()] + // Implement x.M as a conversion-to-bound-interface + // 1) convert x to the bound interface + // 2) call M on that interface + if src.IsInterface() { + // If type arg is an interface (unusual case), + // we do a type assert to the type bound. + mse.X = assertToBound(info, info.dictParam, m.Pos(), mse.X, dst) + } else { + mse.X = convertUsingDictionary(info, info.dictParam, m.Pos(), mse.X, m, dst, true) + // Note: we set nonEscaping==true, because we can assume the backing store for the + // interface conversion doesn't escape. The method call will immediately go to + // a wrapper function which copies all the data out of the interface value. + // (It only matters for non-pointer-shaped interface conversions. See issue 50182.) + } + } + transformDot(mse, false) + } + case ir.OCALL: + call := m.(*ir.CallExpr) + op := call.X.Op() + if op == ir.OMETHVALUE { + // Redo the transformation of OXDOT, now that we + // know the method value is being called. + call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT) + transformDot(call.X.(*ir.SelectorExpr), true) + } + transformCall(call) + + case ir.OCONVIFACE: + if m.Type().IsEmptyInterface() && m.(*ir.ConvExpr).X.Type().IsEmptyInterface() { + // Was T->interface{}, after stenciling it is now interface{}->interface{}. + // No longer need the conversion. See issue 48276. + m.(*ir.ConvExpr).SetOp(ir.OCONVNOP) + break + } + mce := m.(*ir.ConvExpr) + // Note: x's argument is still typed as a type parameter. + // m's argument now has an instantiated type. + + if mce.X.Type().HasShape() || m.Type().HasShape() { + m = convertUsingDictionary(info, info.dictParam, m.Pos(), mce.X, m, m.Type(), false) + } + case ir.ODOTTYPE, ir.ODOTTYPE2: + if !m.Type().HasShape() { + break + } + dt := m.(*ir.TypeAssertExpr) + var rt ir.Node + if dt.Type().IsInterface() || dt.X.Type().IsEmptyInterface() { + ix := findDictType(info, m.Type()) + assert(ix >= 0) + rt = getDictionaryType(info, info.dictParam, dt.Pos(), ix) + } else { + // nonempty interface to noninterface. Need an itab. + ix := -1 + for i, ic := range info.dictInfo.itabConvs { + if ic == m { + ix = info.dictInfo.startItabConv + i + break + } + } + assert(ix >= 0) + rt = getDictionaryEntry(dt.Pos(), info.dictParam, ix, info.dictInfo.dictLen) + } + op := ir.ODYNAMICDOTTYPE + if m.Op() == ir.ODOTTYPE2 { + op = ir.ODYNAMICDOTTYPE2 + } + m = ir.NewDynamicTypeAssertExpr(dt.Pos(), op, dt.X, rt) + m.SetType(dt.Type()) + m.SetTypecheck(1) + case ir.OCASE: + if _, ok := m.(*ir.CommClause); ok { + // This is not a type switch. TODO: Should we use an OSWITCH case here instead of OCASE? + break + } + m := m.(*ir.CaseClause) + for i, c := range m.List { + if c.Op() == ir.OTYPE && c.Type().HasShape() { + // Use a *runtime._type for the dynamic type. + ix := findDictType(info, m.List[i].Type()) + assert(ix >= 0) + dt := ir.NewDynamicType(c.Pos(), getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.dictLen)) + + // For type switch from nonempty interfaces to non-interfaces, we need an itab as well. + if !m.List[i].Type().IsInterface() { + if _, ok := info.dictInfo.type2switchType[m.List[i]]; ok { + // Type switch from nonempty interface. We need a *runtime.itab + // for the dynamic type. + ix := -1 + for j, ic := range info.dictInfo.itabConvs { + if ic == m.List[i] { + ix = info.dictInfo.startItabConv + j + break + } + } + assert(ix >= 0) + dt.ITab = getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.dictLen) + } + } + typed(m.List[i].Type(), dt) + m.List[i] = dt + } + } + + } + return m + } + edit(info.fun) + ir.CurFunc = savef +} + +// findDictType looks for type t in the typeparams or derived types in the generic +// function info.gfInfo. This will indicate the dictionary entry with the +// correct concrete type for the associated instantiated function. +func findDictType(info *instInfo, t *types.Type) int { + for i, dt := range info.dictInfo.shapeParams { + if dt == t { + return i + } + } + for i, dt := range info.dictInfo.derivedTypes { + if types.IdenticalStrict(dt, t) { + return i + len(info.dictInfo.shapeParams) + } + } + return -1 +} + +// convertUsingDictionary converts instantiated value v (type v.Type()) to an interface +// type dst, by returning a new set of nodes that make use of a dictionary entry. in is the +// instantiated node of the CONVIFACE node or XDOT node (for a bound method call) that is causing the +// conversion. +// If nonEscaping is true, the caller guarantees that the backing store needed for the interface data +// word will not escape. +func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v ir.Node, in ir.Node, dst *types.Type, nonEscaping bool) ir.Node { + assert(v.Type().HasShape() || in.Type().HasShape()) + assert(dst.IsInterface()) + + if v.Type().IsInterface() { + // Converting from an interface. The shape-ness of the source doesn't really matter, as + // we'll be using the concrete type from the first interface word. + if dst.IsEmptyInterface() { + // Converting I2E. OCONVIFACE does that for us, and doesn't depend + // on what the empty interface was instantiated with. No dictionary entry needed. + v = ir.NewConvExpr(pos, ir.OCONVIFACE, dst, v) + v.SetTypecheck(1) + return v + } + if !in.Type().HasShape() { + // Regular OCONVIFACE works if the destination isn't parameterized. + v = ir.NewConvExpr(pos, ir.OCONVIFACE, dst, v) + v.SetTypecheck(1) + return v + } + + // We get the destination interface type from the dictionary and the concrete + // type from the argument's itab. Call runtime.convI2I to get the new itab. + tmp := typecheck.Temp(v.Type()) + as := ir.NewAssignStmt(pos, tmp, v) + as.SetTypecheck(1) + itab := ir.NewUnaryExpr(pos, ir.OITAB, tmp) + typed(types.Types[types.TUINTPTR].PtrTo(), itab) + idata := ir.NewUnaryExpr(pos, ir.OIDATA, tmp) + typed(types.Types[types.TUNSAFEPTR], idata) + + fn := typecheck.LookupRuntime("convI2I") + fn.SetTypecheck(1) + types.CalcSize(fn.Type()) + call := ir.NewCallExpr(pos, ir.OCALLFUNC, fn, nil) + typed(types.Types[types.TUINT8].PtrTo(), call) + ix := findDictType(info, in.Type()) + assert(ix >= 0) + inter := getDictionaryType(info, dictParam, pos, ix) + call.Args = []ir.Node{inter, itab} + i := ir.NewBinaryExpr(pos, ir.OEFACE, call, idata) + typed(dst, i) + i.PtrInit().Append(as) + return i + } + + var rt ir.Node + if !dst.IsEmptyInterface() { + // We should have an itab entry in the dictionary. Using this itab + // will be more efficient than converting to an empty interface first + // and then type asserting to dst. + ix := -1 + for i, ic := range info.dictInfo.itabConvs { + if ic == in { + ix = info.dictInfo.startItabConv + i + break + } + } + assert(ix >= 0) + rt = getDictionaryEntry(pos, dictParam, ix, info.dictInfo.dictLen) + } else { + ix := findDictType(info, v.Type()) + assert(ix >= 0) + // Load the actual runtime._type of the type parameter from the dictionary. + rt = getDictionaryType(info, dictParam, pos, ix) + } + + // Figure out what the data field of the interface will be. + data := ir.NewConvExpr(pos, ir.OCONVIDATA, nil, v) + typed(types.Types[types.TUNSAFEPTR], data) + data.NonEscaping = nonEscaping + + // Build an interface from the type and data parts. + var i ir.Node = ir.NewBinaryExpr(pos, ir.OEFACE, rt, data) + typed(dst, i) + return i +} + +func (subst *subster) namelist(l []*ir.Name) []*ir.Name { + s := make([]*ir.Name, len(l)) + for i, n := range l { + s[i] = subst.localvar(n) + } + return s +} + +func (subst *subster) list(l []ir.Node) []ir.Node { + s := make([]ir.Node, len(l)) + for i, n := range l { + s[i] = subst.node(n) + } + return s +} + +// fields sets the Nname field for the Field nodes inside a type signature, based +// on the corresponding in/out parameters in dcl. It depends on the in and out +// parameters being in order in dcl. +func (subst *subster) fields(class ir.Class, oldfields []*types.Field, dcl []*ir.Name) []*types.Field { + // Find the starting index in dcl of declarations of the class (either + // PPARAM or PPARAMOUT). + var i int + for i = range dcl { + if dcl[i].Class == class { + break + } + } + + // Create newfields nodes that are copies of the oldfields nodes, but + // with substitution for any type params, and with Nname set to be the node in + // Dcl for the corresponding PPARAM or PPARAMOUT. + newfields := make([]*types.Field, len(oldfields)) + for j := range oldfields { + newfields[j] = oldfields[j].Copy() + newfields[j].Type = subst.ts.Typ(oldfields[j].Type) + // A PPARAM field will be missing from dcl if its name is + // unspecified or specified as "_". So, we compare the dcl sym + // with the field sym (or sym of the field's Nname node). (Unnamed + // results still have a name like ~r2 in their Nname node.) If + // they don't match, this dcl (if there is one left) must apply to + // a later field. + if i < len(dcl) && (dcl[i].Sym() == oldfields[j].Sym || + (oldfields[j].Nname != nil && dcl[i].Sym() == oldfields[j].Nname.Sym())) { + newfields[j].Nname = dcl[i] + i++ + } + } + return newfields +} + +// deref does a single deref of type t, if it is a pointer type. +func deref(t *types.Type) *types.Type { + if t.IsPtr() { + return t.Elem() + } + return t +} + +// markTypeUsed marks type t as used in order to help avoid dead-code elimination of +// needed methods. +func markTypeUsed(t *types.Type, lsym *obj.LSym) { + if t.IsInterface() { + return + } + // TODO: This is somewhat overkill, we really only need it + // for types that are put into interfaces. + // Note: this relocation is also used in cmd/link/internal/ld/dwarf.go + reflectdata.MarkTypeUsedInInterface(t, lsym) +} + +// getDictionarySym returns the dictionary for the named generic function gf, which +// is instantiated with the type arguments targs. +func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool) *types.Sym { + if len(targs) == 0 { + base.Fatalf("%s should have type arguments", gf.Sym().Name) + } + + // Enforce that only concrete types can make it to here. + for _, t := range targs { + if t.HasShape() { + panic(fmt.Sprintf("shape %+v in dictionary for %s", t, gf.Sym().Name)) + } + } + + // Get a symbol representing the dictionary. + sym := typecheck.MakeDictSym(gf.Sym(), targs, isMeth) + + // Initialize the dictionary, if we haven't yet already. + lsym := sym.Linksym() + if len(lsym.P) > 0 { + // We already started creating this dictionary and its lsym. + return sym + } + + infoPrint("=== Creating dictionary %v\n", sym.Name) + off := 0 + // Emit an entry for each targ (concrete type or gcshape). + for _, t := range targs { + infoPrint(" * %v\n", t) + s := reflectdata.TypeLinksym(t) + off = objw.SymPtr(lsym, off, s, 0) + markTypeUsed(t, lsym) + } + + instInfo := g.getInstantiation(gf, targs, isMeth) + info := instInfo.dictInfo + + subst := typecheck.Tsubster{ + Tparams: info.shapeParams, + Targs: targs, + } + // Emit an entry for each derived type (after substituting targs) + for _, t := range info.derivedTypes { + ts := subst.Typ(t) + infoPrint(" - %v\n", ts) + s := reflectdata.TypeLinksym(ts) + off = objw.SymPtr(lsym, off, s, 0) + markTypeUsed(ts, lsym) + } + // Emit an entry for each subdictionary (after substituting targs) + for _, subDictInfo := range info.subDictCalls { + var sym *types.Sym + n := subDictInfo.callNode + switch n.Op() { + case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH: + call := n.(*ir.CallExpr) + if call.X.Op() == ir.OXDOT || call.X.Op() == ir.ODOTMETH { + var nameNode *ir.Name + se := call.X.(*ir.SelectorExpr) + if se.X.Type().IsShape() { + // This is a method call enabled by a type bound. + tparam := se.X.Type() + if call.X.Op() == ir.ODOTMETH { + // We need this extra check for method expressions, + // which don't add in the implicit XDOTs. + tmpse := ir.NewSelectorExpr(src.NoXPos, ir.OXDOT, se.X, se.Sel) + tmpse = typecheck.AddImplicitDots(tmpse) + tparam = tmpse.X.Type() + } + if !tparam.IsShape() { + // The method expression is not + // really on a typeparam. + break + } + ix := -1 + for i, shape := range info.shapeParams { + if shape == tparam { + ix = i + break + } + } + assert(ix >= 0) + recvType := targs[ix] + if recvType.IsInterface() || len(recvType.RParams()) == 0 { + // No sub-dictionary entry is + // actually needed, since the + // type arg is not an + // instantiated type that + // will have generic methods. + break + } + // This is a method call for an + // instantiated type, so we need a + // sub-dictionary. + targs := recvType.RParams() + genRecvType := recvType.OrigType() + nameNode = typecheck.Lookdot1(call.X, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name) + sym = g.getDictionarySym(nameNode, targs, true) + } else { + // This is the case of a normal + // method call on a generic type. + assert(subDictInfo.savedXNode == se) + sym = g.getSymForMethodCall(se, &subst) + } + } else { + inst, ok := call.X.(*ir.InstExpr) + if ok { + // Code hasn't been transformed yet + assert(subDictInfo.savedXNode == inst) + } + // If !ok, then the generic method/function call has + // already been transformed to a shape instantiation + // call. Either way, use the SelectorExpr/InstExpr + // node saved in info. + cex := subDictInfo.savedXNode + if se, ok := cex.(*ir.SelectorExpr); ok { + sym = g.getSymForMethodCall(se, &subst) + } else { + inst := cex.(*ir.InstExpr) + nameNode := inst.X.(*ir.Name) + subtargs := typecheck.TypesOf(inst.Targs) + for i, t := range subtargs { + subtargs[i] = subst.Typ(t) + } + sym = g.getDictionarySym(nameNode, subtargs, false) + } + } + + case ir.OFUNCINST: + inst := n.(*ir.InstExpr) + nameNode := inst.X.(*ir.Name) + subtargs := typecheck.TypesOf(inst.Targs) + for i, t := range subtargs { + subtargs[i] = subst.Typ(t) + } + sym = g.getDictionarySym(nameNode, subtargs, false) + + case ir.OXDOT, ir.OMETHEXPR, ir.OMETHVALUE: + sym = g.getSymForMethodCall(n.(*ir.SelectorExpr), &subst) + + default: + assert(false) + } + + if sym == nil { + // Unused sub-dictionary entry, just emit 0. + off = objw.Uintptr(lsym, off, 0) + infoPrint(" - Unused subdict entry\n") + } else { + off = objw.SymPtr(lsym, off, sym.Linksym(), 0) + infoPrint(" - Subdict %v\n", sym.Name) + } + } + + g.instantiateMethods() + delay := &delayInfo{ + gf: gf, + targs: targs, + sym: sym, + off: off, + isMeth: isMeth, + } + g.dictSymsToFinalize = append(g.dictSymsToFinalize, delay) + return sym +} + +// getSymForMethodCall gets the dictionary sym for a method call, method value, or method +// expression that has selector se. subst gives the substitution from shape types to +// concrete types. +func (g *genInst) getSymForMethodCall(se *ir.SelectorExpr, subst *typecheck.Tsubster) *types.Sym { + // For everything except method expressions, 'recvType = deref(se.X.Type)' would + // also give the receiver type. For method expressions with embedded types, we + // need to look at the type of the selection to get the final receiver type. + recvType := deref(se.Selection.Type.Recv().Type) + genRecvType := recvType.OrigType() + nameNode := typecheck.Lookdot1(se, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name) + subtargs := recvType.RParams() + s2targs := make([]*types.Type, len(subtargs)) + for i, t := range subtargs { + s2targs[i] = subst.Typ(t) + } + return g.getDictionarySym(nameNode, s2targs, true) +} + +// finalizeSyms finishes up all dictionaries on g.dictSymsToFinalize, by writing out +// any needed LSyms for itabs. The itab lsyms create wrappers which need various +// dictionaries and method instantiations to be complete, so, to avoid recursive +// dependencies, we finalize the itab lsyms only after all dictionaries syms and +// instantiations have been created. +func (g *genInst) finalizeSyms() { + for _, d := range g.dictSymsToFinalize { + infoPrint("=== Finalizing dictionary %s\n", d.sym.Name) + + lsym := d.sym.Linksym() + instInfo := g.getInstantiation(d.gf, d.targs, d.isMeth) + info := instInfo.dictInfo + + subst := typecheck.Tsubster{ + Tparams: info.shapeParams, + Targs: d.targs, + } + + // Emit an entry for each itab + for _, n := range info.itabConvs { + var srctype, dsttype *types.Type + switch n.Op() { + case ir.OXDOT, ir.OMETHVALUE: + se := n.(*ir.SelectorExpr) + srctype = subst.Typ(se.X.Type()) + dsttype = subst.Typ(info.shapeToBound[se.X.Type()]) + case ir.ODOTTYPE, ir.ODOTTYPE2: + srctype = subst.Typ(n.(*ir.TypeAssertExpr).Type()) + dsttype = subst.Typ(n.(*ir.TypeAssertExpr).X.Type()) + case ir.OCONVIFACE: + srctype = subst.Typ(n.(*ir.ConvExpr).X.Type()) + dsttype = subst.Typ(n.Type()) + case ir.OTYPE: + srctype = subst.Typ(n.Type()) + dsttype = subst.Typ(info.type2switchType[n]) + default: + base.Fatalf("itab entry with unknown op %s", n.Op()) + } + if srctype.IsInterface() || dsttype.IsEmptyInterface() { + // No itab is wanted if src type is an interface. We + // will use a type assert instead. + d.off = objw.Uintptr(lsym, d.off, 0) + infoPrint(" + Unused itab entry for %v\n", srctype) + } else { + // Make sure all new fully-instantiated types have + // their methods created before generating any itabs. + g.instantiateMethods() + itabLsym := reflectdata.ITabLsym(srctype, dsttype) + d.off = objw.SymPtr(lsym, d.off, itabLsym, 0) + markTypeUsed(srctype, lsym) + infoPrint(" + Itab for (%v,%v)\n", srctype, dsttype) + } + } + + objw.Global(lsym, int32(d.off), obj.DUPOK|obj.RODATA) + infoPrint("=== Finalized dictionary %s\n", d.sym.Name) + } + g.dictSymsToFinalize = nil +} + +func (g *genInst) getDictionaryValue(pos src.XPos, gf *ir.Name, targs []*types.Type, isMeth bool) ir.Node { + sym := g.getDictionarySym(gf, targs, isMeth) + + // Make (or reuse) a node referencing the dictionary symbol. + var n *ir.Name + if sym.Def != nil { + n = sym.Def.(*ir.Name) + } else { + // We set the position of a static dictionary to be the position of + // one of its uses. + n = ir.NewNameAt(pos, sym) + n.Curfn = ir.CurFunc + n.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter + n.SetTypecheck(1) + n.Class = ir.PEXTERN + sym.Def = n + } + + // Return the address of the dictionary. Addr node gets position that was passed in. + np := typecheck.NodAddrAt(pos, n) + // Note: treat dictionary pointers as uintptrs, so they aren't pointers + // with respect to GC. That saves on stack scanning work, write barriers, etc. + // We can get away with it because dictionaries are global variables. + // TODO: use a cast, or is typing directly ok? + np.SetType(types.Types[types.TUINTPTR]) + np.SetTypecheck(1) + return np +} + +// hasShapeNodes returns true if the type of any node in targs has a shape. +func hasShapeNodes(targs []ir.Node) bool { + for _, n := range targs { + if n.Type().HasShape() { + return true + } + } + return false +} + +// hasShapeTypes returns true if any type in targs has a shape. +func hasShapeTypes(targs []*types.Type) bool { + for _, t := range targs { + if t.HasShape() { + return true + } + } + return false +} + +// getInstInfo get the dictionary format for a function instantiation- type params, derived +// types, and needed subdictionaries and itabs. +func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instInfo) { + info := instInfo.dictInfo + info.shapeParams = shapes + + for _, t := range info.shapeParams { + b := info.shapeToBound[t] + if b.HasShape() { + // If a type bound is parameterized (unusual case), then we + // may need its derived type to do a type assert when doing a + // bound call for a type arg that is an interface. + addType(info, nil, b) + } + } + + for _, n := range st.Dcl { + addType(info, n, n.Type()) + n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1) + } + + if infoPrintMode { + fmt.Printf(">>> InstInfo for %v\n", st) + for _, t := range info.shapeParams { + fmt.Printf(" Typeparam %v\n", t) + } + } + + // Map to remember when we have seen an instantiated function value or method + // expression/value as part of a call, so we can determine when we encounter + // an uncalled function value or method expression/value. + callMap := make(map[ir.Node]bool) + + var visitFunc func(ir.Node) + visitFunc = func(n ir.Node) { + switch n.Op() { + case ir.OFUNCINST: + if !callMap[n] && hasShapeNodes(n.(*ir.InstExpr).Targs) { + infoPrint(" Closure&subdictionary required at generic function value %v\n", n.(*ir.InstExpr).X) + info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil}) + } + case ir.OMETHEXPR, ir.OMETHVALUE: + if !callMap[n] && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) && + len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 && + hasShapeTypes(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) { + if n.(*ir.SelectorExpr).X.Op() == ir.OTYPE { + infoPrint(" Closure&subdictionary required at generic meth expr %v\n", n) + } else { + infoPrint(" Closure&subdictionary required at generic meth value %v\n", n) + } + info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil}) + } + case ir.OCALL: + ce := n.(*ir.CallExpr) + if ce.X.Op() == ir.OFUNCINST { + callMap[ce.X] = true + if hasShapeNodes(ce.X.(*ir.InstExpr).Targs) { + infoPrint(" Subdictionary at generic function/method call: %v - %v\n", ce.X.(*ir.InstExpr).X, n) + // Save the instExpr node for the function call, + // since we will lose this information when the + // generic function call is transformed to a call + // on the shape instantiation. + info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: ce.X}) + } + } + // Note: this XDOT code is not actually needed as long as we + // continue to disable type parameters on RHS of type + // declarations (#45639). + if ce.X.Op() == ir.OXDOT { + callMap[ce.X] = true + if isBoundMethod(info, ce.X.(*ir.SelectorExpr)) { + infoPrint(" Optional subdictionary at generic bound call: %v\n", n) + info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil}) + } + } + case ir.OCALLMETH: + ce := n.(*ir.CallExpr) + if ce.X.Op() == ir.ODOTMETH && + len(deref(ce.X.(*ir.SelectorExpr).X.Type()).RParams()) > 0 { + callMap[ce.X] = true + if hasShapeTypes(deref(ce.X.(*ir.SelectorExpr).X.Type()).RParams()) { + infoPrint(" Subdictionary at generic method call: %v\n", n) + // Save the selector for the method call, since we + // will eventually lose this information when the + // generic method call is transformed into a + // function call on the method shape instantiation. + info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: ce.X}) + } + } + case ir.OCONVIFACE: + if n.Type().IsInterface() && !n.Type().IsEmptyInterface() && + (n.Type().HasShape() || n.(*ir.ConvExpr).X.Type().HasShape()) { + infoPrint(" Itab for interface conv: %v\n", n) + info.itabConvs = append(info.itabConvs, n) + } + case ir.OXDOT: + se := n.(*ir.SelectorExpr) + if isBoundMethod(info, se) { + infoPrint(" Itab for bound call: %v\n", n) + info.itabConvs = append(info.itabConvs, n) + } + case ir.ODOTTYPE, ir.ODOTTYPE2: + if !n.(*ir.TypeAssertExpr).Type().IsInterface() && !n.(*ir.TypeAssertExpr).X.Type().IsEmptyInterface() { + infoPrint(" Itab for dot type: %v\n", n) + info.itabConvs = append(info.itabConvs, n) + } + case ir.OCLOSURE: + // Visit the closure body and add all relevant entries to the + // dictionary of the outer function (closure will just use + // the dictionary of the outer function). + cfunc := n.(*ir.ClosureExpr).Func + for _, n1 := range cfunc.Body { + ir.Visit(n1, visitFunc) + } + for _, n := range cfunc.Dcl { + n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1) + } + case ir.OSWITCH: + ss := n.(*ir.SwitchStmt) + if ss.Tag != nil && ss.Tag.Op() == ir.OTYPESW && + !ss.Tag.(*ir.TypeSwitchGuard).X.Type().IsEmptyInterface() { + for _, cc := range ss.Cases { + for _, c := range cc.List { + if c.Op() == ir.OTYPE && c.Type().HasShape() { + // Type switch from a non-empty interface - might need an itab. + infoPrint(" Itab for type switch: %v\n", c) + info.itabConvs = append(info.itabConvs, c) + if info.type2switchType == nil { + info.type2switchType = map[ir.Node]*types.Type{} + } + info.type2switchType[c] = ss.Tag.(*ir.TypeSwitchGuard).X.Type() + } + } + } + } + } + addType(info, n, n.Type()) + } + + for _, stmt := range st.Body { + ir.Visit(stmt, visitFunc) + } + if infoPrintMode { + for _, t := range info.derivedTypes { + fmt.Printf(" Derived type %v\n", t) + } + fmt.Printf(">>> Done Instinfo\n") + } + info.startSubDict = len(info.shapeParams) + len(info.derivedTypes) + info.startItabConv = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + info.dictLen = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + len(info.itabConvs) +} + +// isBoundMethod returns true if the selection indicated by se is a bound method of +// se.X. se.X must be a shape type (i.e. substituted directly from a type param). If +// isBoundMethod returns false, then the selection must be a field access of a +// structural type. +func isBoundMethod(info *dictInfo, se *ir.SelectorExpr) bool { + bound := info.shapeToBound[se.X.Type()] + return typecheck.Lookdot1(se, se.Sel, bound, bound.AllMethods(), 1) != nil +} + +// addType adds t to info.derivedTypes if it is parameterized type (which is not +// just a simple shape) that is different from any existing type on +// info.derivedTypes. +func addType(info *dictInfo, n ir.Node, t *types.Type) { + if t == nil || !t.HasShape() { + return + } + if t.IsShape() { + return + } + if t.Kind() == types.TFUNC && n != nil && + (t.Recv() != nil || n.Op() == ir.ONAME && n.Name().Class == ir.PFUNC) { + // Don't use the type of a named generic function or method, + // since that is parameterized by other typeparams. + // (They all come from arguments of a FUNCINST node.) + return + } + if doubleCheck && !parameterizedBy(t, info.shapeParams) { + base.Fatalf("adding type with invalid parameters %+v", t) + } + if t.Kind() == types.TSTRUCT && t.IsFuncArgStruct() { + // Multiple return values are not a relevant new type (?). + return + } + // Ignore a derived type we've already added. + for _, et := range info.derivedTypes { + if types.IdenticalStrict(t, et) { + return + } + } + info.derivedTypes = append(info.derivedTypes, t) +} + +// parameterizedBy returns true if t is parameterized by (at most) params. +func parameterizedBy(t *types.Type, params []*types.Type) bool { + return parameterizedBy1(t, params, map[*types.Type]bool{}) +} +func parameterizedBy1(t *types.Type, params []*types.Type, visited map[*types.Type]bool) bool { + if visited[t] { + return true + } + visited[t] = true + + if t.Sym() != nil && len(t.RParams()) > 0 { + // This defined type is instantiated. Check the instantiating types. + for _, r := range t.RParams() { + if !parameterizedBy1(r, params, visited) { + return false + } + } + return true + } + if t.IsShape() { + // Check if t is one of the allowed parameters in scope. + for _, p := range params { + if p == t { + return true + } + } + // Couldn't find t in the list of allowed parameters. + return false + + } + switch t.Kind() { + case types.TARRAY, types.TPTR, types.TSLICE, types.TCHAN: + return parameterizedBy1(t.Elem(), params, visited) + + case types.TMAP: + return parameterizedBy1(t.Key(), params, visited) && parameterizedBy1(t.Elem(), params, visited) + + case types.TFUNC: + return parameterizedBy1(t.TParams(), params, visited) && parameterizedBy1(t.Recvs(), params, visited) && parameterizedBy1(t.Params(), params, visited) && parameterizedBy1(t.Results(), params, visited) + + case types.TSTRUCT: + for _, f := range t.Fields().Slice() { + if !parameterizedBy1(f.Type, params, visited) { + return false + } + } + return true + + case types.TINTER: + for _, f := range t.Methods().Slice() { + if !parameterizedBy1(f.Type, params, visited) { + return false + } + } + return true + + case types.TINT, types.TINT8, types.TINT16, types.TINT32, types.TINT64, + types.TUINT, types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64, + types.TUINTPTR, types.TBOOL, types.TSTRING, types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128, types.TUNSAFEPTR: + return true + + case types.TUNION: + for i := 0; i < t.NumTerms(); i++ { + tt, _ := t.Term(i) + if !parameterizedBy1(tt, params, visited) { + return false + } + } + return true + + default: + base.Fatalf("bad type kind %+v", t) + return true + } +} + +// startClosures starts creation of a closure that has the function type typ. It +// creates all the formal params and results according to the type typ. On return, +// the body and closure variables of the closure must still be filled in, and +// ir.UseClosure() called. +func startClosure(pos src.XPos, outer *ir.Func, typ *types.Type) (*ir.Func, []*types.Field, []*types.Field) { + // Make a new internal function. + fn := ir.NewClosureFunc(pos, outer != nil) + ir.NameClosure(fn.OClosure, outer) + + // Build formal argument and return lists. + var formalParams []*types.Field // arguments of closure + var formalResults []*types.Field // returns of closure + for i := 0; i < typ.NumParams(); i++ { + t := typ.Params().Field(i).Type + arg := ir.NewNameAt(pos, typecheck.LookupNum("a", i)) + if outer != nil { + arg.SetSym(outer.Sym().Pkg.Lookup(arg.Sym().Name)) + } + arg.Class = ir.PPARAM + typed(t, arg) + arg.Curfn = fn + fn.Dcl = append(fn.Dcl, arg) + f := types.NewField(pos, arg.Sym(), t) + f.Nname = arg + f.SetIsDDD(typ.Params().Field(i).IsDDD()) + formalParams = append(formalParams, f) + } + for i := 0; i < typ.NumResults(); i++ { + t := typ.Results().Field(i).Type + result := ir.NewNameAt(pos, typecheck.LookupNum("r", i)) // TODO: names not needed? + if outer != nil { + result.SetSym(outer.Sym().Pkg.Lookup(result.Sym().Name)) + } + result.Class = ir.PPARAMOUT + typed(t, result) + result.Curfn = fn + fn.Dcl = append(fn.Dcl, result) + f := types.NewField(pos, result.Sym(), t) + f.Nname = result + formalResults = append(formalResults, f) + } + + // Build an internal function with the right signature. + closureType := types.NewSignature(typ.Pkg(), nil, nil, formalParams, formalResults) + typed(closureType, fn.Nname) + typed(typ, fn.OClosure) + fn.SetTypecheck(1) + return fn, formalParams, formalResults + +} + +// assertToBound returns a new node that converts a node rcvr with interface type to +// the 'dst' interface type. +func assertToBound(info *instInfo, dictVar *ir.Name, pos src.XPos, rcvr ir.Node, dst *types.Type) ir.Node { + if dst.HasShape() { + ix := findDictType(info, dst) + assert(ix >= 0) + rt := getDictionaryType(info, dictVar, pos, ix) + rcvr = ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, rcvr, rt) + typed(dst, rcvr) + } else { + rcvr = ir.NewTypeAssertExpr(pos, rcvr, nil) + typed(dst, rcvr) + } + return rcvr +} + +// buildClosure2 makes a closure to implement a method expression m (generic form x) +// which has a shape type as receiver. If the receiver is exactly a shape (i.e. from +// a typeparam), then the body of the closure converts m.X (the receiver) to the +// interface bound type, and makes an interface call with the remaining arguments. +// +// The returned closure is fully substituted and has already had any needed +// transformations done. +func (g *genInst) buildClosure2(info *instInfo, m ir.Node) ir.Node { + outer := info.fun + pos := m.Pos() + typ := m.Type() // type of the closure + + fn, formalParams, formalResults := startClosure(pos, outer, typ) + + // Capture dictionary calculated in the outer function + dictVar := ir.CaptureName(pos, fn, info.dictParam) + typed(types.Types[types.TUINTPTR], dictVar) + + // Build arguments to call inside the closure. + var args []ir.Node + for i := 0; i < typ.NumParams(); i++ { + args = append(args, formalParams[i].Nname.(*ir.Name)) + } + + // Build call itself. This involves converting the first argument to the + // bound type (an interface) using the dictionary, and then making an + // interface call with the remaining arguments. + var innerCall ir.Node + rcvr := args[0] + args = args[1:] + assert(m.(*ir.SelectorExpr).X.Type().IsShape()) + dst := info.dictInfo.shapeToBound[m.(*ir.SelectorExpr).X.Type()] + if m.(*ir.SelectorExpr).X.Type().IsInterface() { + // If type arg is an interface (unusual case), we do a type assert to + // the type bound. + rcvr = assertToBound(info, dictVar, pos, rcvr, dst) + } else { + rcvr = convertUsingDictionary(info, dictVar, pos, rcvr, m, dst, false) + } + dot := ir.NewSelectorExpr(pos, ir.ODOTINTER, rcvr, m.(*ir.SelectorExpr).Sel) + dot.Selection = typecheck.Lookdot1(dot, dot.Sel, dot.X.Type(), dot.X.Type().AllMethods(), 1) + + typed(dot.Selection.Type, dot) + innerCall = ir.NewCallExpr(pos, ir.OCALLINTER, dot, args) + t := m.Type() + if t.NumResults() == 0 { + innerCall.SetTypecheck(1) + } else if t.NumResults() == 1 { + typed(t.Results().Field(0).Type, innerCall) + } else { + typed(t.Results(), innerCall) + } + if len(formalResults) > 0 { + innerCall = ir.NewReturnStmt(pos, []ir.Node{innerCall}) + innerCall.SetTypecheck(1) + } + fn.Body = []ir.Node{innerCall} + + // We're all done with the captured dictionary + ir.FinishCaptureNames(pos, outer, fn) + + // Do final checks on closure and return it. + return ir.UseClosure(fn.OClosure, typecheck.Target) +} diff --git a/src/cmd/compile/internal/noder/stmt.go b/src/cmd/compile/internal/noder/stmt.go new file mode 100644 index 0000000..a349a7e --- /dev/null +++ b/src/cmd/compile/internal/noder/stmt.go @@ -0,0 +1,353 @@ +// Copyright 2021 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 noder + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/syntax" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/internal/src" +) + +// stmts creates nodes for a slice of statements that form a scope. +func (g *irgen) stmts(stmts []syntax.Stmt) []ir.Node { + var nodes []ir.Node + types.Markdcl() + for _, stmt := range stmts { + switch s := g.stmt(stmt).(type) { + case nil: // EmptyStmt + case *ir.BlockStmt: + nodes = append(nodes, s.List...) + default: + nodes = append(nodes, s) + } + } + types.Popdcl() + return nodes +} + +func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { + base.Assert(g.exprStmtOK) + switch stmt := stmt.(type) { + case nil, *syntax.EmptyStmt: + return nil + case *syntax.LabeledStmt: + return g.labeledStmt(stmt) + case *syntax.BlockStmt: + return ir.NewBlockStmt(g.pos(stmt), g.blockStmt(stmt)) + case *syntax.ExprStmt: + return wrapname(g.pos(stmt.X), g.expr(stmt.X)) + case *syntax.SendStmt: + n := ir.NewSendStmt(g.pos(stmt), g.expr(stmt.Chan), g.expr(stmt.Value)) + if !g.delayTransform() { + transformSend(n) + } + n.SetTypecheck(1) + return n + case *syntax.DeclStmt: + if g.topFuncIsGeneric && len(stmt.DeclList) > 0 { + if _, ok := stmt.DeclList[0].(*syntax.TypeDecl); ok { + // TODO: remove this restriction. See issue 47631. + base.ErrorfAt(g.pos(stmt), "type declarations inside generic functions are not currently supported") + } + } + n := ir.NewBlockStmt(g.pos(stmt), nil) + g.decls(&n.List, stmt.DeclList) + return n + + case *syntax.AssignStmt: + if stmt.Op != 0 && stmt.Op != syntax.Def { + op := g.op(stmt.Op, binOps[:]) + var n *ir.AssignOpStmt + if stmt.Rhs == nil { + n = IncDec(g.pos(stmt), op, g.expr(stmt.Lhs)) + } else { + // Eval rhs before lhs, for compatibility with noder1 + rhs := g.expr(stmt.Rhs) + lhs := g.expr(stmt.Lhs) + n = ir.NewAssignOpStmt(g.pos(stmt), op, lhs, rhs) + } + if !g.delayTransform() { + transformAsOp(n) + } + n.SetTypecheck(1) + return n + } + + // Eval rhs before lhs, for compatibility with noder1 + rhs := g.exprList(stmt.Rhs) + names, lhs := g.assignList(stmt.Lhs, stmt.Op == syntax.Def) + + if len(lhs) == 1 && len(rhs) == 1 { + n := ir.NewAssignStmt(g.pos(stmt), lhs[0], rhs[0]) + n.Def = initDefn(n, names) + + if !g.delayTransform() { + lhs, rhs := []ir.Node{n.X}, []ir.Node{n.Y} + transformAssign(n, lhs, rhs) + n.X, n.Y = lhs[0], rhs[0] + } + n.SetTypecheck(1) + return n + } + + n := ir.NewAssignListStmt(g.pos(stmt), ir.OAS2, lhs, rhs) + n.Def = initDefn(n, names) + if !g.delayTransform() { + transformAssign(n, n.Lhs, n.Rhs) + } + n.SetTypecheck(1) + return n + + case *syntax.BranchStmt: + return ir.NewBranchStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), branchOps[:]), g.name(stmt.Label)) + case *syntax.CallStmt: + return ir.NewGoDeferStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), callOps[:]), g.expr(stmt.Call)) + case *syntax.ReturnStmt: + n := ir.NewReturnStmt(g.pos(stmt), g.exprList(stmt.Results)) + if !g.delayTransform() { + transformReturn(n) + } + n.SetTypecheck(1) + return n + case *syntax.IfStmt: + return g.ifStmt(stmt) + case *syntax.ForStmt: + return g.forStmt(stmt) + case *syntax.SelectStmt: + n := g.selectStmt(stmt) + + if !g.delayTransform() { + transformSelect(n.(*ir.SelectStmt)) + } + n.SetTypecheck(1) + return n + case *syntax.SwitchStmt: + return g.switchStmt(stmt) + + default: + g.unhandled("statement", stmt) + panic("unreachable") + } +} + +// TODO(mdempsky): Investigate replacing with switch statements or dense arrays. + +var branchOps = [...]ir.Op{ + syntax.Break: ir.OBREAK, + syntax.Continue: ir.OCONTINUE, + syntax.Fallthrough: ir.OFALL, + syntax.Goto: ir.OGOTO, +} + +var callOps = [...]ir.Op{ + syntax.Defer: ir.ODEFER, + syntax.Go: ir.OGO, +} + +func (g *irgen) tokOp(tok int, ops []ir.Op) ir.Op { + // TODO(mdempsky): Validate. + return ops[tok] +} + +func (g *irgen) op(op syntax.Operator, ops []ir.Op) ir.Op { + // TODO(mdempsky): Validate. + return ops[op] +} + +func (g *irgen) assignList(expr syntax.Expr, def bool) ([]*ir.Name, []ir.Node) { + if !def { + return nil, g.exprList(expr) + } + + var exprs []syntax.Expr + if list, ok := expr.(*syntax.ListExpr); ok { + exprs = list.ElemList + } else { + exprs = []syntax.Expr{expr} + } + + var names []*ir.Name + res := make([]ir.Node, len(exprs)) + for i, expr := range exprs { + expr := expr.(*syntax.Name) + if expr.Value == "_" { + res[i] = ir.BlankNode + continue + } + + if obj, ok := g.info.Uses[expr]; ok { + res[i] = g.obj(obj) + continue + } + + name, _ := g.def(expr) + names = append(names, name) + res[i] = name + } + + return names, res +} + +// initDefn marks the given names as declared by defn and populates +// its Init field with ODCL nodes. It then reports whether any names +// were so declared, which can be used to initialize defn.Def. +func initDefn(defn ir.InitNode, names []*ir.Name) bool { + if len(names) == 0 { + return false + } + + init := make([]ir.Node, len(names)) + for i, name := range names { + name.Defn = defn + init[i] = ir.NewDecl(name.Pos(), ir.ODCL, name) + } + defn.SetInit(init) + return true +} + +func (g *irgen) blockStmt(stmt *syntax.BlockStmt) []ir.Node { + return g.stmts(stmt.List) +} + +func (g *irgen) ifStmt(stmt *syntax.IfStmt) ir.Node { + init := g.stmt(stmt.Init) + n := ir.NewIfStmt(g.pos(stmt), g.expr(stmt.Cond), g.blockStmt(stmt.Then), nil) + if stmt.Else != nil { + e := g.stmt(stmt.Else) + if e.Op() == ir.OBLOCK { + e := e.(*ir.BlockStmt) + n.Else = e.List + } else { + n.Else = []ir.Node{e} + } + } + return g.init(init, n) +} + +// unpackTwo returns the first two nodes in list. If list has fewer +// than 2 nodes, then the missing nodes are replaced with nils. +func unpackTwo(list []ir.Node) (fst, snd ir.Node) { + switch len(list) { + case 0: + return nil, nil + case 1: + return list[0], nil + default: + return list[0], list[1] + } +} + +func (g *irgen) forStmt(stmt *syntax.ForStmt) ir.Node { + if r, ok := stmt.Init.(*syntax.RangeClause); ok { + names, lhs := g.assignList(r.Lhs, r.Def) + key, value := unpackTwo(lhs) + n := ir.NewRangeStmt(g.pos(r), key, value, g.expr(r.X), g.blockStmt(stmt.Body)) + n.Def = initDefn(n, names) + if key != nil { + transformCheckAssign(n, key) + } + if value != nil { + transformCheckAssign(n, value) + } + return n + } + + return ir.NewForStmt(g.pos(stmt), g.stmt(stmt.Init), g.expr(stmt.Cond), g.stmt(stmt.Post), g.blockStmt(stmt.Body)) +} + +func (g *irgen) selectStmt(stmt *syntax.SelectStmt) ir.Node { + body := make([]*ir.CommClause, len(stmt.Body)) + for i, clause := range stmt.Body { + body[i] = ir.NewCommStmt(g.pos(clause), g.stmt(clause.Comm), g.stmts(clause.Body)) + } + return ir.NewSelectStmt(g.pos(stmt), body) +} + +func (g *irgen) switchStmt(stmt *syntax.SwitchStmt) ir.Node { + pos := g.pos(stmt) + init := g.stmt(stmt.Init) + + var expr ir.Node + switch tag := stmt.Tag.(type) { + case *syntax.TypeSwitchGuard: + var ident *ir.Ident + if tag.Lhs != nil { + ident = ir.NewIdent(g.pos(tag.Lhs), g.name(tag.Lhs)) + } + expr = ir.NewTypeSwitchGuard(pos, ident, g.expr(tag.X)) + default: + expr = g.expr(tag) + } + + body := make([]*ir.CaseClause, len(stmt.Body)) + for i, clause := range stmt.Body { + // Check for an implicit clause variable before + // visiting body, because it may contain function + // literals that reference it, and then it'll be + // associated to the wrong function. + // + // Also, override its position to the clause's colon, so that + // dwarfgen can find the right scope for it later. + // TODO(mdempsky): We should probably just store the scope + // directly in the ir.Name. + var cv *ir.Name + if obj, ok := g.info.Implicits[clause]; ok { + cv = g.obj(obj) + cv.SetPos(g.makeXPos(clause.Colon)) + assert(expr.Op() == ir.OTYPESW) + cv.Defn = expr + } + body[i] = ir.NewCaseStmt(g.pos(clause), g.exprList(clause.Cases), g.stmts(clause.Body)) + body[i].Var = cv + } + + return g.init(init, ir.NewSwitchStmt(pos, expr, body)) +} + +func (g *irgen) labeledStmt(label *syntax.LabeledStmt) ir.Node { + sym := g.name(label.Label) + lhs := ir.NewLabelStmt(g.pos(label), sym) + ls := g.stmt(label.Stmt) + + // Attach label directly to control statement too. + switch ls := ls.(type) { + case *ir.ForStmt: + ls.Label = sym + case *ir.RangeStmt: + ls.Label = sym + case *ir.SelectStmt: + ls.Label = sym + case *ir.SwitchStmt: + ls.Label = sym + } + + l := []ir.Node{lhs} + if ls != nil { + if ls.Op() == ir.OBLOCK { + ls := ls.(*ir.BlockStmt) + l = append(l, ls.List...) + } else { + l = append(l, ls) + } + } + return ir.NewBlockStmt(src.NoXPos, l) +} + +func (g *irgen) init(init ir.Node, stmt ir.InitNode) ir.InitNode { + if init != nil { + stmt.SetInit([]ir.Node{init}) + } + return stmt +} + +func (g *irgen) name(name *syntax.Name) *types.Sym { + if name == nil { + return nil + } + return typecheck.Lookup(name.Value) +} diff --git a/src/cmd/compile/internal/noder/sync.go b/src/cmd/compile/internal/noder/sync.go new file mode 100644 index 0000000..7af558f --- /dev/null +++ b/src/cmd/compile/internal/noder/sync.go @@ -0,0 +1,187 @@ +// UNREVIEWED + +// Copyright 2021 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 noder + +import ( + "fmt" + "strings" +) + +// enableSync controls whether sync markers are written into unified +// IR's export data format and also whether they're expected when +// reading them back in. They're inessential to the correct +// functioning of unified IR, but are helpful during development to +// detect mistakes. +// +// When sync is enabled, writer stack frames will also be included in +// the export data. Currently, a fixed number of frames are included, +// controlled by -d=syncframes (default 0). +const enableSync = true + +// fmtFrames formats a backtrace for reporting reader/writer desyncs. +func fmtFrames(pcs ...uintptr) []string { + res := make([]string, 0, len(pcs)) + walkFrames(pcs, func(file string, line int, name string, offset uintptr) { + // Trim package from function name. It's just redundant noise. + name = strings.TrimPrefix(name, "cmd/compile/internal/noder.") + + res = append(res, fmt.Sprintf("%s:%v: %s +0x%v", file, line, name, offset)) + }) + return res +} + +type frameVisitor func(file string, line int, name string, offset uintptr) + +// syncMarker is an enum type that represents markers that may be +// written to export data to ensure the reader and writer stay +// synchronized. +type syncMarker int + +//go:generate stringer -type=syncMarker -trimprefix=sync + +// TODO(mdempsky): Cleanup unneeded sync markers. + +// TODO(mdempsky): Split these markers into public/stable markers, and +// private ones. Also, trim unused ones. +const ( + _ syncMarker = iota + syncNode + syncBool + syncInt64 + syncUint64 + syncString + syncPos + syncPkg + syncSym + syncSelector + syncKind + syncType + syncTypePkg + syncSignature + syncParam + syncOp + syncObject + syncExpr + syncStmt + syncDecl + syncConstDecl + syncFuncDecl + syncTypeDecl + syncVarDecl + syncPragma + syncValue + syncEOF + syncMethod + syncFuncBody + syncUse + syncUseObj + syncObjectIdx + syncTypeIdx + syncBOF + syncEntry + syncOpenScope + syncCloseScope + syncGlobal + syncLocal + syncDefine + syncDefLocal + syncUseLocal + syncDefGlobal + syncUseGlobal + syncTypeParams + syncUseLabel + syncDefLabel + syncFuncLit + syncCommonFunc + syncBodyRef + syncLinksymExt + syncHack + syncSetlineno + syncName + syncImportDecl + syncDeclNames + syncDeclName + syncExprList + syncExprs + syncWrapname + syncTypeExpr + syncTypeExprOrNil + syncChanDir + syncParams + syncCloseAnotherScope + syncSum + syncUnOp + syncBinOp + syncStructType + syncInterfaceType + syncPackname + syncEmbedded + syncStmts + syncStmtsFall + syncStmtFall + syncBlockStmt + syncIfStmt + syncForStmt + syncSwitchStmt + syncRangeStmt + syncCaseClause + syncCommClause + syncSelectStmt + syncDecls + syncLabeledStmt + syncCompLit + + sync1 + sync2 + sync3 + sync4 + + syncN + syncDefImplicit + syncUseName + syncUseObjLocal + syncAddLocal + syncBothSignature + syncSetUnderlying + syncLinkname + syncStmt1 + syncStmtsEnd + syncDeclare + syncTopDecls + syncTopConstDecl + syncTopFuncDecl + syncTopTypeDecl + syncTopVarDecl + syncObject1 + syncAddBody + syncLabel + syncFuncExt + syncMethExt + syncOptLabel + syncScalar + syncStmtDecls + syncDeclLocal + syncObjLocal + syncObjLocal1 + syncDeclareLocal + syncPublic + syncPrivate + syncRelocs + syncReloc + syncUseReloc + syncVarExt + syncPkgDef + syncTypeExt + syncVal + syncCodeObj + syncPosBase + syncLocalIdent + syncTypeParamNames + syncTypeParamBounds + syncImplicitTypes + syncObjectName +) diff --git a/src/cmd/compile/internal/noder/syncmarker_string.go b/src/cmd/compile/internal/noder/syncmarker_string.go new file mode 100644 index 0000000..655cafc --- /dev/null +++ b/src/cmd/compile/internal/noder/syncmarker_string.go @@ -0,0 +1,156 @@ +// Code generated by "stringer -type=syncMarker -trimprefix=sync"; DO NOT EDIT. + +package noder + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[syncNode-1] + _ = x[syncBool-2] + _ = x[syncInt64-3] + _ = x[syncUint64-4] + _ = x[syncString-5] + _ = x[syncPos-6] + _ = x[syncPkg-7] + _ = x[syncSym-8] + _ = x[syncSelector-9] + _ = x[syncKind-10] + _ = x[syncType-11] + _ = x[syncTypePkg-12] + _ = x[syncSignature-13] + _ = x[syncParam-14] + _ = x[syncOp-15] + _ = x[syncObject-16] + _ = x[syncExpr-17] + _ = x[syncStmt-18] + _ = x[syncDecl-19] + _ = x[syncConstDecl-20] + _ = x[syncFuncDecl-21] + _ = x[syncTypeDecl-22] + _ = x[syncVarDecl-23] + _ = x[syncPragma-24] + _ = x[syncValue-25] + _ = x[syncEOF-26] + _ = x[syncMethod-27] + _ = x[syncFuncBody-28] + _ = x[syncUse-29] + _ = x[syncUseObj-30] + _ = x[syncObjectIdx-31] + _ = x[syncTypeIdx-32] + _ = x[syncBOF-33] + _ = x[syncEntry-34] + _ = x[syncOpenScope-35] + _ = x[syncCloseScope-36] + _ = x[syncGlobal-37] + _ = x[syncLocal-38] + _ = x[syncDefine-39] + _ = x[syncDefLocal-40] + _ = x[syncUseLocal-41] + _ = x[syncDefGlobal-42] + _ = x[syncUseGlobal-43] + _ = x[syncTypeParams-44] + _ = x[syncUseLabel-45] + _ = x[syncDefLabel-46] + _ = x[syncFuncLit-47] + _ = x[syncCommonFunc-48] + _ = x[syncBodyRef-49] + _ = x[syncLinksymExt-50] + _ = x[syncHack-51] + _ = x[syncSetlineno-52] + _ = x[syncName-53] + _ = x[syncImportDecl-54] + _ = x[syncDeclNames-55] + _ = x[syncDeclName-56] + _ = x[syncExprList-57] + _ = x[syncExprs-58] + _ = x[syncWrapname-59] + _ = x[syncTypeExpr-60] + _ = x[syncTypeExprOrNil-61] + _ = x[syncChanDir-62] + _ = x[syncParams-63] + _ = x[syncCloseAnotherScope-64] + _ = x[syncSum-65] + _ = x[syncUnOp-66] + _ = x[syncBinOp-67] + _ = x[syncStructType-68] + _ = x[syncInterfaceType-69] + _ = x[syncPackname-70] + _ = x[syncEmbedded-71] + _ = x[syncStmts-72] + _ = x[syncStmtsFall-73] + _ = x[syncStmtFall-74] + _ = x[syncBlockStmt-75] + _ = x[syncIfStmt-76] + _ = x[syncForStmt-77] + _ = x[syncSwitchStmt-78] + _ = x[syncRangeStmt-79] + _ = x[syncCaseClause-80] + _ = x[syncCommClause-81] + _ = x[syncSelectStmt-82] + _ = x[syncDecls-83] + _ = x[syncLabeledStmt-84] + _ = x[syncCompLit-85] + _ = x[sync1-86] + _ = x[sync2-87] + _ = x[sync3-88] + _ = x[sync4-89] + _ = x[syncN-90] + _ = x[syncDefImplicit-91] + _ = x[syncUseName-92] + _ = x[syncUseObjLocal-93] + _ = x[syncAddLocal-94] + _ = x[syncBothSignature-95] + _ = x[syncSetUnderlying-96] + _ = x[syncLinkname-97] + _ = x[syncStmt1-98] + _ = x[syncStmtsEnd-99] + _ = x[syncDeclare-100] + _ = x[syncTopDecls-101] + _ = x[syncTopConstDecl-102] + _ = x[syncTopFuncDecl-103] + _ = x[syncTopTypeDecl-104] + _ = x[syncTopVarDecl-105] + _ = x[syncObject1-106] + _ = x[syncAddBody-107] + _ = x[syncLabel-108] + _ = x[syncFuncExt-109] + _ = x[syncMethExt-110] + _ = x[syncOptLabel-111] + _ = x[syncScalar-112] + _ = x[syncStmtDecls-113] + _ = x[syncDeclLocal-114] + _ = x[syncObjLocal-115] + _ = x[syncObjLocal1-116] + _ = x[syncDeclareLocal-117] + _ = x[syncPublic-118] + _ = x[syncPrivate-119] + _ = x[syncRelocs-120] + _ = x[syncReloc-121] + _ = x[syncUseReloc-122] + _ = x[syncVarExt-123] + _ = x[syncPkgDef-124] + _ = x[syncTypeExt-125] + _ = x[syncVal-126] + _ = x[syncCodeObj-127] + _ = x[syncPosBase-128] + _ = x[syncLocalIdent-129] + _ = x[syncTypeParamNames-130] + _ = x[syncTypeParamBounds-131] + _ = x[syncImplicitTypes-132] + _ = x[syncObjectName-133] +} + +const _syncMarker_name = "NodeBoolInt64Uint64StringPosPkgSymSelectorKindTypeTypePkgSignatureParamOpObjectExprStmtDeclConstDeclFuncDeclTypeDeclVarDeclPragmaValueEOFMethodFuncBodyUseUseObjObjectIdxTypeIdxBOFEntryOpenScopeCloseScopeGlobalLocalDefineDefLocalUseLocalDefGlobalUseGlobalTypeParamsUseLabelDefLabelFuncLitCommonFuncBodyRefLinksymExtHackSetlinenoNameImportDeclDeclNamesDeclNameExprListExprsWrapnameTypeExprTypeExprOrNilChanDirParamsCloseAnotherScopeSumUnOpBinOpStructTypeInterfaceTypePacknameEmbeddedStmtsStmtsFallStmtFallBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtCompLit1234NDefImplicitUseNameUseObjLocalAddLocalBothSignatureSetUnderlyingLinknameStmt1StmtsEndDeclareTopDeclsTopConstDeclTopFuncDeclTopTypeDeclTopVarDeclObject1AddBodyLabelFuncExtMethExtOptLabelScalarStmtDeclsDeclLocalObjLocalObjLocal1DeclareLocalPublicPrivateRelocsRelocUseRelocVarExtPkgDefTypeExtValCodeObjPosBaseLocalIdentTypeParamNamesTypeParamBoundsImplicitTypesObjectName" + +var _syncMarker_index = [...]uint16{0, 4, 8, 13, 19, 25, 28, 31, 34, 42, 46, 50, 57, 66, 71, 73, 79, 83, 87, 91, 100, 108, 116, 123, 129, 134, 137, 143, 151, 154, 160, 169, 176, 179, 184, 193, 203, 209, 214, 220, 228, 236, 245, 254, 264, 272, 280, 287, 297, 304, 314, 318, 327, 331, 341, 350, 358, 366, 371, 379, 387, 400, 407, 413, 430, 433, 437, 442, 452, 465, 473, 481, 486, 495, 503, 512, 518, 525, 535, 544, 554, 564, 574, 579, 590, 597, 598, 599, 600, 601, 602, 613, 620, 631, 639, 652, 665, 673, 678, 686, 693, 701, 713, 724, 735, 745, 752, 759, 764, 771, 778, 786, 792, 801, 810, 818, 827, 839, 845, 852, 858, 863, 871, 877, 883, 890, 893, 900, 907, 917, 931, 946, 959, 969} + +func (i syncMarker) String() string { + i -= 1 + if i < 0 || i >= syncMarker(len(_syncMarker_index)-1) { + return "syncMarker(" + strconv.FormatInt(int64(i+1), 10) + ")" + } + return _syncMarker_name[_syncMarker_index[i]:_syncMarker_index[i+1]] +} diff --git a/src/cmd/compile/internal/noder/transform.go b/src/cmd/compile/internal/noder/transform.go new file mode 100644 index 0000000..db28e8d --- /dev/null +++ b/src/cmd/compile/internal/noder/transform.go @@ -0,0 +1,1091 @@ +// Copyright 2021 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. + +// This file contains transformation functions on nodes, which are the +// transformations that the typecheck package does that are distinct from the +// typechecking functionality. These transform functions are pared-down copies of +// the original typechecking functions, with all code removed that is related to: +// +// - Detecting compile-time errors (already done by types2) +// - Setting the actual type of existing nodes (already done based on +// type info from types2) +// - Dealing with untyped constants (which types2 has already resolved) +// +// Each of the transformation functions requires that node passed in has its type +// and typecheck flag set. If the transformation function replaces or adds new +// nodes, it will set the type and typecheck flag for those new nodes. + +package noder + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "fmt" + "go/constant" +) + +// Transformation functions for expressions + +// transformAdd transforms an addition operation (currently just addition of +// strings). Corresponds to the "binary operators" case in typecheck.typecheck1. +func transformAdd(n *ir.BinaryExpr) ir.Node { + assert(n.Type() != nil && n.Typecheck() == 1) + l := n.X + if l.Type().IsString() { + var add *ir.AddStringExpr + if l.Op() == ir.OADDSTR { + add = l.(*ir.AddStringExpr) + add.SetPos(n.Pos()) + } else { + add = ir.NewAddStringExpr(n.Pos(), []ir.Node{l}) + } + r := n.Y + if r.Op() == ir.OADDSTR { + r := r.(*ir.AddStringExpr) + add.List.Append(r.List.Take()...) + } else { + add.List.Append(r) + } + typed(l.Type(), add) + return add + } + return n +} + +// Corresponds to typecheck.stringtoruneslit. +func stringtoruneslit(n *ir.ConvExpr) ir.Node { + if n.X.Op() != ir.OLITERAL || n.X.Val().Kind() != constant.String { + base.Fatalf("stringtoarraylit %v", n) + } + + var list []ir.Node + i := 0 + eltType := n.Type().Elem() + for _, r := range ir.StringVal(n.X) { + elt := ir.NewKeyExpr(base.Pos, ir.NewInt(int64(i)), ir.NewInt(int64(r))) + // Change from untyped int to the actual element type determined + // by types2. No need to change elt.Key, since the array indexes + // are just used for setting up the element ordering. + elt.Value.SetType(eltType) + list = append(list, elt) + i++ + } + + nn := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(n.Type()), nil) + nn.List = list + typed(n.Type(), nn) + // Need to transform the OCOMPLIT. + return transformCompLit(nn) +} + +// transformConv transforms an OCONV node as needed, based on the types involved, +// etc. Corresponds to typecheck.tcConv. +func transformConv(n *ir.ConvExpr) ir.Node { + t := n.X.Type() + op, why := typecheck.Convertop(n.X.Op() == ir.OLITERAL, t, n.Type()) + if op == ir.OXXX { + // types2 currently ignores pragmas, so a 'notinheap' mismatch is the + // one type-related error that it does not catch. This error will be + // caught here by Convertop (see two checks near beginning of + // Convertop) and reported at the end of noding. + base.ErrorfAt(n.Pos(), "cannot convert %L to type %v%s", n.X, n.Type(), why) + return n + } + n.SetOp(op) + switch n.Op() { + case ir.OCONVNOP: + if t.Kind() == n.Type().Kind() { + switch t.Kind() { + case types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128: + // Floating point casts imply rounding and + // so the conversion must be kept. + n.SetOp(ir.OCONV) + } + } + + // Do not convert to []byte literal. See CL 125796. + // Generated code and compiler memory footprint is better without it. + case ir.OSTR2BYTES: + // ok + + case ir.OSTR2RUNES: + if n.X.Op() == ir.OLITERAL { + return stringtoruneslit(n) + } + + case ir.OBYTES2STR: + assert(t.IsSlice()) + assert(t.Elem().Kind() == types.TUINT8) + if t.Elem() != types.ByteType && t.Elem() != types.Types[types.TUINT8] { + // If t is a slice of a user-defined byte type B (not uint8 + // or byte), then add an extra CONVNOP from []B to []byte, so + // that the call to slicebytetostring() added in walk will + // typecheck correctly. + n.X = ir.NewConvExpr(n.X.Pos(), ir.OCONVNOP, types.NewSlice(types.ByteType), n.X) + n.X.SetTypecheck(1) + } + + case ir.ORUNES2STR: + assert(t.IsSlice()) + assert(t.Elem().Kind() == types.TINT32) + if t.Elem() != types.RuneType && t.Elem() != types.Types[types.TINT32] { + // If t is a slice of a user-defined rune type B (not uint32 + // or rune), then add an extra CONVNOP from []B to []rune, so + // that the call to slicerunetostring() added in walk will + // typecheck correctly. + n.X = ir.NewConvExpr(n.X.Pos(), ir.OCONVNOP, types.NewSlice(types.RuneType), n.X) + n.X.SetTypecheck(1) + } + + } + return n +} + +// transformConvCall transforms a conversion call. Corresponds to the OTYPE part of +// typecheck.tcCall. +func transformConvCall(n *ir.CallExpr) ir.Node { + assert(n.Type() != nil && n.Typecheck() == 1) + arg := n.Args[0] + n1 := ir.NewConvExpr(n.Pos(), ir.OCONV, nil, arg) + typed(n.X.Type(), n1) + return transformConv(n1) +} + +// transformCall transforms a normal function/method call. Corresponds to last half +// (non-conversion, non-builtin part) of typecheck.tcCall. This code should work even +// in the case of OCALL/OFUNCINST. +func transformCall(n *ir.CallExpr) { + // Set base.Pos, since transformArgs below may need it, but transformCall + // is called in some passes that don't set base.Pos. + ir.SetPos(n) + // n.Type() can be nil for calls with no return value + assert(n.Typecheck() == 1) + transformArgs(n) + l := n.X + t := l.Type() + + switch l.Op() { + case ir.ODOTINTER: + n.SetOp(ir.OCALLINTER) + + case ir.ODOTMETH: + l := l.(*ir.SelectorExpr) + n.SetOp(ir.OCALLMETH) + + tp := t.Recv().Type + + if l.X == nil || !types.Identical(l.X.Type(), tp) { + base.Fatalf("method receiver") + } + + default: + n.SetOp(ir.OCALLFUNC) + } + + typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args) + if l.Op() == ir.ODOTMETH && len(deref(n.X.Type().Recv().Type).RParams()) == 0 { + typecheck.FixMethodCall(n) + } + if t.NumResults() == 1 { + if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.ONAME { + if sym := n.X.(*ir.Name).Sym(); types.IsRuntimePkg(sym.Pkg) && sym.Name == "getg" { + // Emit code for runtime.getg() directly instead of calling function. + // Most such rewrites (for example the similar one for math.Sqrt) should be done in walk, + // so that the ordering pass can make sure to preserve the semantics of the original code + // (in particular, the exact time of the function call) by introducing temporaries. + // In this case, we know getg() always returns the same result within a given function + // and we want to avoid the temporaries, so we do the rewrite earlier than is typical. + n.SetOp(ir.OGETG) + } + } + return + } +} + +// transformEarlyCall transforms the arguments of a call with an OFUNCINST node. +func transformEarlyCall(n *ir.CallExpr) { + transformArgs(n) + typecheckaste(ir.OCALL, n.X, n.IsDDD, n.X.Type().Params(), n.Args) +} + +// transformCompare transforms a compare operation (currently just equals/not +// equals). Corresponds to the "comparison operators" case in +// typecheck.typecheck1, including tcArith. +func transformCompare(n *ir.BinaryExpr) { + assert(n.Type() != nil && n.Typecheck() == 1) + if (n.Op() == ir.OEQ || n.Op() == ir.ONE) && !types.Identical(n.X.Type(), n.Y.Type()) { + // Comparison is okay as long as one side is assignable to the + // other. The only allowed case where the conversion is not CONVNOP is + // "concrete == interface". In that case, check comparability of + // the concrete type. The conversion allocates, so only do it if + // the concrete type is huge. + l, r := n.X, n.Y + lt, rt := l.Type(), r.Type() + converted := false + if rt.Kind() != types.TBLANK { + aop, _ := typecheck.Assignop(lt, rt) + if aop != ir.OXXX { + types.CalcSize(lt) + if lt.HasShape() || rt.IsInterface() == lt.IsInterface() || lt.Size() >= 1<<16 { + l = ir.NewConvExpr(base.Pos, aop, rt, l) + l.SetTypecheck(1) + } + + converted = true + } + } + + if !converted && lt.Kind() != types.TBLANK { + aop, _ := typecheck.Assignop(rt, lt) + if aop != ir.OXXX { + types.CalcSize(rt) + if rt.HasShape() || rt.IsInterface() == lt.IsInterface() || rt.Size() >= 1<<16 { + r = ir.NewConvExpr(base.Pos, aop, lt, r) + r.SetTypecheck(1) + } + } + } + n.X, n.Y = l, r + } +} + +// Corresponds to typecheck.implicitstar. +func implicitstar(n ir.Node) ir.Node { + // insert implicit * if needed for fixed array + t := n.Type() + if !t.IsPtr() { + return n + } + t = t.Elem() + if !t.IsArray() { + return n + } + star := ir.NewStarExpr(base.Pos, n) + star.SetImplicit(true) + return typed(t, star) +} + +// transformIndex transforms an index operation. Corresponds to typecheck.tcIndex. +func transformIndex(n *ir.IndexExpr) { + assert(n.Type() != nil && n.Typecheck() == 1) + n.X = implicitstar(n.X) + l := n.X + t := l.Type() + if t.Kind() == types.TMAP { + n.Index = assignconvfn(n.Index, t.Key()) + n.SetOp(ir.OINDEXMAP) + // Set type to just the map value, not (value, bool). This is + // different from types2, but fits the later stages of the + // compiler better. + n.SetType(t.Elem()) + n.Assigned = false + } +} + +// transformSlice transforms a slice operation. Corresponds to typecheck.tcSlice. +func transformSlice(n *ir.SliceExpr) { + assert(n.Type() != nil && n.Typecheck() == 1) + l := n.X + if l.Type().IsArray() { + addr := typecheck.NodAddr(n.X) + addr.SetImplicit(true) + typed(types.NewPtr(n.X.Type()), addr) + n.X = addr + l = addr + } + t := l.Type() + if t.IsString() { + n.SetOp(ir.OSLICESTR) + } else if t.IsPtr() && t.Elem().IsArray() { + if n.Op().IsSlice3() { + n.SetOp(ir.OSLICE3ARR) + } else { + n.SetOp(ir.OSLICEARR) + } + } +} + +// Transformation functions for statements + +// Corresponds to typecheck.checkassign. +func transformCheckAssign(stmt ir.Node, n ir.Node) { + if n.Op() == ir.OINDEXMAP { + n := n.(*ir.IndexExpr) + n.Assigned = true + return + } +} + +// Corresponds to typecheck.assign. +func transformAssign(stmt ir.Node, lhs, rhs []ir.Node) { + checkLHS := func(i int, typ *types.Type) { + transformCheckAssign(stmt, lhs[i]) + } + + cr := len(rhs) + if len(rhs) == 1 { + if rtyp := rhs[0].Type(); rtyp != nil && rtyp.IsFuncArgStruct() { + cr = rtyp.NumFields() + } + } + + // x, ok = y +assignOK: + for len(lhs) == 2 && cr == 1 { + stmt := stmt.(*ir.AssignListStmt) + r := rhs[0] + + switch r.Op() { + case ir.OINDEXMAP: + stmt.SetOp(ir.OAS2MAPR) + case ir.ORECV: + stmt.SetOp(ir.OAS2RECV) + case ir.ODOTTYPE: + r := r.(*ir.TypeAssertExpr) + stmt.SetOp(ir.OAS2DOTTYPE) + r.SetOp(ir.ODOTTYPE2) + case ir.ODYNAMICDOTTYPE: + r := r.(*ir.DynamicTypeAssertExpr) + stmt.SetOp(ir.OAS2DOTTYPE) + r.SetOp(ir.ODYNAMICDOTTYPE2) + default: + break assignOK + } + checkLHS(0, r.Type()) + checkLHS(1, types.UntypedBool) + t := lhs[0].Type() + if t != nil && rhs[0].Type().HasShape() && t.IsInterface() && !types.IdenticalStrict(t, rhs[0].Type()) { + // This is a multi-value assignment (map, channel, or dot-type) + // where the main result is converted to an interface during the + // assignment. Normally, the needed CONVIFACE is not created + // until (*orderState).as2ok(), because the AS2* ops and their + // sub-ops are so tightly intertwined. But we need to create the + // CONVIFACE now to enable dictionary lookups. So, assign the + // results first to temps, so that we can manifest the CONVIFACE + // in assigning the first temp to lhs[0]. If we added the + // CONVIFACE into rhs[0] directly, we would break a lot of later + // code that depends on the tight coupling between the AS2* ops + // and their sub-ops. (Issue #50642). + v := typecheck.Temp(rhs[0].Type()) + ok := typecheck.Temp(types.Types[types.TBOOL]) + as := ir.NewAssignListStmt(base.Pos, stmt.Op(), []ir.Node{v, ok}, []ir.Node{r}) + as.Def = true + as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, v)) + as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, ok)) + as.SetTypecheck(1) + // Change stmt to be a normal assignment of the temps to the final + // left-hand-sides. We re-create the original multi-value assignment + // so that it assigns to the temps and add it as an init of stmt. + // + // TODO: fix the order of evaluation, so that the lval of lhs[0] + // is evaluated before rhs[0] (similar to problem in #50672). + stmt.SetOp(ir.OAS2) + stmt.PtrInit().Append(as) + // assignconvfn inserts the CONVIFACE. + stmt.Rhs = []ir.Node{assignconvfn(v, t), ok} + } + return + } + + if len(lhs) != cr { + for i := range lhs { + checkLHS(i, nil) + } + return + } + + // x,y,z = f() + if cr > len(rhs) { + stmt := stmt.(*ir.AssignListStmt) + stmt.SetOp(ir.OAS2FUNC) + r := rhs[0].(*ir.CallExpr) + rtyp := r.Type() + + mismatched := false + failed := false + for i := range lhs { + result := rtyp.Field(i).Type + checkLHS(i, result) + + if lhs[i].Type() == nil || result == nil { + failed = true + } else if lhs[i] != ir.BlankNode && !types.Identical(lhs[i].Type(), result) { + mismatched = true + } + } + if mismatched && !failed { + typecheck.RewriteMultiValueCall(stmt, r) + } + return + } + + for i, r := range rhs { + checkLHS(i, r.Type()) + if lhs[i].Type() != nil { + rhs[i] = assignconvfn(r, lhs[i].Type()) + } + } +} + +// Corresponds to typecheck.typecheckargs. Really just deals with multi-value calls. +func transformArgs(n ir.InitNode) { + var list []ir.Node + switch n := n.(type) { + default: + base.Fatalf("transformArgs %+v", n.Op()) + case *ir.CallExpr: + list = n.Args + if n.IsDDD { + return + } + case *ir.ReturnStmt: + list = n.Results + } + if len(list) != 1 { + return + } + + t := list[0].Type() + if t == nil || !t.IsFuncArgStruct() { + return + } + + // Save n as n.Orig for fmt.go. + if ir.Orig(n) == n { + n.(ir.OrigNode).SetOrig(ir.SepCopy(n)) + } + + // Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...). + typecheck.RewriteMultiValueCall(n, list[0]) +} + +// assignconvfn converts node n for assignment to type t. Corresponds to +// typecheck.assignconvfn. +func assignconvfn(n ir.Node, t *types.Type) ir.Node { + if t.Kind() == types.TBLANK { + return n + } + + if n.Op() == ir.OPAREN { + n = n.(*ir.ParenExpr).X + } + + if types.IdenticalStrict(n.Type(), t) { + return n + } + + op, why := Assignop(n.Type(), t) + if op == ir.OXXX { + base.Fatalf("found illegal assignment %+v -> %+v; %s", n.Type(), t, why) + } + + r := ir.NewConvExpr(base.Pos, op, t, n) + r.SetTypecheck(1) + r.SetImplicit(true) + return r +} + +func Assignop(src, dst *types.Type) (ir.Op, string) { + if src == dst { + return ir.OCONVNOP, "" + } + if src == nil || dst == nil || src.Kind() == types.TFORW || dst.Kind() == types.TFORW || src.Underlying() == nil || dst.Underlying() == nil { + return ir.OXXX, "" + } + + // 1. src type is identical to dst (taking shapes into account) + if types.Identical(src, dst) { + // We already know from assignconvfn above that IdenticalStrict(src, + // dst) is false, so the types are not exactly the same and one of + // src or dst is a shape. If dst is an interface (which means src is + // an interface too), we need a real OCONVIFACE op; otherwise we need a + // OCONVNOP. See issue #48453. + if dst.IsInterface() { + return ir.OCONVIFACE, "" + } else { + return ir.OCONVNOP, "" + } + } + return typecheck.Assignop1(src, dst) +} + +// Corresponds to typecheck.typecheckaste, but we add an extra flag convifaceOnly +// only. If convifaceOnly is true, we only do interface conversion. We use this to do +// early insertion of CONVIFACE nodes during noder2, when the function or args may +// have typeparams. +func typecheckaste(op ir.Op, call ir.Node, isddd bool, tstruct *types.Type, nl ir.Nodes) { + var t *types.Type + var i int + + lno := base.Pos + defer func() { base.Pos = lno }() + + var n ir.Node + if len(nl) == 1 { + n = nl[0] + } + + i = 0 + for _, tl := range tstruct.Fields().Slice() { + t = tl.Type + if tl.IsDDD() { + if isddd { + n = nl[i] + ir.SetPos(n) + if n.Type() != nil { + nl[i] = assignconvfn(n, t) + } + return + } + + // TODO(mdempsky): Make into ... call with implicit slice. + for ; i < len(nl); i++ { + n = nl[i] + ir.SetPos(n) + if n.Type() != nil { + nl[i] = assignconvfn(n, t.Elem()) + } + } + return + } + + n = nl[i] + ir.SetPos(n) + if n.Type() != nil { + nl[i] = assignconvfn(n, t) + } + i++ + } +} + +// transformSend transforms a send statement, converting the value to appropriate +// type for the channel, as needed. Corresponds of typecheck.tcSend. +func transformSend(n *ir.SendStmt) { + n.Value = assignconvfn(n.Value, n.Chan.Type().Elem()) +} + +// transformReturn transforms a return node, by doing the needed assignments and +// any necessary conversions. Corresponds to typecheck.tcReturn() +func transformReturn(rs *ir.ReturnStmt) { + transformArgs(rs) + nl := rs.Results + if ir.HasNamedResults(ir.CurFunc) && len(nl) == 0 { + return + } + + typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), nl) +} + +// transformSelect transforms a select node, creating an assignment list as needed +// for each case. Corresponds to typecheck.tcSelect(). +func transformSelect(sel *ir.SelectStmt) { + for _, ncase := range sel.Cases { + if ncase.Comm != nil { + n := ncase.Comm + oselrecv2 := func(dst, recv ir.Node, def bool) { + selrecv := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv}) + if dst.Op() == ir.ONAME && dst.(*ir.Name).Defn == n { + // Must fix Defn for dst, since we are + // completely changing the node. + dst.(*ir.Name).Defn = selrecv + } + selrecv.Def = def + selrecv.SetTypecheck(1) + selrecv.SetInit(n.Init()) + ncase.Comm = selrecv + } + switch n.Op() { + case ir.OAS: + // convert x = <-c into x, _ = <-c + // remove implicit conversions; the eventual assignment + // will reintroduce them. + n := n.(*ir.AssignStmt) + if r := n.Y; r.Op() == ir.OCONVNOP || r.Op() == ir.OCONVIFACE { + r := r.(*ir.ConvExpr) + if r.Implicit() { + n.Y = r.X + } + } + oselrecv2(n.X, n.Y, n.Def) + + case ir.OAS2RECV: + n := n.(*ir.AssignListStmt) + n.SetOp(ir.OSELRECV2) + + case ir.ORECV: + // convert <-c into _, _ = <-c + n := n.(*ir.UnaryExpr) + oselrecv2(ir.BlankNode, n, false) + + case ir.OSEND: + break + } + } + } +} + +// transformAsOp transforms an AssignOp statement. Corresponds to OASOP case in +// typecheck1. +func transformAsOp(n *ir.AssignOpStmt) { + transformCheckAssign(n, n.X) +} + +// transformDot transforms an OXDOT (or ODOT) or ODOT, ODOTPTR, ODOTMETH, +// ODOTINTER, or OMETHVALUE, as appropriate. It adds in extra nodes as needed to +// access embedded fields. Corresponds to typecheck.tcDot. +func transformDot(n *ir.SelectorExpr, isCall bool) ir.Node { + assert(n.Type() != nil && n.Typecheck() == 1) + if n.Op() == ir.OXDOT { + n = typecheck.AddImplicitDots(n) + n.SetOp(ir.ODOT) + + // Set the Selection field and typecheck flag for any new ODOT nodes + // added by AddImplicitDots(), and also transform to ODOTPTR if + // needed. Equivalent to 'n.X = typecheck(n.X, ctxExpr|ctxType)' in + // tcDot. + for n1 := n; n1.X.Op() == ir.ODOT; { + n1 = n1.X.(*ir.SelectorExpr) + if !n1.Implicit() { + break + } + t1 := n1.X.Type() + if t1.IsPtr() && !t1.Elem().IsInterface() { + t1 = t1.Elem() + n1.SetOp(ir.ODOTPTR) + } + typecheck.Lookdot(n1, t1, 0) + n1.SetTypecheck(1) + } + } + + t := n.X.Type() + + if n.X.Op() == ir.OTYPE { + return transformMethodExpr(n) + } + + if t.IsPtr() && !t.Elem().IsInterface() { + t = t.Elem() + n.SetOp(ir.ODOTPTR) + } + + f := typecheck.Lookdot(n, t, 0) + assert(f != nil) + + if (n.Op() == ir.ODOTINTER || n.Op() == ir.ODOTMETH) && !isCall { + n.SetOp(ir.OMETHVALUE) + // This converts a method type to a function type. See issue 47775. + n.SetType(typecheck.NewMethodType(n.Type(), nil)) + } + return n +} + +// Corresponds to typecheck.typecheckMethodExpr. +func transformMethodExpr(n *ir.SelectorExpr) (res ir.Node) { + t := n.X.Type() + + // Compute the method set for t. + var ms *types.Fields + if t.IsInterface() { + ms = t.AllMethods() + } else { + mt := types.ReceiverBaseType(t) + typecheck.CalcMethods(mt) + ms = mt.AllMethods() + + // The method expression T.m requires a wrapper when T + // is different from m's declared receiver type. We + // normally generate these wrappers while writing out + // runtime type descriptors, which is always done for + // types declared at package scope. However, we need + // to make sure to generate wrappers for anonymous + // receiver types too. + if mt.Sym() == nil { + typecheck.NeedRuntimeType(t) + } + } + + s := n.Sel + m := typecheck.Lookdot1(n, s, t, ms, 0) + if !t.HasShape() { + // It's OK to not find the method if t is instantiated by shape types, + // because we will use the methods on the generic type anyway. + assert(m != nil) + } + + n.SetOp(ir.OMETHEXPR) + n.Selection = m + n.SetType(typecheck.NewMethodType(m.Type, n.X.Type())) + return n +} + +// Corresponds to typecheck.tcAppend. +func transformAppend(n *ir.CallExpr) ir.Node { + transformArgs(n) + args := n.Args + t := args[0].Type() + assert(t.IsSlice()) + + if n.IsDDD { + if t.Elem().IsKind(types.TUINT8) && args[1].Type().IsString() { + return n + } + + args[1] = assignconvfn(args[1], t.Underlying()) + return n + } + + as := args[1:] + for i, n := range as { + assert(n.Type() != nil) + as[i] = assignconvfn(n, t.Elem()) + } + return n +} + +// Corresponds to typecheck.tcComplex. +func transformComplex(n *ir.BinaryExpr) ir.Node { + l := n.X + r := n.Y + + assert(types.Identical(l.Type(), r.Type())) + + var t *types.Type + switch l.Type().Kind() { + case types.TFLOAT32: + t = types.Types[types.TCOMPLEX64] + case types.TFLOAT64: + t = types.Types[types.TCOMPLEX128] + default: + panic(fmt.Sprintf("transformComplex: unexpected type %v", l.Type())) + } + + // Must set the type here for generics, because this can't be determined + // by substitution of the generic types. + typed(t, n) + return n +} + +// Corresponds to typecheck.tcDelete. +func transformDelete(n *ir.CallExpr) ir.Node { + transformArgs(n) + args := n.Args + assert(len(args) == 2) + + l := args[0] + r := args[1] + + args[1] = assignconvfn(r, l.Type().Key()) + return n +} + +// Corresponds to typecheck.tcMake. +func transformMake(n *ir.CallExpr) ir.Node { + args := n.Args + + n.Args = nil + l := args[0] + t := l.Type() + assert(t != nil) + + i := 1 + var nn ir.Node + switch t.Kind() { + case types.TSLICE: + l = args[i] + i++ + var r ir.Node + if i < len(args) { + r = args[i] + i++ + } + nn = ir.NewMakeExpr(n.Pos(), ir.OMAKESLICE, l, r) + + case types.TMAP: + if i < len(args) { + l = args[i] + i++ + } else { + l = ir.NewInt(0) + } + nn = ir.NewMakeExpr(n.Pos(), ir.OMAKEMAP, l, nil) + nn.SetEsc(n.Esc()) + + case types.TCHAN: + l = nil + if i < len(args) { + l = args[i] + i++ + } else { + l = ir.NewInt(0) + } + nn = ir.NewMakeExpr(n.Pos(), ir.OMAKECHAN, l, nil) + default: + panic(fmt.Sprintf("transformMake: unexpected type %v", t)) + } + + assert(i == len(args)) + typed(n.Type(), nn) + return nn +} + +// Corresponds to typecheck.tcPanic. +func transformPanic(n *ir.UnaryExpr) ir.Node { + n.X = assignconvfn(n.X, types.Types[types.TINTER]) + return n +} + +// Corresponds to typecheck.tcPrint. +func transformPrint(n *ir.CallExpr) ir.Node { + transformArgs(n) + return n +} + +// Corresponds to typecheck.tcRealImag. +func transformRealImag(n *ir.UnaryExpr) ir.Node { + l := n.X + var t *types.Type + + // Determine result type. + switch l.Type().Kind() { + case types.TCOMPLEX64: + t = types.Types[types.TFLOAT32] + case types.TCOMPLEX128: + t = types.Types[types.TFLOAT64] + default: + panic(fmt.Sprintf("transformRealImag: unexpected type %v", l.Type())) + } + + // Must set the type here for generics, because this can't be determined + // by substitution of the generic types. + typed(t, n) + return n +} + +// Corresponds to typecheck.tcLenCap. +func transformLenCap(n *ir.UnaryExpr) ir.Node { + n.X = implicitstar(n.X) + return n +} + +// Corresponds to Builtin part of tcCall. +func transformBuiltin(n *ir.CallExpr) ir.Node { + // n.Type() can be nil for builtins with no return value + assert(n.Typecheck() == 1) + fun := n.X.(*ir.Name) + op := fun.BuiltinOp + + switch op { + case ir.OAPPEND, ir.ODELETE, ir.OMAKE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER: + n.SetOp(op) + n.X = nil + switch op { + case ir.OAPPEND: + return transformAppend(n) + case ir.ODELETE: + return transformDelete(n) + case ir.OMAKE: + return transformMake(n) + case ir.OPRINT, ir.OPRINTN: + return transformPrint(n) + case ir.ORECOVER: + // nothing more to do + return n + } + + case ir.OCAP, ir.OCLOSE, ir.OIMAG, ir.OLEN, ir.OPANIC, ir.OREAL: + transformArgs(n) + fallthrough + + case ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: + u := ir.NewUnaryExpr(n.Pos(), op, n.Args[0]) + u1 := typed(n.Type(), ir.InitExpr(n.Init(), u)) // typecheckargs can add to old.Init + switch op { + case ir.OCAP, ir.OLEN: + return transformLenCap(u1.(*ir.UnaryExpr)) + case ir.OREAL, ir.OIMAG: + return transformRealImag(u1.(*ir.UnaryExpr)) + case ir.OPANIC: + return transformPanic(u1.(*ir.UnaryExpr)) + case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: + // This corresponds to the EvalConst() call near end of typecheck(). + return typecheck.EvalConst(u1) + case ir.OCLOSE, ir.ONEW: + // nothing more to do + return u1 + } + + case ir.OCOMPLEX, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE: + transformArgs(n) + b := ir.NewBinaryExpr(n.Pos(), op, n.Args[0], n.Args[1]) + n1 := typed(n.Type(), ir.InitExpr(n.Init(), b)) + if op != ir.OCOMPLEX { + // nothing more to do + return n1 + } + return transformComplex(n1.(*ir.BinaryExpr)) + + default: + panic(fmt.Sprintf("transformBuiltin: unexpected op %v", op)) + } + + return n +} + +func hasKeys(l ir.Nodes) bool { + for _, n := range l { + if n.Op() == ir.OKEY || n.Op() == ir.OSTRUCTKEY { + return true + } + } + return false +} + +// transformArrayLit runs assignconvfn on each array element and returns the +// length of the slice/array that is needed to hold all the array keys/indexes +// (one more than the highest index). Corresponds to typecheck.typecheckarraylit. +func transformArrayLit(elemType *types.Type, bound int64, elts []ir.Node) int64 { + var key, length int64 + for i, elt := range elts { + ir.SetPos(elt) + r := elts[i] + var kv *ir.KeyExpr + if elt.Op() == ir.OKEY { + elt := elt.(*ir.KeyExpr) + key = typecheck.IndexConst(elt.Key) + assert(key >= 0) + kv = elt + r = elt.Value + } + + r = assignconvfn(r, elemType) + if kv != nil { + kv.Value = r + } else { + elts[i] = r + } + + key++ + if key > length { + length = key + } + } + + return length +} + +// transformCompLit transforms n to an OARRAYLIT, OSLICELIT, OMAPLIT, or +// OSTRUCTLIT node, with any needed conversions. Corresponds to +// typecheck.tcCompLit (and includes parts corresponding to tcStructLitKey). +func transformCompLit(n *ir.CompLitExpr) (res ir.Node) { + assert(n.Type() != nil && n.Typecheck() == 1) + lno := base.Pos + defer func() { + base.Pos = lno + }() + + // Save original node (including n.Right) + n.SetOrig(ir.Copy(n)) + + ir.SetPos(n) + + t := n.Type() + + switch t.Kind() { + default: + base.Fatalf("transformCompLit %v", t.Kind()) + + case types.TARRAY: + transformArrayLit(t.Elem(), t.NumElem(), n.List) + n.SetOp(ir.OARRAYLIT) + + case types.TSLICE: + length := transformArrayLit(t.Elem(), -1, n.List) + n.SetOp(ir.OSLICELIT) + n.Len = length + + case types.TMAP: + for _, l := range n.List { + ir.SetPos(l) + assert(l.Op() == ir.OKEY) + l := l.(*ir.KeyExpr) + + r := l.Key + l.Key = assignconvfn(r, t.Key()) + + r = l.Value + l.Value = assignconvfn(r, t.Elem()) + } + + n.SetOp(ir.OMAPLIT) + + case types.TSTRUCT: + // Need valid field offsets for Xoffset below. + types.CalcSize(t) + + if len(n.List) != 0 && !hasKeys(n.List) { + // simple list of values + ls := n.List + for i, n1 := range ls { + ir.SetPos(n1) + + f := t.Field(i) + n1 = assignconvfn(n1, f.Type) + ls[i] = ir.NewStructKeyExpr(base.Pos, f, n1) + } + assert(len(ls) >= t.NumFields()) + } else { + // keyed list + ls := n.List + for i, l := range ls { + ir.SetPos(l) + + kv := l.(*ir.KeyExpr) + key := kv.Key + + // Sym might have resolved to name in other top-level + // package, because of import dot. Redirect to correct sym + // before we do the lookup. + s := key.Sym() + if id, ok := key.(*ir.Ident); ok && typecheck.DotImportRefs[id] != nil { + s = typecheck.Lookup(s.Name) + } + if types.IsExported(s.Name) && s.Pkg != types.LocalPkg { + // Exported field names should always have + // local pkg. We only need to do this + // adjustment for generic functions that are + // being transformed after being imported + // from another package. + s = typecheck.Lookup(s.Name) + } + + // An OXDOT uses the Sym field to hold + // the field to the right of the dot, + // so s will be non-nil, but an OXDOT + // is never a valid struct literal key. + assert(!(s == nil || key.Op() == ir.OXDOT || s.IsBlank())) + + f := typecheck.Lookdot1(nil, s, t, t.Fields(), 0) + l := ir.NewStructKeyExpr(l.Pos(), f, kv.Value) + ls[i] = l + + l.Value = assignconvfn(l.Value, f.Type) + } + } + + n.SetOp(ir.OSTRUCTLIT) + } + + return n +} + +// transformAddr corresponds to typecheck.tcAddr. +func transformAddr(n *ir.AddrExpr) { + switch n.X.Op() { + case ir.OARRAYLIT, ir.OMAPLIT, ir.OSLICELIT, ir.OSTRUCTLIT: + n.SetOp(ir.OPTRLIT) + } +} diff --git a/src/cmd/compile/internal/noder/types.go b/src/cmd/compile/internal/noder/types.go new file mode 100644 index 0000000..ff3a4d9 --- /dev/null +++ b/src/cmd/compile/internal/noder/types.go @@ -0,0 +1,517 @@ +// Copyright 2021 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 noder + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/compile/internal/types2" + "cmd/internal/src" + "strings" +) + +func (g *irgen) pkg(pkg *types2.Package) *types.Pkg { + switch pkg { + case nil: + return types.BuiltinPkg + case g.self: + return types.LocalPkg + case types2.Unsafe: + return types.UnsafePkg + } + return types.NewPkg(pkg.Path(), pkg.Name()) +} + +var universeAny = types2.Universe.Lookup("any").Type() + +// typ converts a types2.Type to a types.Type, including caching of previously +// translated types. +func (g *irgen) typ(typ types2.Type) *types.Type { + // Defer the CheckSize calls until we have fully-defined a + // (possibly-recursive) top-level type. + types.DeferCheckSize() + res := g.typ1(typ) + types.ResumeCheckSize() + + // Finish up any types on typesToFinalize, now that we are at the top of a + // fully-defined (possibly recursive) type. fillinMethods could create more + // types to finalize. + for len(g.typesToFinalize) > 0 { + l := len(g.typesToFinalize) + info := g.typesToFinalize[l-1] + g.typesToFinalize = g.typesToFinalize[:l-1] + types.DeferCheckSize() + g.fillinMethods(info.typ, info.ntyp) + types.ResumeCheckSize() + } + return res +} + +// typ1 is like typ, but doesn't call CheckSize, since it may have only +// constructed part of a recursive type. Should not be called from outside this +// file (g.typ is the "external" entry point). +func (g *irgen) typ1(typ types2.Type) *types.Type { + // See issue 49583: the type checker has trouble keeping track of aliases, + // but for such a common alias as any we can improve things by preserving a + // pointer identity that can be checked when formatting type strings. + if typ == universeAny { + return types.AnyType + } + // Cache type2-to-type mappings. Important so that each defined generic + // type (instantiated or not) has a single types.Type representation. + // Also saves a lot of computation and memory by avoiding re-translating + // types2 types repeatedly. + res, ok := g.typs[typ] + if !ok { + res = g.typ0(typ) + // Calculate the size for all concrete types seen by the frontend. + // This is the replacement for the CheckSize() calls in the types1 + // typechecker. These will be deferred until the top-level g.typ(). + if res != nil && !res.IsUntyped() && !res.IsFuncArgStruct() && !res.HasTParam() { + types.CheckSize(res) + } + g.typs[typ] = res + } + return res +} + +// instTypeName2 creates a name for an instantiated type, base on the type args +// (given as types2 types). +func (g *irgen) instTypeName2(name string, targs *types2.TypeList) string { + rparams := make([]*types.Type, targs.Len()) + for i := range rparams { + rparams[i] = g.typ(targs.At(i)) + } + return typecheck.InstTypeName(name, rparams) +} + +// typ0 converts a types2.Type to a types.Type, but doesn't do the caching check +// at the top level. +func (g *irgen) typ0(typ types2.Type) *types.Type { + switch typ := typ.(type) { + case *types2.Basic: + return g.basic(typ) + case *types2.Named: + // If tparams is set, but targs is not, typ is a base generic + // type. typ is appearing as part of the source type of an alias, + // since that is the only use of a generic type that doesn't + // involve instantiation. We just translate the named type in the + // normal way below using g.obj(). + if typ.TypeParams() != nil && typ.TypeArgs() != nil { + // typ is an instantiation of a defined (named) generic type. + // This instantiation should also be a defined (named) type. + // types2 gives us the substituted type in t.Underlying() + // The substituted type may or may not still have type + // params. We might, for example, be substituting one type + // param for another type param. + // + // When converted to types.Type, typ has a unique name, + // based on the names of the type arguments. + instName := g.instTypeName2(typ.Obj().Name(), typ.TypeArgs()) + s := g.pkg(typ.Obj().Pkg()).Lookup(instName) + + // Make sure the base generic type exists in type1 (it may + // not yet if we are referecing an imported generic type, as + // opposed to a generic type declared in this package). Make + // sure to do this lookup before checking s.Def, in case + // s.Def gets defined while importing base (if an imported + // type). (Issue #50486). + base := g.obj(typ.Origin().Obj()) + + if s.Def != nil { + // We have already encountered this instantiation. + // Use the type we previously created, since there + // must be exactly one instance of a defined type. + return s.Def.Type() + } + + if base.Class == ir.PAUTO { + // If the base type is a local type, we want to pop + // this instantiated type symbol/definition when we + // leave the containing block, so we don't use it + // incorrectly later. + types.Pushdcl(s) + } + + // Create a forwarding type first and put it in the g.typs + // map, in order to deal with recursive generic types + // (including via method signatures). Set up the extra + // ntyp information (Def, RParams, which may set + // HasTParam) before translating the underlying type + // itself, so we handle recursion correctly. + ntyp := typecheck.NewIncompleteNamedType(g.pos(typ.Obj().Pos()), s) + g.typs[typ] = ntyp + + // If ntyp still has type params, then we must be + // referencing something like 'value[T2]', as when + // specifying the generic receiver of a method, where + // value was defined as "type value[T any] ...". Save the + // type args, which will now be the new typeparams of the + // current type. + // + // If ntyp does not have type params, we are saving the + // non-generic types used to instantiate this type. We'll + // use these when instantiating the methods of the + // instantiated type. + targs := typ.TypeArgs() + rparams := make([]*types.Type, targs.Len()) + for i := range rparams { + rparams[i] = g.typ1(targs.At(i)) + } + ntyp.SetRParams(rparams) + //fmt.Printf("Saw new type %v %v\n", instName, ntyp.HasTParam()) + + // Save the symbol for the base generic type. + ntyp.SetOrigType(base.Type()) + ntyp.SetUnderlying(g.typ1(typ.Underlying())) + if typ.NumMethods() != 0 { + // Save a delayed call to g.fillinMethods() (once + // potentially recursive types have been fully + // resolved). + g.typesToFinalize = append(g.typesToFinalize, + &typeDelayInfo{ + typ: typ, + ntyp: ntyp, + }) + } + return ntyp + } + obj := g.obj(typ.Obj()) + if obj.Op() != ir.OTYPE { + base.FatalfAt(obj.Pos(), "expected type: %L", obj) + } + return obj.Type() + + case *types2.Array: + return types.NewArray(g.typ1(typ.Elem()), typ.Len()) + case *types2.Chan: + return types.NewChan(g.typ1(typ.Elem()), dirs[typ.Dir()]) + case *types2.Map: + return types.NewMap(g.typ1(typ.Key()), g.typ1(typ.Elem())) + case *types2.Pointer: + return types.NewPtr(g.typ1(typ.Elem())) + case *types2.Signature: + return g.signature(nil, typ) + case *types2.Slice: + return types.NewSlice(g.typ1(typ.Elem())) + + case *types2.Struct: + fields := make([]*types.Field, typ.NumFields()) + for i := range fields { + v := typ.Field(i) + f := types.NewField(g.pos(v), g.selector(v), g.typ1(v.Type())) + f.Note = typ.Tag(i) + if v.Embedded() { + f.Embedded = 1 + } + fields[i] = f + } + return types.NewStruct(g.tpkg(typ), fields) + + case *types2.Interface: + embeddeds := make([]*types.Field, typ.NumEmbeddeds()) + j := 0 + for i := range embeddeds { + // TODO(mdempsky): Get embedding position. + e := typ.EmbeddedType(i) + + // With Go 1.18, an embedded element can be any type, not + // just an interface. + embeddeds[j] = types.NewField(src.NoXPos, nil, g.typ1(e)) + j++ + } + embeddeds = embeddeds[:j] + + methods := make([]*types.Field, typ.NumExplicitMethods()) + for i := range methods { + m := typ.ExplicitMethod(i) + mtyp := g.signature(types.FakeRecv(), m.Type().(*types2.Signature)) + methods[i] = types.NewField(g.pos(m), g.selector(m), mtyp) + } + + return types.NewInterface(g.tpkg(typ), append(embeddeds, methods...), typ.IsImplicit()) + + case *types2.TypeParam: + // Save the name of the type parameter in the sym of the type. + // Include the types2 subscript in the sym name + pkg := g.tpkg(typ) + // Create the unique types1 name for a type param, using its context + // with a function, type, or method declaration. Also, map blank type + // param names to a unique name based on their type param index. The + // unique blank names will be exported, but will be reverted during + // types2 and gcimporter import. + assert(g.curDecl != "") + nm := typecheck.TparamExportName(g.curDecl, typ.Obj().Name(), typ.Index()) + sym := pkg.Lookup(nm) + if sym.Def != nil { + // Make sure we use the same type param type for the same + // name, whether it is created during types1-import or + // this types2-to-types1 translation. + return sym.Def.Type() + } + tp := types.NewTypeParam(sym, typ.Index()) + nname := ir.NewDeclNameAt(g.pos(typ.Obj().Pos()), ir.OTYPE, sym) + sym.Def = nname + nname.SetType(tp) + tp.SetNod(nname) + // Set g.typs[typ] in case the bound methods reference typ. + g.typs[typ] = tp + + bound := g.typ1(typ.Constraint()) + tp.SetBound(bound) + return tp + + case *types2.Union: + nt := typ.Len() + tlist := make([]*types.Type, nt) + tildes := make([]bool, nt) + for i := range tlist { + t := typ.Term(i) + tlist[i] = g.typ1(t.Type()) + tildes[i] = t.Tilde() + } + return types.NewUnion(tlist, tildes) + + case *types2.Tuple: + // Tuples are used for the type of a function call (i.e. the + // return value of the function). + if typ == nil { + return (*types.Type)(nil) + } + fields := make([]*types.Field, typ.Len()) + for i := range fields { + fields[i] = g.param(typ.At(i)) + } + t := types.NewStruct(types.LocalPkg, fields) + t.StructType().Funarg = types.FunargResults + return t + + default: + base.FatalfAt(src.NoXPos, "unhandled type: %v (%T)", typ, typ) + panic("unreachable") + } +} + +// fillinMethods fills in the method name nodes and types for a defined type with at +// least one method. This is needed for later typechecking when looking up methods of +// instantiated types, and for actually generating the methods for instantiated +// types. +func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) { + targs2 := typ.TypeArgs() + targs := make([]*types.Type, targs2.Len()) + for i := range targs { + targs[i] = g.typ1(targs2.At(i)) + } + + methods := make([]*types.Field, typ.NumMethods()) + for i := range methods { + m := typ.Method(i) + recvType := deref2(types2.AsSignature(m.Type()).Recv().Type()) + var meth *ir.Name + imported := false + if m.Pkg() != g.self { + // Imported methods cannot be loaded by name (what + // g.obj() does) - they must be loaded via their + // type. + meth = g.obj(recvType.(*types2.Named).Obj()).Type().Methods().Index(i).Nname.(*ir.Name) + // XXX Because Obj() returns the object of the base generic + // type, we have to still do the method translation below. + imported = true + } else { + meth = g.obj(m) + } + assert(recvType == types2.Type(typ)) + if imported { + // Unfortunately, meth is the type of the method of the + // generic type, so we have to do a substitution to get + // the name/type of the method of the instantiated type, + // using m.Type().RParams() and typ.TArgs() + inst2 := g.instTypeName2("", typ.TypeArgs()) + name := meth.Sym().Name + i1 := strings.Index(name, "[") + i2 := strings.Index(name[i1:], "]") + assert(i1 >= 0 && i2 >= 0) + // Generate the name of the instantiated method. + name = name[0:i1] + inst2 + name[i1+i2+1:] + newsym := meth.Sym().Pkg.Lookup(name) + var meth2 *ir.Name + if newsym.Def != nil { + meth2 = newsym.Def.(*ir.Name) + } else { + meth2 = ir.NewNameAt(meth.Pos(), newsym) + rparams := types2.AsSignature(m.Type()).RecvTypeParams() + tparams := make([]*types.Type, rparams.Len()) + // Set g.curDecl to be the method context, so type + // params in the receiver of the method that we are + // translating gets the right unique name. We could + // be in a top-level typeDecl, so save and restore + // the current contents of g.curDecl. + savedCurDecl := g.curDecl + g.curDecl = typ.Obj().Name() + "." + m.Name() + for i := range tparams { + tparams[i] = g.typ1(rparams.At(i)) + } + g.curDecl = savedCurDecl + assert(len(tparams) == len(targs)) + ts := typecheck.Tsubster{ + Tparams: tparams, + Targs: targs, + } + // Do the substitution of the type + meth2.SetType(ts.Typ(meth.Type())) + newsym.Def = meth2 + } + meth = meth2 + } + methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type()) + methods[i].Nname = meth + } + ntyp.Methods().Set(methods) + if !ntyp.HasTParam() && !ntyp.HasShape() { + // Generate all the methods for a new fully-instantiated type. + typecheck.NeedInstType(ntyp) + } +} + +func (g *irgen) signature(recv *types.Field, sig *types2.Signature) *types.Type { + tparams2 := sig.TypeParams() + tparams := make([]*types.Field, tparams2.Len()) + for i := range tparams { + tp := tparams2.At(i).Obj() + tparams[i] = types.NewField(g.pos(tp), g.sym(tp), g.typ1(tp.Type())) + } + + do := func(typ *types2.Tuple) []*types.Field { + fields := make([]*types.Field, typ.Len()) + for i := range fields { + fields[i] = g.param(typ.At(i)) + } + return fields + } + params := do(sig.Params()) + results := do(sig.Results()) + if sig.Variadic() { + params[len(params)-1].SetIsDDD(true) + } + + return types.NewSignature(g.tpkg(sig), recv, tparams, params, results) +} + +func (g *irgen) param(v *types2.Var) *types.Field { + return types.NewField(g.pos(v), g.sym(v), g.typ1(v.Type())) +} + +func (g *irgen) sym(obj types2.Object) *types.Sym { + if name := obj.Name(); name != "" { + return g.pkg(obj.Pkg()).Lookup(obj.Name()) + } + return nil +} + +func (g *irgen) selector(obj types2.Object) *types.Sym { + pkg, name := g.pkg(obj.Pkg()), obj.Name() + if types.IsExported(name) { + pkg = types.LocalPkg + } + return pkg.Lookup(name) +} + +// tpkg returns the package that a function, interface, struct, or typeparam type +// expression appeared in. +// +// Caveat: For the degenerate types "func()", "interface{}", and +// "struct{}", tpkg always returns LocalPkg. However, we only need the +// package information so that go/types can report it via its API, and +// the reason we fail to return the original package for these +// particular types is because go/types does *not* report it for +// them. So in practice this limitation is probably moot. +func (g *irgen) tpkg(typ types2.Type) *types.Pkg { + if obj := anyObj(typ); obj != nil { + return g.pkg(obj.Pkg()) + } + return types.LocalPkg +} + +// anyObj returns some object accessible from typ, if any. +func anyObj(typ types2.Type) types2.Object { + switch typ := typ.(type) { + case *types2.Signature: + if recv := typ.Recv(); recv != nil { + return recv + } + if params := typ.Params(); params.Len() > 0 { + return params.At(0) + } + if results := typ.Results(); results.Len() > 0 { + return results.At(0) + } + case *types2.Struct: + if typ.NumFields() > 0 { + return typ.Field(0) + } + case *types2.Interface: + if typ.NumExplicitMethods() > 0 { + return typ.ExplicitMethod(0) + } + case *types2.TypeParam: + return typ.Obj() + } + return nil +} + +func (g *irgen) basic(typ *types2.Basic) *types.Type { + switch typ.Name() { + case "byte": + return types.ByteType + case "rune": + return types.RuneType + } + return *basics[typ.Kind()] +} + +var basics = [...]**types.Type{ + types2.Invalid: new(*types.Type), + types2.Bool: &types.Types[types.TBOOL], + types2.Int: &types.Types[types.TINT], + types2.Int8: &types.Types[types.TINT8], + types2.Int16: &types.Types[types.TINT16], + types2.Int32: &types.Types[types.TINT32], + types2.Int64: &types.Types[types.TINT64], + types2.Uint: &types.Types[types.TUINT], + types2.Uint8: &types.Types[types.TUINT8], + types2.Uint16: &types.Types[types.TUINT16], + types2.Uint32: &types.Types[types.TUINT32], + types2.Uint64: &types.Types[types.TUINT64], + types2.Uintptr: &types.Types[types.TUINTPTR], + types2.Float32: &types.Types[types.TFLOAT32], + types2.Float64: &types.Types[types.TFLOAT64], + types2.Complex64: &types.Types[types.TCOMPLEX64], + types2.Complex128: &types.Types[types.TCOMPLEX128], + types2.String: &types.Types[types.TSTRING], + types2.UnsafePointer: &types.Types[types.TUNSAFEPTR], + types2.UntypedBool: &types.UntypedBool, + types2.UntypedInt: &types.UntypedInt, + types2.UntypedRune: &types.UntypedRune, + types2.UntypedFloat: &types.UntypedFloat, + types2.UntypedComplex: &types.UntypedComplex, + types2.UntypedString: &types.UntypedString, + types2.UntypedNil: &types.Types[types.TNIL], +} + +var dirs = [...]types.ChanDir{ + types2.SendRecv: types.Cboth, + types2.SendOnly: types.Csend, + types2.RecvOnly: types.Crecv, +} + +// deref2 does a single deref of types2 type t, if it is a pointer type. +func deref2(t types2.Type) types2.Type { + if ptr := types2.AsPointer(t); ptr != nil { + t = ptr.Elem() + } + return t +} diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go new file mode 100644 index 0000000..ec0012d --- /dev/null +++ b/src/cmd/compile/internal/noder/unified.go @@ -0,0 +1,334 @@ +// UNREVIEWED + +// Copyright 2021 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 noder + +import ( + "bytes" + "fmt" + "internal/goversion" + "io" + "runtime" + "sort" + + "cmd/compile/internal/base" + "cmd/compile/internal/inline" + "cmd/compile/internal/ir" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/compile/internal/types2" + "cmd/internal/src" +) + +// localPkgReader holds the package reader used for reading the local +// package. It exists so the unified IR linker can refer back to it +// later. +var localPkgReader *pkgReader + +// unified construct the local package's IR from syntax's AST. +// +// The pipeline contains 2 steps: +// +// (1) Generate package export data "stub". +// +// (2) Generate package IR from package export data. +// +// The package data "stub" at step (1) contains everything from the local package, +// but nothing that have been imported. When we're actually writing out export data +// to the output files (see writeNewExport function), we run the "linker", which does +// a few things: +// +// + Updates compiler extensions data (e.g., inlining cost, escape analysis results). +// +// + Handles re-exporting any transitive dependencies. +// +// + Prunes out any unnecessary details (e.g., non-inlineable functions, because any +// downstream importers only care about inlinable functions). +// +// The source files are typechecked twice, once before writing export data +// using types2 checker, once after read export data using gc/typecheck. +// This duplication of work will go away once we always use types2 checker, +// we can remove the gc/typecheck pass. The reason it is still here: +// +// + It reduces engineering costs in maintaining a fork of typecheck +// (e.g., no need to backport fixes like CL 327651). +// +// + It makes it easier to pass toolstash -cmp. +// +// + Historically, we would always re-run the typechecker after import, even though +// we know the imported data is valid. It's not ideal, but also not causing any +// problem either. +// +// + There's still transformation that being done during gc/typecheck, like rewriting +// multi-valued function call, or transform ir.OINDEX -> ir.OINDEXMAP. +// +// Using syntax+types2 tree, which already has a complete representation of generics, +// the unified IR has the full typed AST for doing introspection during step (1). +// In other words, we have all necessary information to build the generic IR form +// (see writer.captureVars for an example). +func unified(noders []*noder) { + inline.NewInline = InlineCall + + if !quirksMode() { + writeNewExportFunc = writeNewExport + } else if base.Flag.G != 0 { + base.Errorf("cannot use -G and -d=quirksmode together") + } + + newReadImportFunc = func(data string, pkg1 *types.Pkg, ctxt *types2.Context, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) { + pr := newPkgDecoder(pkg1.Path, data) + + // Read package descriptors for both types2 and compiler backend. + readPackage(newPkgReader(pr), pkg1) + pkg2 = readPackage2(ctxt, packages, pr) + return + } + + data := writePkgStub(noders) + + // We already passed base.Flag.Lang to types2 to handle validating + // the user's source code. Bump it up now to the current version and + // re-parse, so typecheck doesn't complain if we construct IR that + // utilizes newer Go features. + base.Flag.Lang = fmt.Sprintf("go1.%d", goversion.Version) + types.ParseLangFlag() + + assert(types.LocalPkg.Path == "") + types.LocalPkg.Height = 0 // reset so pkgReader.pkgIdx doesn't complain + target := typecheck.Target + + typecheck.TypecheckAllowed = true + + localPkgReader = newPkgReader(newPkgDecoder(types.LocalPkg.Path, data)) + readPackage(localPkgReader, types.LocalPkg) + + r := localPkgReader.newReader(relocMeta, privateRootIdx, syncPrivate) + r.pkgInit(types.LocalPkg, target) + + // Type-check any top-level assignments. We ignore non-assignments + // here because other declarations are typechecked as they're + // constructed. + for i, ndecls := 0, len(target.Decls); i < ndecls; i++ { + switch n := target.Decls[i]; n.Op() { + case ir.OAS, ir.OAS2: + target.Decls[i] = typecheck.Stmt(n) + } + } + + // Don't use range--bodyIdx can add closures to todoBodies. + for len(todoBodies) > 0 { + // The order we expand bodies doesn't matter, so pop from the end + // to reduce todoBodies reallocations if it grows further. + fn := todoBodies[len(todoBodies)-1] + todoBodies = todoBodies[:len(todoBodies)-1] + + pri, ok := bodyReader[fn] + assert(ok) + pri.funcBody(fn) + + // Instantiated generic function: add to Decls for typechecking + // and compilation. + if fn.OClosure == nil && len(pri.dict.targs) != 0 { + target.Decls = append(target.Decls, fn) + } + } + todoBodies = nil + todoBodiesDone = true + + // Check that nothing snuck past typechecking. + for _, n := range target.Decls { + if n.Typecheck() == 0 { + base.FatalfAt(n.Pos(), "missed typecheck: %v", n) + } + + // For functions, check that at least their first statement (if + // any) was typechecked too. + if fn, ok := n.(*ir.Func); ok && len(fn.Body) != 0 { + if stmt := fn.Body[0]; stmt.Typecheck() == 0 { + base.FatalfAt(stmt.Pos(), "missed typecheck: %v", stmt) + } + } + } + + base.ExitIfErrors() // just in case +} + +// writePkgStub type checks the given parsed source files, +// writes an export data package stub representing them, +// and returns the result. +func writePkgStub(noders []*noder) string { + m, pkg, info := checkFiles(noders) + + pw := newPkgWriter(m, pkg, info) + + pw.collectDecls(noders) + + publicRootWriter := pw.newWriter(relocMeta, syncPublic) + privateRootWriter := pw.newWriter(relocMeta, syncPrivate) + + assert(publicRootWriter.idx == publicRootIdx) + assert(privateRootWriter.idx == privateRootIdx) + + { + w := publicRootWriter + w.pkg(pkg) + w.bool(false) // has init; XXX + + scope := pkg.Scope() + names := scope.Names() + w.len(len(names)) + for _, name := range scope.Names() { + w.obj(scope.Lookup(name), nil) + } + + w.sync(syncEOF) + w.flush() + } + + { + w := privateRootWriter + w.pkgInit(noders) + w.flush() + } + + var sb bytes.Buffer // TODO(mdempsky): strings.Builder after #44505 is resolved + pw.dump(&sb) + + // At this point, we're done with types2. Make sure the package is + // garbage collected. + freePackage(pkg) + + return sb.String() +} + +// freePackage ensures the given package is garbage collected. +func freePackage(pkg *types2.Package) { + // The GC test below relies on a precise GC that runs finalizers as + // soon as objects are unreachable. Our implementation provides + // this, but other/older implementations may not (e.g., Go 1.4 does + // not because of #22350). To avoid imposing unnecessary + // restrictions on the GOROOT_BOOTSTRAP toolchain, we skip the test + // during bootstrapping. + if base.CompilerBootstrap { + return + } + + // Set a finalizer on pkg so we can detect if/when it's collected. + done := make(chan struct{}) + runtime.SetFinalizer(pkg, func(*types2.Package) { close(done) }) + + // Important: objects involved in cycles are not finalized, so zero + // out pkg to break its cycles and allow the finalizer to run. + *pkg = types2.Package{} + + // It typically takes just 1 or 2 cycles to release pkg, but it + // doesn't hurt to try a few more times. + for i := 0; i < 10; i++ { + select { + case <-done: + return + default: + runtime.GC() + } + } + + base.Fatalf("package never finalized") +} + +func readPackage(pr *pkgReader, importpkg *types.Pkg) { + r := pr.newReader(relocMeta, publicRootIdx, syncPublic) + + pkg := r.pkg() + assert(pkg == importpkg) + + if r.bool() { + sym := pkg.Lookup(".inittask") + task := ir.NewNameAt(src.NoXPos, sym) + task.Class = ir.PEXTERN + sym.Def = task + } + + for i, n := 0, r.len(); i < n; i++ { + r.sync(syncObject) + assert(!r.bool()) + idx := r.reloc(relocObj) + assert(r.len() == 0) + + path, name, code := r.p.peekObj(idx) + if code != objStub { + objReader[types.NewPkg(path, "").Lookup(name)] = pkgReaderIndex{pr, idx, nil} + } + } +} + +func writeNewExport(out io.Writer) { + l := linker{ + pw: newPkgEncoder(), + + pkgs: make(map[string]int), + decls: make(map[*types.Sym]int), + } + + publicRootWriter := l.pw.newEncoder(relocMeta, syncPublic) + assert(publicRootWriter.idx == publicRootIdx) + + var selfPkgIdx int + + { + pr := localPkgReader + r := pr.newDecoder(relocMeta, publicRootIdx, syncPublic) + + r.sync(syncPkg) + selfPkgIdx = l.relocIdx(pr, relocPkg, r.reloc(relocPkg)) + + r.bool() // has init + + for i, n := 0, r.len(); i < n; i++ { + r.sync(syncObject) + assert(!r.bool()) + idx := r.reloc(relocObj) + assert(r.len() == 0) + + xpath, xname, xtag := pr.peekObj(idx) + assert(xpath == pr.pkgPath) + assert(xtag != objStub) + + if types.IsExported(xname) { + l.relocIdx(pr, relocObj, idx) + } + } + + r.sync(syncEOF) + } + + { + var idxs []int + for _, idx := range l.decls { + idxs = append(idxs, idx) + } + sort.Ints(idxs) + + w := publicRootWriter + + w.sync(syncPkg) + w.reloc(relocPkg, selfPkgIdx) + + w.bool(typecheck.Lookup(".inittask").Def != nil) + + w.len(len(idxs)) + for _, idx := range idxs { + w.sync(syncObject) + w.bool(false) + w.reloc(relocObj, idx) + w.len(0) + } + + w.sync(syncEOF) + w.flush() + } + + l.pw.dump(out) +} diff --git a/src/cmd/compile/internal/noder/unified_test.go b/src/cmd/compile/internal/noder/unified_test.go new file mode 100644 index 0000000..d7334df --- /dev/null +++ b/src/cmd/compile/internal/noder/unified_test.go @@ -0,0 +1,160 @@ +// Copyright 2021 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 noder_test + +import ( + "encoding/json" + "flag" + exec "internal/execabs" + "os" + "reflect" + "runtime" + "strings" + "testing" +) + +var ( + flagCmp = flag.Bool("cmp", false, "enable TestUnifiedCompare") + flagPkgs = flag.String("pkgs", "std", "list of packages to compare (ignored in -short mode)") + flagAll = flag.Bool("all", false, "enable testing of all GOOS/GOARCH targets") + flagParallel = flag.Bool("parallel", false, "test GOOS/GOARCH targets in parallel") +) + +// TestUnifiedCompare implements a test similar to running: +// +// $ go build -toolexec="toolstash -cmp" std +// +// The -pkgs flag controls the list of packages tested. +// +// By default, only the native GOOS/GOARCH target is enabled. The -all +// flag enables testing of non-native targets. The -parallel flag +// additionally enables testing of targets in parallel. +// +// Caution: Testing all targets is very resource intensive! On an IBM +// P920 (dual Intel Xeon Gold 6154 CPUs; 36 cores, 192GB RAM), testing +// all targets in parallel takes about 5 minutes. Using the 'go test' +// command's -run flag for subtest matching is recommended for less +// powerful machines. +func TestUnifiedCompare(t *testing.T) { + // TODO(mdempsky): Either re-enable or delete. Disabled for now to + // avoid impeding others' forward progress. + if !*flagCmp { + t.Skip("skipping TestUnifiedCompare (use -cmp to enable)") + } + + targets, err := exec.Command("go", "tool", "dist", "list").Output() + if err != nil { + t.Fatal(err) + } + + for _, target := range strings.Fields(string(targets)) { + t.Run(target, func(t *testing.T) { + parts := strings.Split(target, "/") + goos, goarch := parts[0], parts[1] + + if !(*flagAll || goos == runtime.GOOS && goarch == runtime.GOARCH) { + t.Skip("skipping non-native target (use -all to enable)") + } + if *flagParallel { + t.Parallel() + } + + pkgs1 := loadPackages(t, goos, goarch, "-d=unified=0 -d=inlfuncswithclosures=0 -d=unifiedquirks=1 -G=0") + pkgs2 := loadPackages(t, goos, goarch, "-d=unified=1 -d=inlfuncswithclosures=0 -d=unifiedquirks=1 -G=0") + + if len(pkgs1) != len(pkgs2) { + t.Fatalf("length mismatch: %v != %v", len(pkgs1), len(pkgs2)) + } + + for i := range pkgs1 { + pkg1 := pkgs1[i] + pkg2 := pkgs2[i] + + path := pkg1.ImportPath + if path != pkg2.ImportPath { + t.Fatalf("mismatched paths: %q != %q", path, pkg2.ImportPath) + } + + // Packages that don't have any source files (e.g., packages + // unsafe, embed/internal/embedtest, and cmd/internal/moddeps). + if pkg1.Export == "" && pkg2.Export == "" { + continue + } + + if pkg1.BuildID == pkg2.BuildID { + t.Errorf("package %q: build IDs unexpectedly matched", path) + } + + // Unlike toolstash -cmp, we're comparing the same compiler + // binary against itself, just with different flags. So we + // don't need to worry about skipping over mismatched version + // strings, but we do need to account for differing build IDs. + // + // Fortunately, build IDs are cryptographic 256-bit hashes, + // and cmd/go provides us with them up front. So we can just + // use them as delimeters to split the files, and then check + // that the substrings are all equal. + file1 := strings.Split(readFile(t, pkg1.Export), pkg1.BuildID) + file2 := strings.Split(readFile(t, pkg2.Export), pkg2.BuildID) + if !reflect.DeepEqual(file1, file2) { + t.Errorf("package %q: compile output differs", path) + } + } + }) + } +} + +type pkg struct { + ImportPath string + Export string + BuildID string + Incomplete bool +} + +func loadPackages(t *testing.T, goos, goarch, gcflags string) []pkg { + args := []string{"list", "-e", "-export", "-json", "-gcflags=all=" + gcflags, "--"} + if testing.Short() { + t.Log("short testing mode; only testing package runtime") + args = append(args, "runtime") + } else { + args = append(args, strings.Fields(*flagPkgs)...) + } + + cmd := exec.Command("go", args...) + cmd.Env = append(os.Environ(), "GOOS="+goos, "GOARCH="+goarch) + cmd.Stderr = os.Stderr + t.Logf("running %v", cmd) + stdout, err := cmd.StdoutPipe() + if err != nil { + t.Fatal(err) + } + if err := cmd.Start(); err != nil { + t.Fatal(err) + } + + var res []pkg + for dec := json.NewDecoder(stdout); dec.More(); { + var pkg pkg + if err := dec.Decode(&pkg); err != nil { + t.Fatal(err) + } + if pkg.Incomplete { + t.Fatalf("incomplete package: %q", pkg.ImportPath) + } + res = append(res, pkg) + } + if err := cmd.Wait(); err != nil { + t.Fatal(err) + } + return res +} + +func readFile(t *testing.T, name string) string { + buf, err := os.ReadFile(name) + if err != nil { + t.Fatal(err) + } + return string(buf) +} diff --git a/src/cmd/compile/internal/noder/validate.go b/src/cmd/compile/internal/noder/validate.go new file mode 100644 index 0000000..dcacae7 --- /dev/null +++ b/src/cmd/compile/internal/noder/validate.go @@ -0,0 +1,132 @@ +// Copyright 2021 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 noder + +import ( + "go/constant" + + "cmd/compile/internal/base" + "cmd/compile/internal/syntax" + "cmd/compile/internal/types" + "cmd/compile/internal/types2" +) + +// match reports whether types t1 and t2 are consistent +// representations for a given expression's type. +func (g *irgen) match(t1 *types.Type, t2 types2.Type, hasOK bool) bool { + tuple, ok := t2.(*types2.Tuple) + if !ok { + // Not a tuple; can use simple type identity comparison. + return types.Identical(t1, g.typ(t2)) + } + + if hasOK { + // For has-ok values, types2 represents the expression's type as a + // 2-element tuple, whereas ir just uses the first type and infers + // that the second type is boolean. Must match either, since we + // sometimes delay the transformation to the ir form. + if tuple.Len() == 2 && types.Identical(t1, g.typ(tuple.At(0).Type())) { + return true + } + return types.Identical(t1, g.typ(t2)) + } + + if t1 == nil || tuple == nil { + return t1 == nil && tuple == nil + } + if !t1.IsFuncArgStruct() { + return false + } + if t1.NumFields() != tuple.Len() { + return false + } + for i, result := range t1.FieldSlice() { + if !types.Identical(result.Type, g.typ(tuple.At(i).Type())) { + return false + } + } + return true +} + +func (g *irgen) validate(n syntax.Node) { + switch n := n.(type) { + case *syntax.CallExpr: + tv := g.info.Types[n.Fun] + if tv.IsBuiltin() { + fun := n.Fun + for { + builtin, ok := fun.(*syntax.ParenExpr) + if !ok { + break + } + fun = builtin.X + } + switch builtin := fun.(type) { + case *syntax.Name: + g.validateBuiltin(builtin.Value, n) + case *syntax.SelectorExpr: + g.validateBuiltin(builtin.Sel.Value, n) + default: + g.unhandled("builtin", n) + } + } + } +} + +func (g *irgen) validateBuiltin(name string, call *syntax.CallExpr) { + switch name { + case "Alignof", "Offsetof", "Sizeof": + // Check that types2+gcSizes calculates sizes the same + // as cmd/compile does. + + tv := g.info.Types[call] + if !tv.IsValue() { + base.FatalfAt(g.pos(call), "expected a value") + } + + if tv.Value == nil { + break // unsafe op is not a constant, so no further validation + } + + got, ok := constant.Int64Val(tv.Value) + if !ok { + base.FatalfAt(g.pos(call), "expected int64 constant value") + } + + want := g.unsafeExpr(name, call.ArgList[0]) + if got != want { + base.FatalfAt(g.pos(call), "got %v from types2, but want %v", got, want) + } + } +} + +// unsafeExpr evaluates the given unsafe builtin function on arg. +func (g *irgen) unsafeExpr(name string, arg syntax.Expr) int64 { + switch name { + case "Alignof": + return g.typ(g.info.Types[arg].Type).Alignment() + case "Sizeof": + return g.typ(g.info.Types[arg].Type).Size() + } + + // Offsetof + + sel := arg.(*syntax.SelectorExpr) + selection := g.info.Selections[sel] + + typ := g.typ(g.info.Types[sel.X].Type) + typ = deref(typ) + + var offset int64 + for _, i := range selection.Index() { + // Ensure field offsets have been calculated. + types.CalcSize(typ) + + f := typ.Field(i) + offset += f.Offset + typ = f.Type + } + return offset +} diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go new file mode 100644 index 0000000..46e8339 --- /dev/null +++ b/src/cmd/compile/internal/noder/writer.go @@ -0,0 +1,1862 @@ +// UNREVIEWED + +// Copyright 2021 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 noder + +import ( + "fmt" + "go/constant" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/syntax" + "cmd/compile/internal/types2" +) + +type pkgWriter struct { + pkgEncoder + + m posMap + curpkg *types2.Package + info *types2.Info + + posBasesIdx map[*syntax.PosBase]int + pkgsIdx map[*types2.Package]int + typsIdx map[types2.Type]int + globalsIdx map[types2.Object]int + + funDecls map[*types2.Func]*syntax.FuncDecl + typDecls map[*types2.TypeName]typeDeclGen + + linknames map[types2.Object]string + cgoPragmas [][]string + + dups dupTypes +} + +func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter { + return &pkgWriter{ + pkgEncoder: newPkgEncoder(), + + m: m, + curpkg: pkg, + info: info, + + pkgsIdx: make(map[*types2.Package]int), + globalsIdx: make(map[types2.Object]int), + typsIdx: make(map[types2.Type]int), + + posBasesIdx: make(map[*syntax.PosBase]int), + + funDecls: make(map[*types2.Func]*syntax.FuncDecl), + typDecls: make(map[*types2.TypeName]typeDeclGen), + + linknames: make(map[types2.Object]string), + } +} + +func (pw *pkgWriter) errorf(p poser, msg string, args ...interface{}) { + base.ErrorfAt(pw.m.pos(p), msg, args...) +} + +func (pw *pkgWriter) fatalf(p poser, msg string, args ...interface{}) { + base.FatalfAt(pw.m.pos(p), msg, args...) +} + +func (pw *pkgWriter) unexpected(what string, p poser) { + pw.fatalf(p, "unexpected %s: %v (%T)", what, p, p) +} + +type writer struct { + p *pkgWriter + + encoder + + // TODO(mdempsky): We should be able to prune localsIdx whenever a + // scope closes, and then maybe we can just use the same map for + // storing the TypeParams too (as their TypeName instead). + + // variables declared within this function + localsIdx map[*types2.Var]int + + closureVars []posObj + closureVarsIdx map[*types2.Var]int + + dict *writerDict + derived bool +} + +// A writerDict tracks types and objects that are used by a declaration. +type writerDict struct { + implicits []*types2.TypeName + + // derived is a slice of type indices for computing derived types + // (i.e., types that depend on the declaration's type parameters). + derived []derivedInfo + + // derivedIdx maps a Type to its corresponding index within the + // derived slice, if present. + derivedIdx map[types2.Type]int + + // funcs lists references to generic functions that were + // instantiated with derived types (i.e., that require + // sub-dictionaries when called at run time). + funcs []objInfo +} + +type derivedInfo struct { + idx int + needed bool +} + +type typeInfo struct { + idx int + derived bool +} + +type objInfo struct { + idx int // index for the generic function declaration + explicits []typeInfo // info for the type arguments +} + +func (info objInfo) anyDerived() bool { + for _, explicit := range info.explicits { + if explicit.derived { + return true + } + } + return false +} + +func (info objInfo) equals(other objInfo) bool { + if info.idx != other.idx { + return false + } + assert(len(info.explicits) == len(other.explicits)) + for i, targ := range info.explicits { + if targ != other.explicits[i] { + return false + } + } + return true +} + +func (pw *pkgWriter) newWriter(k reloc, marker syncMarker) *writer { + return &writer{ + encoder: pw.newEncoder(k, marker), + p: pw, + } +} + +// @@@ Positions + +func (w *writer) pos(p poser) { + w.sync(syncPos) + pos := p.Pos() + + // TODO(mdempsky): Track down the remaining cases here and fix them. + if !w.bool(pos.IsKnown()) { + return + } + + // TODO(mdempsky): Delta encoding. Also, if there's a b-side, update + // its position base too (but not vice versa!). + w.posBase(pos.Base()) + w.uint(pos.Line()) + w.uint(pos.Col()) +} + +func (w *writer) posBase(b *syntax.PosBase) { + w.reloc(relocPosBase, w.p.posBaseIdx(b)) +} + +func (pw *pkgWriter) posBaseIdx(b *syntax.PosBase) int { + if idx, ok := pw.posBasesIdx[b]; ok { + return idx + } + + w := pw.newWriter(relocPosBase, syncPosBase) + w.p.posBasesIdx[b] = w.idx + + w.string(trimFilename(b)) + + if !w.bool(b.IsFileBase()) { + w.pos(b) + w.uint(b.Line()) + w.uint(b.Col()) + } + + return w.flush() +} + +// @@@ Packages + +func (w *writer) pkg(pkg *types2.Package) { + w.sync(syncPkg) + w.reloc(relocPkg, w.p.pkgIdx(pkg)) +} + +func (pw *pkgWriter) pkgIdx(pkg *types2.Package) int { + if idx, ok := pw.pkgsIdx[pkg]; ok { + return idx + } + + w := pw.newWriter(relocPkg, syncPkgDef) + pw.pkgsIdx[pkg] = w.idx + + if pkg == nil { + w.string("builtin") + } else { + var path string + if pkg != w.p.curpkg { + path = pkg.Path() + } + w.string(path) + w.string(pkg.Name()) + w.len(pkg.Height()) + + w.len(len(pkg.Imports())) + for _, imp := range pkg.Imports() { + w.pkg(imp) + } + } + + return w.flush() +} + +// @@@ Types + +var anyTypeName = types2.Universe.Lookup("any").(*types2.TypeName) + +func (w *writer) typ(typ types2.Type) { + w.typInfo(w.p.typIdx(typ, w.dict)) +} + +func (w *writer) typInfo(info typeInfo) { + w.sync(syncType) + if w.bool(info.derived) { + w.len(info.idx) + w.derived = true + } else { + w.reloc(relocType, info.idx) + } +} + +// typIdx returns the index where the export data description of type +// can be read back in. If no such index exists yet, it's created. +// +// typIdx also reports whether typ is a derived type; that is, whether +// its identity depends on type parameters. +func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo { + if quirksMode() { + typ = pw.dups.orig(typ) + } + + if idx, ok := pw.typsIdx[typ]; ok { + return typeInfo{idx: idx, derived: false} + } + if dict != nil { + if idx, ok := dict.derivedIdx[typ]; ok { + return typeInfo{idx: idx, derived: true} + } + } + + w := pw.newWriter(relocType, syncTypeIdx) + w.dict = dict + + switch typ := typ.(type) { + default: + base.Fatalf("unexpected type: %v (%T)", typ, typ) + + case *types2.Basic: + switch kind := typ.Kind(); { + case kind == types2.Invalid: + base.Fatalf("unexpected types2.Invalid") + + case types2.Typ[kind] == typ: + w.code(typeBasic) + w.len(int(kind)) + + default: + // Handle "byte" and "rune" as references to their TypeName. + obj := types2.Universe.Lookup(typ.Name()) + assert(obj.Type() == typ) + + w.code(typeNamed) + w.obj(obj, nil) + } + + case *types2.Named: + // Type aliases can refer to uninstantiated generic types, so we + // might see len(TParams) != 0 && len(TArgs) == 0 here. + // TODO(mdempsky): Revisit after #46477 is resolved. + assert(typ.TypeParams().Len() == typ.TypeArgs().Len() || typ.TypeArgs().Len() == 0) + + // TODO(mdempsky): Why do we need to loop here? + orig := typ + for orig.TypeArgs() != nil { + orig = orig.Origin() + } + + w.code(typeNamed) + w.obj(orig.Obj(), typ.TypeArgs()) + + case *types2.TypeParam: + index := func() int { + for idx, name := range w.dict.implicits { + if name.Type().(*types2.TypeParam) == typ { + return idx + } + } + + return len(w.dict.implicits) + typ.Index() + }() + + w.derived = true + w.code(typeTypeParam) + w.len(index) + + case *types2.Array: + w.code(typeArray) + w.uint64(uint64(typ.Len())) + w.typ(typ.Elem()) + + case *types2.Chan: + w.code(typeChan) + w.len(int(typ.Dir())) + w.typ(typ.Elem()) + + case *types2.Map: + w.code(typeMap) + w.typ(typ.Key()) + w.typ(typ.Elem()) + + case *types2.Pointer: + w.code(typePointer) + w.typ(typ.Elem()) + + case *types2.Signature: + base.Assertf(typ.TypeParams() == nil, "unexpected type params: %v", typ) + w.code(typeSignature) + w.signature(typ) + + case *types2.Slice: + w.code(typeSlice) + w.typ(typ.Elem()) + + case *types2.Struct: + w.code(typeStruct) + w.structType(typ) + + case *types2.Interface: + if typ == anyTypeName.Type() { + w.code(typeNamed) + w.obj(anyTypeName, nil) + break + } + + w.code(typeInterface) + w.interfaceType(typ) + + case *types2.Union: + w.code(typeUnion) + w.unionType(typ) + } + + if w.derived { + idx := len(dict.derived) + dict.derived = append(dict.derived, derivedInfo{idx: w.flush()}) + dict.derivedIdx[typ] = idx + return typeInfo{idx: idx, derived: true} + } + + pw.typsIdx[typ] = w.idx + return typeInfo{idx: w.flush(), derived: false} +} + +func (w *writer) structType(typ *types2.Struct) { + w.len(typ.NumFields()) + for i := 0; i < typ.NumFields(); i++ { + f := typ.Field(i) + w.pos(f) + w.selector(f) + w.typ(f.Type()) + w.string(typ.Tag(i)) + w.bool(f.Embedded()) + } +} + +func (w *writer) unionType(typ *types2.Union) { + w.len(typ.Len()) + for i := 0; i < typ.Len(); i++ { + t := typ.Term(i) + w.bool(t.Tilde()) + w.typ(t.Type()) + } +} + +func (w *writer) interfaceType(typ *types2.Interface) { + w.len(typ.NumExplicitMethods()) + w.len(typ.NumEmbeddeds()) + + for i := 0; i < typ.NumExplicitMethods(); i++ { + m := typ.ExplicitMethod(i) + sig := m.Type().(*types2.Signature) + assert(sig.TypeParams() == nil) + + w.pos(m) + w.selector(m) + w.signature(sig) + } + + for i := 0; i < typ.NumEmbeddeds(); i++ { + w.typ(typ.EmbeddedType(i)) + } +} + +func (w *writer) signature(sig *types2.Signature) { + w.sync(syncSignature) + w.params(sig.Params()) + w.params(sig.Results()) + w.bool(sig.Variadic()) +} + +func (w *writer) params(typ *types2.Tuple) { + w.sync(syncParams) + w.len(typ.Len()) + for i := 0; i < typ.Len(); i++ { + w.param(typ.At(i)) + } +} + +func (w *writer) param(param *types2.Var) { + w.sync(syncParam) + w.pos(param) + w.localIdent(param) + w.typ(param.Type()) +} + +// @@@ Objects + +func (w *writer) obj(obj types2.Object, explicits *types2.TypeList) { + explicitInfos := make([]typeInfo, explicits.Len()) + for i := range explicitInfos { + explicitInfos[i] = w.p.typIdx(explicits.At(i), w.dict) + } + info := objInfo{idx: w.p.objIdx(obj), explicits: explicitInfos} + + if _, ok := obj.(*types2.Func); ok && info.anyDerived() { + idx := -1 + for i, prev := range w.dict.funcs { + if prev.equals(info) { + idx = i + } + } + if idx < 0 { + idx = len(w.dict.funcs) + w.dict.funcs = append(w.dict.funcs, info) + } + + // TODO(mdempsky): Push up into expr; this shouldn't appear + // outside of expression context. + w.sync(syncObject) + w.bool(true) + w.len(idx) + return + } + + // TODO(mdempsky): Push up into typIdx; this shouldn't be needed + // except while writing out types. + if isDefinedType(obj) && obj.Pkg() == w.p.curpkg { + decl, ok := w.p.typDecls[obj.(*types2.TypeName)] + assert(ok) + if len(decl.implicits) != 0 { + w.derived = true + } + } + + w.sync(syncObject) + w.bool(false) + w.reloc(relocObj, info.idx) + + w.len(len(info.explicits)) + for _, info := range info.explicits { + w.typInfo(info) + } +} + +func (pw *pkgWriter) objIdx(obj types2.Object) int { + if idx, ok := pw.globalsIdx[obj]; ok { + return idx + } + + dict := &writerDict{ + derivedIdx: make(map[types2.Type]int), + } + + if isDefinedType(obj) && obj.Pkg() == pw.curpkg { + decl, ok := pw.typDecls[obj.(*types2.TypeName)] + assert(ok) + dict.implicits = decl.implicits + } + + w := pw.newWriter(relocObj, syncObject1) + wext := pw.newWriter(relocObjExt, syncObject1) + wname := pw.newWriter(relocName, syncObject1) + wdict := pw.newWriter(relocObjDict, syncObject1) + + pw.globalsIdx[obj] = w.idx // break cycles + assert(wext.idx == w.idx) + assert(wname.idx == w.idx) + assert(wdict.idx == w.idx) + + w.dict = dict + wext.dict = dict + + code := w.doObj(wext, obj) + w.flush() + wext.flush() + + wname.qualifiedIdent(obj) + wname.code(code) + wname.flush() + + wdict.objDict(obj, w.dict) + wdict.flush() + + return w.idx +} + +func (w *writer) doObj(wext *writer, obj types2.Object) codeObj { + if obj.Pkg() != w.p.curpkg { + return objStub + } + + switch obj := obj.(type) { + default: + w.p.unexpected("object", obj) + panic("unreachable") + + case *types2.Const: + w.pos(obj) + w.typ(obj.Type()) + w.value(obj.Val()) + return objConst + + case *types2.Func: + decl, ok := w.p.funDecls[obj] + assert(ok) + sig := obj.Type().(*types2.Signature) + + w.pos(obj) + w.typeParamNames(sig.TypeParams()) + w.signature(sig) + w.pos(decl) + wext.funcExt(obj) + return objFunc + + case *types2.TypeName: + decl, ok := w.p.typDecls[obj] + assert(ok) + + if obj.IsAlias() { + w.pos(obj) + w.typ(obj.Type()) + return objAlias + } + + named := obj.Type().(*types2.Named) + assert(named.TypeArgs() == nil) + + w.pos(obj) + w.typeParamNames(named.TypeParams()) + wext.typeExt(obj) + w.typExpr(decl.Type) + + w.len(named.NumMethods()) + for i := 0; i < named.NumMethods(); i++ { + w.method(wext, named.Method(i)) + } + + return objType + + case *types2.Var: + w.pos(obj) + w.typ(obj.Type()) + wext.varExt(obj) + return objVar + } +} + +// typExpr writes the type represented by the given expression. +func (w *writer) typExpr(expr syntax.Expr) { + tv, ok := w.p.info.Types[expr] + assert(ok) + assert(tv.IsType()) + w.typ(tv.Type) +} + +// objDict writes the dictionary needed for reading the given object. +func (w *writer) objDict(obj types2.Object, dict *writerDict) { + // TODO(mdempsky): Split objDict into multiple entries? reader.go + // doesn't care about the type parameter bounds, and reader2.go + // doesn't care about referenced functions. + + w.dict = dict // TODO(mdempsky): This is a bit sketchy. + + w.len(len(dict.implicits)) + + tparams := objTypeParams(obj) + ntparams := tparams.Len() + w.len(ntparams) + for i := 0; i < ntparams; i++ { + w.typ(tparams.At(i).Constraint()) + } + + nderived := len(dict.derived) + w.len(nderived) + for _, typ := range dict.derived { + w.reloc(relocType, typ.idx) + w.bool(typ.needed) + } + + nfuncs := len(dict.funcs) + w.len(nfuncs) + for _, fn := range dict.funcs { + w.reloc(relocObj, fn.idx) + w.len(len(fn.explicits)) + for _, targ := range fn.explicits { + w.typInfo(targ) + } + } + + assert(len(dict.derived) == nderived) + assert(len(dict.funcs) == nfuncs) +} + +func (w *writer) typeParamNames(tparams *types2.TypeParamList) { + w.sync(syncTypeParamNames) + + ntparams := tparams.Len() + for i := 0; i < ntparams; i++ { + tparam := tparams.At(i).Obj() + w.pos(tparam) + w.localIdent(tparam) + } +} + +func (w *writer) method(wext *writer, meth *types2.Func) { + decl, ok := w.p.funDecls[meth] + assert(ok) + sig := meth.Type().(*types2.Signature) + + w.sync(syncMethod) + w.pos(meth) + w.selector(meth) + w.typeParamNames(sig.RecvTypeParams()) + w.param(sig.Recv()) + w.signature(sig) + + w.pos(decl) // XXX: Hack to workaround linker limitations. + wext.funcExt(meth) +} + +// qualifiedIdent writes out the name of an object declared at package +// scope. (For now, it's also used to refer to local defined types.) +func (w *writer) qualifiedIdent(obj types2.Object) { + w.sync(syncSym) + + name := obj.Name() + if isDefinedType(obj) && obj.Pkg() == w.p.curpkg { + decl, ok := w.p.typDecls[obj.(*types2.TypeName)] + assert(ok) + if decl.gen != 0 { + // TODO(mdempsky): Find a better solution than embedding middle + // dot in the symbol name; this is terrible. + name = fmt.Sprintf("%s·%v", name, decl.gen) + } + } + + w.pkg(obj.Pkg()) + w.string(name) +} + +// TODO(mdempsky): We should be able to omit pkg from both localIdent +// and selector, because they should always be known from context. +// However, past frustrations with this optimization in iexport make +// me a little nervous to try it again. + +// localIdent writes the name of a locally declared object (i.e., +// objects that can only be accessed by name, within the context of a +// particular function). +func (w *writer) localIdent(obj types2.Object) { + assert(!isGlobal(obj)) + w.sync(syncLocalIdent) + w.pkg(obj.Pkg()) + w.string(obj.Name()) +} + +// selector writes the name of a field or method (i.e., objects that +// can only be accessed using selector expressions). +func (w *writer) selector(obj types2.Object) { + w.sync(syncSelector) + w.pkg(obj.Pkg()) + w.string(obj.Name()) +} + +// @@@ Compiler extensions + +func (w *writer) funcExt(obj *types2.Func) { + decl, ok := w.p.funDecls[obj] + assert(ok) + + // TODO(mdempsky): Extend these pragma validation flags to account + // for generics. E.g., linkname probably doesn't make sense at + // least. + + pragma := asPragmaFlag(decl.Pragma) + if pragma&ir.Systemstack != 0 && pragma&ir.Nosplit != 0 { + w.p.errorf(decl, "go:nosplit and go:systemstack cannot be combined") + } + + if decl.Body != nil { + if pragma&ir.Noescape != 0 { + w.p.errorf(decl, "can only use //go:noescape with external func implementations") + } + } else { + if base.Flag.Complete || decl.Name.Value == "init" { + // Linknamed functions are allowed to have no body. Hopefully + // the linkname target has a body. See issue 23311. + if _, ok := w.p.linknames[obj]; !ok { + w.p.errorf(decl, "missing function body") + } + } + } + + sig, block := obj.Type().(*types2.Signature), decl.Body + body, closureVars := w.p.bodyIdx(w.p.curpkg, sig, block, w.dict) + assert(len(closureVars) == 0) + + w.sync(syncFuncExt) + w.pragmaFlag(pragma) + w.linkname(obj) + w.bool(false) // stub extension + w.reloc(relocBody, body) + w.sync(syncEOF) +} + +func (w *writer) typeExt(obj *types2.TypeName) { + decl, ok := w.p.typDecls[obj] + assert(ok) + + w.sync(syncTypeExt) + + w.pragmaFlag(asPragmaFlag(decl.Pragma)) + + // No LSym.SymIdx info yet. + w.int64(-1) + w.int64(-1) +} + +func (w *writer) varExt(obj *types2.Var) { + w.sync(syncVarExt) + w.linkname(obj) +} + +func (w *writer) linkname(obj types2.Object) { + w.sync(syncLinkname) + w.int64(-1) + w.string(w.p.linknames[obj]) +} + +func (w *writer) pragmaFlag(p ir.PragmaFlag) { + w.sync(syncPragma) + w.int(int(p)) +} + +// @@@ Function bodies + +func (pw *pkgWriter) bodyIdx(pkg *types2.Package, sig *types2.Signature, block *syntax.BlockStmt, dict *writerDict) (idx int, closureVars []posObj) { + w := pw.newWriter(relocBody, syncFuncBody) + w.dict = dict + + w.funcargs(sig) + if w.bool(block != nil) { + w.stmts(block.List) + w.pos(block.Rbrace) + } + + return w.flush(), w.closureVars +} + +func (w *writer) funcargs(sig *types2.Signature) { + do := func(params *types2.Tuple, result bool) { + for i := 0; i < params.Len(); i++ { + w.funcarg(params.At(i), result) + } + } + + if recv := sig.Recv(); recv != nil { + w.funcarg(recv, false) + } + do(sig.Params(), false) + do(sig.Results(), true) +} + +func (w *writer) funcarg(param *types2.Var, result bool) { + if param.Name() != "" || result { + w.addLocal(param) + } +} + +func (w *writer) addLocal(obj *types2.Var) { + w.sync(syncAddLocal) + idx := len(w.localsIdx) + if enableSync { + w.int(idx) + } + if w.localsIdx == nil { + w.localsIdx = make(map[*types2.Var]int) + } + w.localsIdx[obj] = idx +} + +func (w *writer) useLocal(pos syntax.Pos, obj *types2.Var) { + w.sync(syncUseObjLocal) + + if idx, ok := w.localsIdx[obj]; w.bool(ok) { + w.len(idx) + return + } + + idx, ok := w.closureVarsIdx[obj] + if !ok { + if w.closureVarsIdx == nil { + w.closureVarsIdx = make(map[*types2.Var]int) + } + idx = len(w.closureVars) + w.closureVars = append(w.closureVars, posObj{pos, obj}) + w.closureVarsIdx[obj] = idx + } + w.len(idx) +} + +func (w *writer) openScope(pos syntax.Pos) { + w.sync(syncOpenScope) + w.pos(pos) +} + +func (w *writer) closeScope(pos syntax.Pos) { + w.sync(syncCloseScope) + w.pos(pos) + w.closeAnotherScope() +} + +func (w *writer) closeAnotherScope() { + w.sync(syncCloseAnotherScope) +} + +// @@@ Statements + +func (w *writer) stmt(stmt syntax.Stmt) { + var stmts []syntax.Stmt + if stmt != nil { + stmts = []syntax.Stmt{stmt} + } + w.stmts(stmts) +} + +func (w *writer) stmts(stmts []syntax.Stmt) { + w.sync(syncStmts) + for _, stmt := range stmts { + w.stmt1(stmt) + } + w.code(stmtEnd) + w.sync(syncStmtsEnd) +} + +func (w *writer) stmt1(stmt syntax.Stmt) { + switch stmt := stmt.(type) { + default: + w.p.unexpected("statement", stmt) + + case nil, *syntax.EmptyStmt: + return + + case *syntax.AssignStmt: + switch { + case stmt.Rhs == nil: + w.code(stmtIncDec) + w.op(binOps[stmt.Op]) + w.expr(stmt.Lhs) + w.pos(stmt) + + case stmt.Op != 0 && stmt.Op != syntax.Def: + w.code(stmtAssignOp) + w.op(binOps[stmt.Op]) + w.expr(stmt.Lhs) + w.pos(stmt) + w.expr(stmt.Rhs) + + default: + w.code(stmtAssign) + w.pos(stmt) + w.exprList(stmt.Rhs) + w.assignList(stmt.Lhs) + } + + case *syntax.BlockStmt: + w.code(stmtBlock) + w.blockStmt(stmt) + + case *syntax.BranchStmt: + w.code(stmtBranch) + w.pos(stmt) + w.op(branchOps[stmt.Tok]) + w.optLabel(stmt.Label) + + case *syntax.CallStmt: + w.code(stmtCall) + w.pos(stmt) + w.op(callOps[stmt.Tok]) + w.expr(stmt.Call) + + case *syntax.DeclStmt: + for _, decl := range stmt.DeclList { + w.declStmt(decl) + } + + case *syntax.ExprStmt: + w.code(stmtExpr) + w.expr(stmt.X) + + case *syntax.ForStmt: + w.code(stmtFor) + w.forStmt(stmt) + + case *syntax.IfStmt: + w.code(stmtIf) + w.ifStmt(stmt) + + case *syntax.LabeledStmt: + w.code(stmtLabel) + w.pos(stmt) + w.label(stmt.Label) + w.stmt1(stmt.Stmt) + + case *syntax.ReturnStmt: + w.code(stmtReturn) + w.pos(stmt) + w.exprList(stmt.Results) + + case *syntax.SelectStmt: + w.code(stmtSelect) + w.selectStmt(stmt) + + case *syntax.SendStmt: + w.code(stmtSend) + w.pos(stmt) + w.expr(stmt.Chan) + w.expr(stmt.Value) + + case *syntax.SwitchStmt: + w.code(stmtSwitch) + w.switchStmt(stmt) + } +} + +func (w *writer) assignList(expr syntax.Expr) { + exprs := unpackListExpr(expr) + w.len(len(exprs)) + + for _, expr := range exprs { + if name, ok := expr.(*syntax.Name); ok && name.Value != "_" { + if obj, ok := w.p.info.Defs[name]; ok { + obj := obj.(*types2.Var) + + w.bool(true) + w.pos(obj) + w.localIdent(obj) + w.typ(obj.Type()) + + // TODO(mdempsky): Minimize locals index size by deferring + // this until the variables actually come into scope. + w.addLocal(obj) + continue + } + } + + w.bool(false) + w.expr(expr) + } +} + +func (w *writer) declStmt(decl syntax.Decl) { + switch decl := decl.(type) { + default: + w.p.unexpected("declaration", decl) + + case *syntax.ConstDecl: + + case *syntax.TypeDecl: + // Quirk: The legacy inliner doesn't support inlining functions + // with type declarations. Unified IR doesn't have any need to + // write out type declarations explicitly (they're always looked + // up via global index tables instead), so we just write out a + // marker so the reader knows to synthesize a fake declaration to + // prevent inlining. + if quirksMode() { + w.code(stmtTypeDeclHack) + } + + case *syntax.VarDecl: + values := unpackListExpr(decl.Values) + + // Quirk: When N variables are declared with N initialization + // values, we need to decompose that into N interleaved + // declarations+initializations, because it leads to different + // (albeit semantically equivalent) code generation. + if quirksMode() && len(decl.NameList) == len(values) { + for i, name := range decl.NameList { + w.code(stmtAssign) + w.pos(decl) + w.exprList(values[i]) + w.assignList(name) + } + break + } + + w.code(stmtAssign) + w.pos(decl) + w.exprList(decl.Values) + w.assignList(namesAsExpr(decl.NameList)) + } +} + +func (w *writer) blockStmt(stmt *syntax.BlockStmt) { + w.sync(syncBlockStmt) + w.openScope(stmt.Pos()) + w.stmts(stmt.List) + w.closeScope(stmt.Rbrace) +} + +func (w *writer) forStmt(stmt *syntax.ForStmt) { + w.sync(syncForStmt) + w.openScope(stmt.Pos()) + + if rang, ok := stmt.Init.(*syntax.RangeClause); w.bool(ok) { + w.pos(rang) + w.expr(rang.X) + w.assignList(rang.Lhs) + } else { + w.pos(stmt) + w.stmt(stmt.Init) + w.expr(stmt.Cond) + w.stmt(stmt.Post) + } + + w.blockStmt(stmt.Body) + w.closeAnotherScope() +} + +func (w *writer) ifStmt(stmt *syntax.IfStmt) { + w.sync(syncIfStmt) + w.openScope(stmt.Pos()) + w.pos(stmt) + w.stmt(stmt.Init) + w.expr(stmt.Cond) + w.blockStmt(stmt.Then) + w.stmt(stmt.Else) + w.closeAnotherScope() +} + +func (w *writer) selectStmt(stmt *syntax.SelectStmt) { + w.sync(syncSelectStmt) + + w.pos(stmt) + w.len(len(stmt.Body)) + for i, clause := range stmt.Body { + if i > 0 { + w.closeScope(clause.Pos()) + } + w.openScope(clause.Pos()) + + w.pos(clause) + w.stmt(clause.Comm) + w.stmts(clause.Body) + } + if len(stmt.Body) > 0 { + w.closeScope(stmt.Rbrace) + } +} + +func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { + w.sync(syncSwitchStmt) + + w.openScope(stmt.Pos()) + w.pos(stmt) + w.stmt(stmt.Init) + + if guard, ok := stmt.Tag.(*syntax.TypeSwitchGuard); w.bool(ok) { + w.pos(guard) + if tag := guard.Lhs; w.bool(tag != nil) { + w.pos(tag) + w.string(tag.Value) + } + w.expr(guard.X) + } else { + w.expr(stmt.Tag) + } + + w.len(len(stmt.Body)) + for i, clause := range stmt.Body { + if i > 0 { + w.closeScope(clause.Pos()) + } + w.openScope(clause.Pos()) + + w.pos(clause) + w.exprList(clause.Cases) + + if obj, ok := w.p.info.Implicits[clause]; ok { + // TODO(mdempsky): These pos details are quirkish, but also + // necessary so the variable's position is correct for DWARF + // scope assignment later. It would probably be better for us to + // instead just set the variable's DWARF scoping info earlier so + // we can give it the correct position information. + pos := clause.Pos() + if typs := unpackListExpr(clause.Cases); len(typs) != 0 { + pos = typeExprEndPos(typs[len(typs)-1]) + } + w.pos(pos) + + obj := obj.(*types2.Var) + w.typ(obj.Type()) + w.addLocal(obj) + } + + w.stmts(clause.Body) + } + if len(stmt.Body) > 0 { + w.closeScope(stmt.Rbrace) + } + + w.closeScope(stmt.Rbrace) +} + +func (w *writer) label(label *syntax.Name) { + w.sync(syncLabel) + + // TODO(mdempsky): Replace label strings with dense indices. + w.string(label.Value) +} + +func (w *writer) optLabel(label *syntax.Name) { + w.sync(syncOptLabel) + if w.bool(label != nil) { + w.label(label) + } +} + +// @@@ Expressions + +func (w *writer) expr(expr syntax.Expr) { + expr = unparen(expr) // skip parens; unneeded after typecheck + + obj, inst := lookupObj(w.p.info, expr) + targs := inst.TypeArgs + + if tv, ok := w.p.info.Types[expr]; ok { + // TODO(mdempsky): Be more judicious about which types are marked as "needed". + if inst.Type != nil { + w.needType(inst.Type) + } else { + w.needType(tv.Type) + } + + if tv.IsType() { + w.code(exprType) + w.typ(tv.Type) + return + } + + if tv.Value != nil { + pos := expr.Pos() + if quirksMode() { + if obj != nil { + // Quirk: IR (and thus iexport) doesn't track position + // information for uses of declared objects. + pos = syntax.Pos{} + } else if tv.Value.Kind() == constant.String { + // Quirk: noder.sum picks a particular position for certain + // string concatenations. + pos = sumPos(expr) + } + } + + w.code(exprConst) + w.pos(pos) + w.typ(tv.Type) + w.value(tv.Value) + + // TODO(mdempsky): These details are only important for backend + // diagnostics. Explore writing them out separately. + w.op(constExprOp(expr)) + w.string(syntax.String(expr)) + return + } + } + + if obj != nil { + if isGlobal(obj) { + w.code(exprName) + w.obj(obj, targs) + return + } + + obj := obj.(*types2.Var) + assert(!obj.IsField()) + assert(targs.Len() == 0) + + w.code(exprLocal) + w.useLocal(expr.Pos(), obj) + return + } + + switch expr := expr.(type) { + default: + w.p.unexpected("expression", expr) + + case nil: // absent slice index, for condition, or switch tag + w.code(exprNone) + + case *syntax.Name: + assert(expr.Value == "_") + w.code(exprBlank) + + case *syntax.CompositeLit: + w.code(exprCompLit) + w.compLit(expr) + + case *syntax.FuncLit: + w.code(exprFuncLit) + w.funcLit(expr) + + case *syntax.SelectorExpr: + sel, ok := w.p.info.Selections[expr] + assert(ok) + + w.code(exprSelector) + w.expr(expr.X) + w.pos(expr) + w.selector(sel.Obj()) + + case *syntax.IndexExpr: + tv, ok := w.p.info.Types[expr.Index] + assert(ok && tv.IsValue()) + + w.code(exprIndex) + w.expr(expr.X) + w.pos(expr) + w.expr(expr.Index) + + case *syntax.SliceExpr: + w.code(exprSlice) + w.expr(expr.X) + w.pos(expr) + for _, n := range &expr.Index { + w.expr(n) + } + + case *syntax.AssertExpr: + w.code(exprAssert) + w.expr(expr.X) + w.pos(expr) + w.expr(expr.Type) + + case *syntax.Operation: + if expr.Y == nil { + w.code(exprUnaryOp) + w.op(unOps[expr.Op]) + w.pos(expr) + w.expr(expr.X) + break + } + + w.code(exprBinaryOp) + w.op(binOps[expr.Op]) + w.expr(expr.X) + w.pos(expr) + w.expr(expr.Y) + + case *syntax.CallExpr: + tv, ok := w.p.info.Types[expr.Fun] + assert(ok) + if tv.IsType() { + assert(len(expr.ArgList) == 1) + assert(!expr.HasDots) + + w.code(exprConvert) + w.typ(tv.Type) + w.pos(expr) + w.expr(expr.ArgList[0]) + break + } + + writeFunExpr := func() { + if selector, ok := unparen(expr.Fun).(*syntax.SelectorExpr); ok { + if sel, ok := w.p.info.Selections[selector]; ok && sel.Kind() == types2.MethodVal { + w.expr(selector.X) + w.bool(true) // method call + w.pos(selector) + w.selector(sel.Obj()) + return + } + } + + w.expr(expr.Fun) + w.bool(false) // not a method call (i.e., normal function call) + } + + w.code(exprCall) + writeFunExpr() + w.pos(expr) + w.exprs(expr.ArgList) + w.bool(expr.HasDots) + } +} + +func (w *writer) compLit(lit *syntax.CompositeLit) { + tv, ok := w.p.info.Types[lit] + assert(ok) + + w.sync(syncCompLit) + w.pos(lit) + w.typ(tv.Type) + + typ := tv.Type + if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok { + typ = ptr.Elem() + } + str, isStruct := types2.CoreType(typ).(*types2.Struct) + + w.len(len(lit.ElemList)) + for i, elem := range lit.ElemList { + if isStruct { + if kv, ok := elem.(*syntax.KeyValueExpr); ok { + // use position of expr.Key rather than of elem (which has position of ':') + w.pos(kv.Key) + w.len(fieldIndex(w.p.info, str, kv.Key.(*syntax.Name))) + elem = kv.Value + } else { + w.pos(elem) + w.len(i) + } + } else { + if kv, ok := elem.(*syntax.KeyValueExpr); w.bool(ok) { + // use position of expr.Key rather than of elem (which has position of ':') + w.pos(kv.Key) + w.expr(kv.Key) + elem = kv.Value + } + } + w.pos(elem) + w.expr(elem) + } +} + +func (w *writer) funcLit(expr *syntax.FuncLit) { + tv, ok := w.p.info.Types[expr] + assert(ok) + sig := tv.Type.(*types2.Signature) + + body, closureVars := w.p.bodyIdx(w.p.curpkg, sig, expr.Body, w.dict) + + w.sync(syncFuncLit) + w.pos(expr) + w.pos(expr.Type) // for QuirksMode + w.signature(sig) + + w.len(len(closureVars)) + for _, cv := range closureVars { + w.pos(cv.pos) + if quirksMode() { + cv.pos = expr.Body.Rbrace + } + w.useLocal(cv.pos, cv.obj) + } + + w.reloc(relocBody, body) +} + +type posObj struct { + pos syntax.Pos + obj *types2.Var +} + +func (w *writer) exprList(expr syntax.Expr) { + w.sync(syncExprList) + w.exprs(unpackListExpr(expr)) +} + +func (w *writer) exprs(exprs []syntax.Expr) { + if len(exprs) == 0 { + assert(exprs == nil) + } + + w.sync(syncExprs) + w.len(len(exprs)) + for _, expr := range exprs { + w.expr(expr) + } +} + +func (w *writer) op(op ir.Op) { + // TODO(mdempsky): Remove in favor of explicit codes? Would make + // export data more stable against internal refactorings, but low + // priority at the moment. + assert(op != 0) + w.sync(syncOp) + w.len(int(op)) +} + +func (w *writer) needType(typ types2.Type) { + // Decompose tuple into component element types. + if typ, ok := typ.(*types2.Tuple); ok { + for i := 0; i < typ.Len(); i++ { + w.needType(typ.At(i).Type()) + } + return + } + + if info := w.p.typIdx(typ, w.dict); info.derived { + w.dict.derived[info.idx].needed = true + } +} + +// @@@ Package initialization + +// Caution: This code is still clumsy, because toolstash -cmp is +// particularly sensitive to it. + +type typeDeclGen struct { + *syntax.TypeDecl + gen int + + // Implicit type parameters in scope at this type declaration. + implicits []*types2.TypeName +} + +type fileImports struct { + importedEmbed, importedUnsafe bool +} + +type declCollector struct { + pw *pkgWriter + typegen *int + file *fileImports + withinFunc bool + implicits []*types2.TypeName +} + +func (c *declCollector) withTParams(obj types2.Object) *declCollector { + tparams := objTypeParams(obj) + n := tparams.Len() + if n == 0 { + return c + } + + copy := *c + copy.implicits = copy.implicits[:len(copy.implicits):len(copy.implicits)] + for i := 0; i < n; i++ { + copy.implicits = append(copy.implicits, tparams.At(i).Obj()) + } + return © +} + +func (c *declCollector) Visit(n syntax.Node) syntax.Visitor { + pw := c.pw + + switch n := n.(type) { + case *syntax.File: + pw.checkPragmas(n.Pragma, ir.GoBuildPragma, false) + + case *syntax.ImportDecl: + pw.checkPragmas(n.Pragma, 0, false) + + switch pkgNameOf(pw.info, n).Imported().Path() { + case "embed": + c.file.importedEmbed = true + case "unsafe": + c.file.importedUnsafe = true + } + + case *syntax.ConstDecl: + pw.checkPragmas(n.Pragma, 0, false) + + case *syntax.FuncDecl: + pw.checkPragmas(n.Pragma, funcPragmas, false) + + obj := pw.info.Defs[n.Name].(*types2.Func) + pw.funDecls[obj] = n + + return c.withTParams(obj) + + case *syntax.TypeDecl: + obj := pw.info.Defs[n.Name].(*types2.TypeName) + d := typeDeclGen{TypeDecl: n, implicits: c.implicits} + + if n.Alias { + pw.checkPragmas(n.Pragma, 0, false) + } else { + pw.checkPragmas(n.Pragma, typePragmas, false) + + // Assign a unique ID to function-scoped defined types. + if c.withinFunc { + *c.typegen++ + d.gen = *c.typegen + } + } + + pw.typDecls[obj] = d + + // TODO(mdempsky): Omit? Not strictly necessary; only matters for + // type declarations within function literals within parameterized + // type declarations, but types2 the function literals will be + // constant folded away. + return c.withTParams(obj) + + case *syntax.VarDecl: + pw.checkPragmas(n.Pragma, 0, true) + + if p, ok := n.Pragma.(*pragmas); ok && len(p.Embeds) > 0 { + if err := checkEmbed(n, c.file.importedEmbed, c.withinFunc); err != nil { + pw.errorf(p.Embeds[0].Pos, "%s", err) + } + } + + // Workaround for #46208. For variable declarations that + // declare multiple variables and have an explicit type + // expression, the type expression is evaluated multiple + // times. This affects toolstash -cmp, because iexport is + // sensitive to *types.Type pointer identity. + if quirksMode() && n.Type != nil { + tv, ok := pw.info.Types[n.Type] + assert(ok) + assert(tv.IsType()) + for _, name := range n.NameList { + obj := pw.info.Defs[name].(*types2.Var) + pw.dups.add(obj.Type(), tv.Type) + } + } + + case *syntax.BlockStmt: + if !c.withinFunc { + copy := *c + copy.withinFunc = true + return © + } + } + + return c +} + +func (pw *pkgWriter) collectDecls(noders []*noder) { + var typegen int + for _, p := range noders { + var file fileImports + + syntax.Walk(p.file, &declCollector{ + pw: pw, + typegen: &typegen, + file: &file, + }) + + pw.cgoPragmas = append(pw.cgoPragmas, p.pragcgobuf...) + + for _, l := range p.linknames { + if !file.importedUnsafe { + pw.errorf(l.pos, "//go:linkname only allowed in Go files that import \"unsafe\"") + continue + } + + switch obj := pw.curpkg.Scope().Lookup(l.local).(type) { + case *types2.Func, *types2.Var: + if _, ok := pw.linknames[obj]; !ok { + pw.linknames[obj] = l.remote + } else { + pw.errorf(l.pos, "duplicate //go:linkname for %s", l.local) + } + + default: + // TODO(mdempsky): Enable after #42938 is fixed. + if false { + pw.errorf(l.pos, "//go:linkname must refer to declared function or variable") + } + } + } + } +} + +func (pw *pkgWriter) checkPragmas(p syntax.Pragma, allowed ir.PragmaFlag, embedOK bool) { + if p == nil { + return + } + pragma := p.(*pragmas) + + for _, pos := range pragma.Pos { + if pos.Flag&^allowed != 0 { + pw.errorf(pos.Pos, "misplaced compiler directive") + } + } + + if !embedOK { + for _, e := range pragma.Embeds { + pw.errorf(e.Pos, "misplaced go:embed directive") + } + } +} + +func (w *writer) pkgInit(noders []*noder) { + if quirksMode() { + posBases := posBasesOf(noders) + w.len(len(posBases)) + for _, posBase := range posBases { + w.posBase(posBase) + } + + objs := importedObjsOf(w.p.curpkg, w.p.info, noders) + w.len(len(objs)) + for _, obj := range objs { + w.qualifiedIdent(obj) + } + } + + w.len(len(w.p.cgoPragmas)) + for _, cgoPragma := range w.p.cgoPragmas { + w.strings(cgoPragma) + } + + w.sync(syncDecls) + for _, p := range noders { + for _, decl := range p.file.DeclList { + w.pkgDecl(decl) + } + } + w.code(declEnd) + + w.sync(syncEOF) +} + +func (w *writer) pkgDecl(decl syntax.Decl) { + switch decl := decl.(type) { + default: + w.p.unexpected("declaration", decl) + + case *syntax.ImportDecl: + + case *syntax.ConstDecl: + w.code(declOther) + w.pkgObjs(decl.NameList...) + + case *syntax.FuncDecl: + if decl.Name.Value == "_" { + break // skip blank functions + } + + obj := w.p.info.Defs[decl.Name].(*types2.Func) + sig := obj.Type().(*types2.Signature) + + if sig.RecvTypeParams() != nil || sig.TypeParams() != nil { + break // skip generic functions + } + + if recv := sig.Recv(); recv != nil { + w.code(declMethod) + w.typ(recvBase(recv)) + w.selector(obj) + break + } + + w.code(declFunc) + w.pkgObjs(decl.Name) + + case *syntax.TypeDecl: + if len(decl.TParamList) != 0 { + break // skip generic type decls + } + + if decl.Name.Value == "_" { + break // skip blank type decls + } + + name := w.p.info.Defs[decl.Name].(*types2.TypeName) + // Skip type declarations for interfaces that are only usable as + // type parameter bounds. + if iface, ok := name.Type().Underlying().(*types2.Interface); ok && !iface.IsMethodSet() { + break + } + + // Skip aliases to uninstantiated generic types. + // TODO(mdempsky): Revisit after #46477 is resolved. + if name.IsAlias() { + named, ok := name.Type().(*types2.Named) + if ok && named.TypeParams().Len() != 0 && named.TypeArgs().Len() == 0 { + break + } + } + + w.code(declOther) + w.pkgObjs(decl.Name) + + case *syntax.VarDecl: + w.code(declVar) + w.pos(decl) + w.pkgObjs(decl.NameList...) + w.exprList(decl.Values) + + var embeds []pragmaEmbed + if p, ok := decl.Pragma.(*pragmas); ok { + embeds = p.Embeds + } + w.len(len(embeds)) + for _, embed := range embeds { + w.pos(embed.Pos) + w.strings(embed.Patterns) + } + } +} + +func (w *writer) pkgObjs(names ...*syntax.Name) { + w.sync(syncDeclNames) + w.len(len(names)) + + for _, name := range names { + obj, ok := w.p.info.Defs[name] + assert(ok) + + w.sync(syncDeclName) + w.obj(obj, nil) + } +} + +// @@@ Helpers + +// isDefinedType reports whether obj is a defined type. +func isDefinedType(obj types2.Object) bool { + if obj, ok := obj.(*types2.TypeName); ok { + return !obj.IsAlias() + } + return false +} + +// isGlobal reports whether obj was declared at package scope. +// +// Caveat: blank objects are not declared. +func isGlobal(obj types2.Object) bool { + return obj.Parent() == obj.Pkg().Scope() +} + +// lookupObj returns the object that expr refers to, if any. If expr +// is an explicit instantiation of a generic object, then the instance +// object is returned as well. +func lookupObj(info *types2.Info, expr syntax.Expr) (obj types2.Object, inst types2.Instance) { + if index, ok := expr.(*syntax.IndexExpr); ok { + args := unpackListExpr(index.Index) + if len(args) == 1 { + tv, ok := info.Types[args[0]] + assert(ok) + if tv.IsValue() { + return // normal index expression + } + } + + expr = index.X + } + + // Strip package qualifier, if present. + if sel, ok := expr.(*syntax.SelectorExpr); ok { + if !isPkgQual(info, sel) { + return // normal selector expression + } + expr = sel.Sel + } + + if name, ok := expr.(*syntax.Name); ok { + obj = info.Uses[name] + inst = info.Instances[name] + } + return +} + +// isPkgQual reports whether the given selector expression is a +// package-qualified identifier. +func isPkgQual(info *types2.Info, sel *syntax.SelectorExpr) bool { + if name, ok := sel.X.(*syntax.Name); ok { + _, isPkgName := info.Uses[name].(*types2.PkgName) + return isPkgName + } + return false +} + +// recvBase returns the base type for the given receiver parameter. +func recvBase(recv *types2.Var) *types2.Named { + typ := recv.Type() + if ptr, ok := typ.(*types2.Pointer); ok { + typ = ptr.Elem() + } + return typ.(*types2.Named) +} + +// namesAsExpr returns a list of names as a syntax.Expr. +func namesAsExpr(names []*syntax.Name) syntax.Expr { + if len(names) == 1 { + return names[0] + } + + exprs := make([]syntax.Expr, len(names)) + for i, name := range names { + exprs[i] = name + } + return &syntax.ListExpr{ElemList: exprs} +} + +// fieldIndex returns the index of the struct field named by key. +func fieldIndex(info *types2.Info, str *types2.Struct, key *syntax.Name) int { + field := info.Uses[key].(*types2.Var) + + for i := 0; i < str.NumFields(); i++ { + if str.Field(i) == field { + return i + } + } + + panic(fmt.Sprintf("%s: %v is not a field of %v", key.Pos(), field, str)) +} + +// objTypeParams returns the type parameters on the given object. +func objTypeParams(obj types2.Object) *types2.TypeParamList { + switch obj := obj.(type) { + case *types2.Func: + sig := obj.Type().(*types2.Signature) + if sig.Recv() != nil { + return sig.RecvTypeParams() + } + return sig.TypeParams() + case *types2.TypeName: + if !obj.IsAlias() { + return obj.Type().(*types2.Named).TypeParams() + } + } + return nil +} + +func asPragmaFlag(p syntax.Pragma) ir.PragmaFlag { + if p == nil { + return 0 + } + return p.(*pragmas).Flag +} |