Matthew Dempsky has uploaded this change for review.
[dev.regabi] all: merge master (dab3e5a) into dev.regabi
This merge had two conflicts to resolve:
1. The embed code on master had somewhat substantially diverged, so
this CL tediously backported the changes to dev.regabi. In particular,
I went through all of the embed changes to gc/{embed,noder,syntax}.go
and made sure the analogous code on dev.regabi in noder/noder.go and
staticdata/embed.go mirrors it.
2. The init-cycle reporting code on master was extended slightly to
track already visited declarations to avoid exponential behavior. The
same fix is applied on dev.regabi, just using ir.NameSet instead of
map[ir.Node]bool.
Conflicts:
- src/cmd/compile/internal/gc/embed.go
- src/cmd/compile/internal/gc/noder.go
- src/cmd/compile/internal/gc/syntax.go
- src/cmd/compile/internal/pkginit/initorder.go
- src/embed/internal/embedtest/embed_test.go
- src/go/types/stdlib_test.go
Merge List:
+ 2021-01-22 dab3e5affe runtime: switch runtime to libc for openbsd/amd64
+ 2021-01-22 a1b53d85da cmd/go: add documentation for test and xtest fields output by go list
+ 2021-01-22 b268b60774 runtime: remove pthread_kill/pthread_self for openbsd
+ 2021-01-22 ec4051763d runtime: fix typo in mgcscavenge.go
+ 2021-01-22 7ece3a7b17 net/http: fix flaky TestDisableKeepAliveUpgrade
+ 2021-01-22 50cba0506f time: clarify Timer.Reset behavior on AfterFunc Timers
+ 2021-01-22 cf10e69f17 doc/go1.16: mention net/http.Transport.GetProxyConnectHeader
+ 2021-01-22 ec1b945265 doc/go1.16: mention path/filepath.WalkDir
+ 2021-01-22 11def3d40b doc/go1.16: mention syscall.AllThreadsSyscall
+ 2021-01-21 07b0235609 doc/go1.16: add notes about package-specific fs.FS changes
+ 2021-01-21 e2b4f1fea5 doc/go1.16: minor formatting fix
+ 2021-01-21 9f43a9e07b doc/go1.16: mention new debug/elf constants
+ 2021-01-21 3c2f11ba5b cmd/go: overwrite program name with full path
+ 2021-01-21 953d1feca9 all: introduce and use internal/execabs
+ 2021-01-21 b186e4d70d cmd/go: add test case for cgo CC setting
+ 2021-01-21 5a8a2265fb cmd/cgo: report exec errors a bit more clearly
+ 2021-01-21 46e2e2e9d9 cmd/go: pass resolved CC, GCCGO to cgo
+ 2021-01-21 3d40895e36 runtime: switch openbsd/arm64 to pthreads
+ 2021-01-21 d95ca91380 crypto/elliptic: fix P-224 field reduction
+ 2021-01-20 ecf4ebf100 cmd/internal/moddeps: check content of all modules in GOROOT
+ 2021-01-20 d2d155d1ae runtime: don't adjust timer pp field in timerWaiting status
+ 2021-01-20 803d18fc6c cmd/go: set Incomplete field on go list output if no files match embed
+ 2021-01-20 6e243ce71d cmd/go: have go mod vendor copy embedded files in subdirs
+ 2021-01-20 be28e5abc5 cmd/go: fix mod_get_fallback test
+ 2021-01-20 928bda4f4a runtime: convert openbsd/amd64 locking to libc
+ 2021-01-19 824f2d635c cmd/go: allow go fmt to complete when embedded file is missing
+ 2021-01-19 0575e35e50 cmd/compile: require 'go 1.16' go.mod line for //go:embed
+ 2021-01-19 ccb2e90688 cmd/link: exit before Asmb2 if error
+ 2021-01-19 ca5774a5a5 embed: treat uninitialized FS as empty
+ 2021-01-19 d047c91a6c cmd/link,runtime: switch openbsd/amd64 to pthreads
+ 2021-01-19 61debffd97 runtime: factor out usesLibcall
+ 2021-01-19 9fed39d281 runtime: factor out mStackIsSystemAllocated
+ 2021-01-18 dbab079835 runtime: free Windows event handles after last lock is dropped
+ 2021-01-18 5a8fbb0d2d os: do not close syscall.Stdin in TestReadStdin
+ 2021-01-15 682a1d2176 runtime: detect errors in DuplicateHandle
+ 2021-01-15 9f83418b83 cmd/link: remove GOROOT write in TestBuildForTvOS
+ 2021-01-15 ec9470162f cmd/compile: allow embed into any string or byte slice type
+ 2021-01-15 54198b04db cmd/compile: disallow embed of var inside func
+ 2021-01-15 b386c735e7 cmd/go: fix go generate docs
+ 2021-01-15 bb5075a525 syscall: remove RtlGenRandom and move it into internal/syscall
+ 2021-01-15 1deae0b597 os: invoke processKiller synchronously in testKillProcess
+ 2021-01-15 ff196c3e84 crypto/x509: update iOS bundled roots to version 55188.40.9
+ 2021-01-14 e125ccd10e cmd/go: in 'go mod edit', validate versions given to -retract and -exclude
+ 2021-01-14 eb330020dc cmd/dist, cmd/go: pass -arch for C compilation on Darwin
+ 2021-01-14 84e8a06f62 cmd/cgo: remove unnecessary space in cgo export header
+ 2021-01-14 0c86b999c3 cmd/test2json: document passing -test.paniconexit0
+ 2021-01-14 9135795891 cmd/go/internal/load: report positions for embed errors
+ 2021-01-14 d9b79e53bb cmd/compile: fix wrong complement for arm64 floating-point comparisons
+ 2021-01-14 c73232d08f cmd/go/internal/load: refactor setErrorPos to PackageError.setPos
+ 2021-01-14 6aa28d3e06 go/build: report positions for go:embed directives
+ 2021-01-13 7eb31d999c cmd/go: add hints to more missing sum error messages
+ 2021-01-12 ba76567bc2 cmd/go/internal/modload: delete unused *mvsReqs.next method
+ 2021-01-12 665def2c11 encoding/asn1: document unmarshaling behavior for IMPLICIT string fields
+ 2021-01-11 81ea89adf3 cmd/go: fix non-script staleness checks interacting badly with GOFLAGS
+ 2021-01-11 759309029f doc: update editors.html for Go 1.16
+ 2021-01-11 c3b4c7093a cmd/internal/objfile: don't require runtime.symtab symbol for XCOFF
+ 2021-01-08 59bfc18e34 cmd/go: add hint to read 'go help vcs' to GOVCS errors
+ 2021-01-08 cd6f3a54e4 cmd/go: revise 'go help' documentation for modules
+ 2021-01-08 6192b98751 cmd/go: make hints in error messages more consistent
+ 2021-01-08 25886cf4bd cmd/go: preserve sums for indirect deps fetched by 'go mod download'
+ 2021-01-08 6250833911 runtime/metrics: mark histogram metrics as cumulative
+ 2021-01-08 8f6a9acbb3 runtime/metrics: remove unused StopTheWorld Description field
+ 2021-01-08 6598c65646 cmd/compile: fix exponential-time init-cycle reporting
+ 2021-01-08 fefad1dc85 test: fix timeout code for invoking compiler
+ 2021-01-08 6728118e0a cmd/go: pass signals forward during "go tool"
+ 2021-01-08 e65c543f3c go/build/constraint: add parser for build tag constraint expressions
+ 2021-01-08 0c5afc4fb7 testing/fstest,os: clarify racy behavior of TestFS
+ 2021-01-08 32afcc9436 runtime/metrics: change unit on *-by-size metrics to match bucket unit
+ 2021-01-08 c6513bca5a io/fs: minor corrections to Glob doc
+ 2021-01-08 304f769ffc cmd/compile: don't short-circuit copies whose source is volatile
+ 2021-01-08 ae97717133 runtime,runtime/metrics: use explicit histogram boundaries
+ 2021-01-08 a9ccd2d795 go/build: skip string literal while findEmbed
+ 2021-01-08 d92f8add32 archive/tar: fix typo in comment
+ 2021-01-08 cab1202183 cmd/link: accept extra blocks in TestFallocate
+ 2021-01-08 ee4d32249b io/fs: minor corrections to Glob release date
+ 2021-01-08 54bd1ccce2 cmd: update to latest golang.org/x/tools
+ 2021-01-07 9ec21a8f34 Revert "reflect: support multiple keys in struct tags"
+ 2021-01-07 091414b5b7 io/fs: correct WalkDirFunc documentation
+ 2021-01-07 9b55088d6b doc/go1.16: add release note for disallowing non-ASCII import paths
+ 2021-01-07 fa90aaca7d cmd/compile: fix late expand_calls leaf type for OpStructSelect/OpArraySelect
+ 2021-01-07 7cee66d4cb cmd/go: add documentation for Embed fields in go list output
+ 2021-01-07 e60cffa4ca html/template: attach functions to namespace
+ 2021-01-07 6da2d3b7d7 cmd/link: fix typo in asm.go
+ 2021-01-07 df81a15819 runtime: check mips64 VDSO clock_gettime return code
+ 2021-01-06 4787e906cf crypto/x509: rollback new CertificateRequest fields
+ 2021-01-06 c9658bee93 cmd/go: make module suggestion more friendly
+ 2021-01-06 4c668b25c6 runtime/metrics: fix panic message for Float64Histogram
+ 2021-01-06 d2131704a6 net/http/httputil: fix deadlock in DumpRequestOut
+ 2021-01-05 3e1e13ce6d cmd/go: set cfg.BuildMod to "readonly" by default with no module root
+ 2021-01-05 0b0d004983 cmd/go: pass embedcfg to gccgo if supported
+ 2021-01-05 1b85e7c057 cmd/go: don't scan gccgo standard library packages for imports
+ 2021-01-05 6b37b15d95 runtime: don't take allglock in tracebackothers
+ 2021-01-04 9eef49cfa6 math/rand: fix typo in comment
+ 2021-01-04 b01fb2af9e testing/fstest: fix typo in error message
+ 2021-01-01 3dd5867605 doc: 2021 is the Year of the Gopher
+ 2020-12-31 95ce805d14 io/fs: remove darwin/arm64 special condition
+ 2020-12-30 20d0991b86 lib/time, time/tzdata: update tzdata to 2020f
+ 2020-12-30 ed301733bb misc/cgo/testcarchive: remove special flags for Darwin/ARM
+ 2020-12-30 0ae2e032f2 misc/cgo/test: enable TestCrossPackageTests on darwin/arm64
+ 2020-12-29 780b4de16b misc/ios: fix wording for command line instructions
+ 2020-12-29 b4a71c95d2 doc/go1.16: reference misc/ios/README for how to build iOS programs
+ 2020-12-29 f83e0f6616 misc/ios: add to README how to build ios executables
+ 2020-12-28 4fd9455882 io/fs: fix typo in comment
Change-Id: I2f257bbc5fbb05f15c2d959f8cfe0ce13b083538
---
D src/cmd/compile/internal/gc/embed.go
D src/cmd/compile/internal/gc/initorder.go
D src/cmd/compile/internal/gc/noder.go
D src/cmd/compile/internal/gc/syntax.go
M src/cmd/compile/internal/noder/noder.go
M src/cmd/compile/internal/pkginit/initorder.go
M src/cmd/compile/internal/staticdata/embed.go
M src/embed/internal/embedtest/embed_test.go
M src/go/types/stdlib_test.go
10 files changed, 43 insertions(+), 3,652 deletions(-)
diff --git a/src/cmd/compile/internal/gc/embed.go b/src/cmd/compile/internal/gc/embed.go
deleted file mode 100644
index 37082ef..0000000
--- a/src/cmd/compile/internal/gc/embed.go
+++ /dev/null
@@ -1,259 +0,0 @@
-<<<<<<< HEAD (d7e71c [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSe)
-=======
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package gc
-
-import (
- "cmd/compile/internal/syntax"
- "cmd/compile/internal/types"
- "cmd/internal/obj"
- "encoding/json"
- "io/ioutil"
- "log"
- "path"
- "sort"
- "strconv"
- "strings"
-)
-
-var embedlist []*Node
-
-var embedCfg struct {
- Patterns map[string][]string
- Files map[string]string
-}
-
-func readEmbedCfg(file string) {
- data, err := ioutil.ReadFile(file)
- if err != nil {
- log.Fatalf("-embedcfg: %v", err)
- }
- if err := json.Unmarshal(data, &embedCfg); err != nil {
- log.Fatalf("%s: %v", file, err)
- }
- if embedCfg.Patterns == nil {
- log.Fatalf("%s: invalid embedcfg: missing Patterns", file)
- }
- if embedCfg.Files == nil {
- log.Fatalf("%s: invalid embedcfg: missing Files", file)
- }
-}
-
-const (
- embedUnknown = iota
- embedBytes
- embedString
- embedFiles
-)
-
-func varEmbed(p *noder, names []*Node, typ *Node, exprs []*Node, embeds []PragmaEmbed) {
- haveEmbed := false
- for _, decl := range p.file.DeclList {
- imp, ok := decl.(*syntax.ImportDecl)
- if !ok {
- // imports always come first
- break
- }
- path, _ := strconv.Unquote(imp.Path.Value)
- if path == "embed" {
- haveEmbed = true
- break
- }
- }
-
- pos := embeds[0].Pos
- if !haveEmbed {
- p.yyerrorpos(pos, "invalid go:embed: missing import \"embed\"")
- return
- }
- if len(names) > 1 {
- p.yyerrorpos(pos, "go:embed cannot apply to multiple vars")
- return
- }
- if len(exprs) > 0 {
- p.yyerrorpos(pos, "go:embed cannot apply to var with initializer")
- return
- }
- if typ == nil {
- // Should not happen, since len(exprs) == 0 now.
- p.yyerrorpos(pos, "go:embed cannot apply to var without type")
- return
- }
- if dclcontext != PEXTERN {
- p.yyerrorpos(pos, "go:embed cannot apply to var inside func")
- return
- }
-
- var list []irEmbed
- for _, e := range embeds {
- list = append(list, irEmbed{Pos: p.makeXPos(e.Pos), Patterns: e.Patterns})
- }
- v := names[0]
- v.Name.Param.SetEmbedList(list)
- embedlist = append(embedlist, v)
-}
-
-func embedFileList(v *Node, kind int) []string {
- // Build list of files to store.
- have := make(map[string]bool)
- var list []string
- for _, e := range v.Name.Param.EmbedList() {
- for _, pattern := range e.Patterns {
- files, ok := embedCfg.Patterns[pattern]
- if !ok {
- yyerrorl(e.Pos, "invalid go:embed: build system did not map pattern: %s", pattern)
- }
- for _, file := range files {
- if embedCfg.Files[file] == "" {
- yyerrorl(e.Pos, "invalid go:embed: build system did not map file: %s", file)
- continue
- }
- if !have[file] {
- have[file] = true
- list = append(list, file)
- }
- if kind == embedFiles {
- for dir := path.Dir(file); dir != "." && !have[dir]; dir = path.Dir(dir) {
- have[dir] = true
- list = append(list, dir+"/")
- }
- }
- }
- }
- }
- sort.Slice(list, func(i, j int) bool {
- return embedFileLess(list[i], list[j])
- })
-
- if kind == embedString || kind == embedBytes {
- if len(list) > 1 {
- yyerrorl(v.Pos, "invalid go:embed: multiple files for type %v", v.Type)
- return nil
- }
- }
-
- return list
-}
-
-// embedKind determines the kind of embedding variable.
-func embedKind(typ *types.Type) int {
- if typ.Sym != nil && typ.Sym.Name == "FS" && (typ.Sym.Pkg.Path == "embed" || (typ.Sym.Pkg == localpkg && myimportpath == "embed")) {
- return embedFiles
- }
- if typ.Etype == types.TSTRING {
- return embedString
- }
- if typ.Etype == types.TSLICE && typ.Elem().Etype == types.TUINT8 {
- return embedBytes
- }
- return embedUnknown
-}
-
-func embedFileNameSplit(name string) (dir, elem string, isDir bool) {
- if name[len(name)-1] == '/' {
- isDir = true
- name = name[:len(name)-1]
- }
- i := len(name) - 1
- for i >= 0 && name[i] != '/' {
- i--
- }
- if i < 0 {
- return ".", name, isDir
- }
- return name[:i], name[i+1:], isDir
-}
-
-// embedFileLess implements the sort order for a list of embedded files.
-// See the comment inside ../../../../embed/embed.go's Files struct for rationale.
-func embedFileLess(x, y string) bool {
- xdir, xelem, _ := embedFileNameSplit(x)
- ydir, yelem, _ := embedFileNameSplit(y)
- return xdir < ydir || xdir == ydir && xelem < yelem
-}
-
-func dumpembeds() {
- for _, v := range embedlist {
- initEmbed(v)
- }
-}
-
-// initEmbed emits the init data for a //go:embed variable,
-// which is either a string, a []byte, or an embed.FS.
-func initEmbed(v *Node) {
- commentPos := v.Name.Param.EmbedList()[0].Pos
- if !langSupported(1, 16, localpkg) {
- lno := lineno
- lineno = commentPos
- yyerrorv("go1.16", "go:embed")
- lineno = lno
- return
- }
- if embedCfg.Patterns == nil {
- yyerrorl(commentPos, "invalid go:embed: build system did not supply embed configuration")
- return
- }
- kind := embedKind(v.Type)
- if kind == embedUnknown {
- yyerrorl(v.Pos, "go:embed cannot apply to var of type %v", v.Type)
- return
- }
-
- files := embedFileList(v, kind)
- switch kind {
- case embedString, embedBytes:
- file := files[0]
- fsym, size, err := fileStringSym(v.Pos, embedCfg.Files[file], kind == embedString, nil)
- if err != nil {
- yyerrorl(v.Pos, "embed %s: %v", file, err)
- }
- sym := v.Sym.Linksym()
- off := 0
- off = dsymptr(sym, off, fsym, 0) // data string
- off = duintptr(sym, off, uint64(size)) // len
- if kind == embedBytes {
- duintptr(sym, off, uint64(size)) // cap for slice
- }
-
- case embedFiles:
- slicedata := Ctxt.Lookup(`"".` + v.Sym.Name + `.files`)
- off := 0
- // []files pointed at by Files
- off = dsymptr(slicedata, off, slicedata, 3*Widthptr) // []file, pointing just past slice
- off = duintptr(slicedata, off, uint64(len(files)))
- off = duintptr(slicedata, off, uint64(len(files)))
-
- // embed/embed.go type file is:
- // name string
- // data string
- // hash [16]byte
- // Emit one of these per file in the set.
- const hashSize = 16
- hash := make([]byte, hashSize)
- for _, file := range files {
- off = dsymptr(slicedata, off, stringsym(v.Pos, file), 0) // file string
- off = duintptr(slicedata, off, uint64(len(file)))
- if strings.HasSuffix(file, "/") {
- // entry for directory - no data
- off = duintptr(slicedata, off, 0)
- off = duintptr(slicedata, off, 0)
- off += hashSize
- } else {
- fsym, size, err := fileStringSym(v.Pos, embedCfg.Files[file], true, hash)
- if err != nil {
- yyerrorl(v.Pos, "embed %s: %v", file, err)
- }
- off = dsymptr(slicedata, off, fsym, 0) // data string
- off = duintptr(slicedata, off, uint64(size))
- off = int(slicedata.WriteBytes(Ctxt, int64(off), hash))
- }
- }
- ggloblsym(slicedata, int32(off), obj.RODATA|obj.LOCAL)
- sym := v.Sym.Linksym()
- dsymptr(sym, 0, slicedata, 0)
- }
-}
->>>>>>> BRANCH (dab3e5 runtime: switch runtime to libc for openbsd/amd64)
diff --git a/src/cmd/compile/internal/gc/initorder.go b/src/cmd/compile/internal/gc/initorder.go
deleted file mode 100644
index 2c1a493..0000000
--- a/src/cmd/compile/internal/gc/initorder.go
+++ /dev/null
@@ -1,361 +0,0 @@
-<<<<<<< HEAD (d7e71c [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSe)
-=======
-// Copyright 2019 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package gc
-
-import (
- "bytes"
- "container/heap"
- "fmt"
-)
-
-// Package initialization
-//
-// Here we implement the algorithm for ordering package-level variable
-// initialization. The spec is written in terms of variable
-// initialization, but multiple variables initialized by a single
-// assignment are handled together, so here we instead focus on
-// ordering initialization assignments. Conveniently, this maps well
-// to how we represent package-level initializations using the Node
-// AST.
-//
-// Assignments are in one of three phases: NotStarted, Pending, or
-// Done. For assignments in the Pending phase, we use Xoffset to
-// record the number of unique variable dependencies whose
-// initialization assignment is not yet Done. We also maintain a
-// "blocking" map that maps assignments back to all of the assignments
-// that depend on it.
-//
-// For example, for an initialization like:
-//
-// var x = f(a, b, b)
-// var a, b = g()
-//
-// the "x = f(a, b, b)" assignment depends on two variables (a and b),
-// so its Xoffset will be 2. Correspondingly, the "a, b = g()"
-// assignment's "blocking" entry will have two entries back to x's
-// assignment.
-//
-// Logically, initialization works by (1) taking all NotStarted
-// assignments, calculating their dependencies, and marking them
-// Pending; (2) adding all Pending assignments with Xoffset==0 to a
-// "ready" priority queue (ordered by variable declaration position);
-// and (3) iteratively processing the next Pending assignment from the
-// queue, decreasing the Xoffset of assignments it's blocking, and
-// adding them to the queue if decremented to 0.
-//
-// As an optimization, we actually apply each of these three steps for
-// each assignment. This yields the same order, but keeps queue size
-// down and thus also heap operation costs.
-
-// Static initialization phase.
-// These values are stored in two bits in Node.flags.
-const (
- InitNotStarted = iota
- InitDone
- InitPending
-)
-
-type InitOrder struct {
- // blocking maps initialization assignments to the assignments
- // that depend on it.
- blocking map[*Node][]*Node
-
- // ready is the queue of Pending initialization assignments
- // that are ready for initialization.
- ready declOrder
-}
-
-// initOrder computes initialization order for a list l of
-// package-level declarations (in declaration order) and outputs the
-// corresponding list of statements to include in the init() function
-// body.
-func initOrder(l []*Node) []*Node {
- s := InitSchedule{
- initplans: make(map[*Node]*InitPlan),
- inittemps: make(map[*Node]*Node),
- }
- o := InitOrder{
- blocking: make(map[*Node][]*Node),
- }
-
- // Process all package-level assignment in declaration order.
- for _, n := range l {
- switch n.Op {
- case OAS, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
- o.processAssign(n)
- o.flushReady(s.staticInit)
- case ODCLCONST, ODCLFUNC, ODCLTYPE:
- // nop
- default:
- Fatalf("unexpected package-level statement: %v", n)
- }
- }
-
- // Check that all assignments are now Done; if not, there must
- // have been a dependency cycle.
- for _, n := range l {
- switch n.Op {
- case OAS, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
- if n.Initorder() != InitDone {
- // If there have already been errors
- // printed, those errors may have
- // confused us and there might not be
- // a loop. Let the user fix those
- // first.
- if nerrors > 0 {
- errorexit()
- }
-
- findInitLoopAndExit(firstLHS(n), new([]*Node), make(map[*Node]bool))
- Fatalf("initialization unfinished, but failed to identify loop")
- }
- }
- }
-
- // Invariant consistency check. If this is non-zero, then we
- // should have found a cycle above.
- if len(o.blocking) != 0 {
- Fatalf("expected empty map: %v", o.blocking)
- }
-
- return s.out
-}
-
-func (o *InitOrder) processAssign(n *Node) {
- if n.Initorder() != InitNotStarted || n.Xoffset != BADWIDTH {
- Fatalf("unexpected state: %v, %v, %v", n, n.Initorder(), n.Xoffset)
- }
-
- n.SetInitorder(InitPending)
- n.Xoffset = 0
-
- // Compute number of variable dependencies and build the
- // inverse dependency ("blocking") graph.
- for dep := range collectDeps(n, true) {
- defn := dep.Name.Defn
- // Skip dependencies on functions (PFUNC) and
- // variables already initialized (InitDone).
- if dep.Class() != PEXTERN || defn.Initorder() == InitDone {
- continue
- }
- n.Xoffset++
- o.blocking[defn] = append(o.blocking[defn], n)
- }
-
- if n.Xoffset == 0 {
- heap.Push(&o.ready, n)
- }
-}
-
-// flushReady repeatedly applies initialize to the earliest (in
-// declaration order) assignment ready for initialization and updates
-// the inverse dependency ("blocking") graph.
-func (o *InitOrder) flushReady(initialize func(*Node)) {
- for o.ready.Len() != 0 {
- n := heap.Pop(&o.ready).(*Node)
- if n.Initorder() != InitPending || n.Xoffset != 0 {
- Fatalf("unexpected state: %v, %v, %v", n, n.Initorder(), n.Xoffset)
- }
-
- initialize(n)
- n.SetInitorder(InitDone)
- n.Xoffset = BADWIDTH
-
- blocked := o.blocking[n]
- delete(o.blocking, n)
-
- for _, m := range blocked {
- m.Xoffset--
- if m.Xoffset == 0 {
- heap.Push(&o.ready, m)
- }
- }
- }
-}
-
-// findInitLoopAndExit searches for an initialization loop involving variable
-// or function n. If one is found, it reports the loop as an error and exits.
-//
-// path points to a slice used for tracking the sequence of
-// variables/functions visited. Using a pointer to a slice allows the
-// slice capacity to grow and limit reallocations.
-func findInitLoopAndExit(n *Node, path *[]*Node, ok map[*Node]bool) {
- for i, x := range *path {
- if x == n {
- reportInitLoopAndExit((*path)[i:])
- return
- }
- }
-
- // There might be multiple loops involving n; by sorting
- // references, we deterministically pick the one reported.
- refers := collectDeps(n.Name.Defn, false).Sorted(func(ni, nj *Node) bool {
- return ni.Pos.Before(nj.Pos)
- })
-
- *path = append(*path, n)
- for _, ref := range refers {
- // Short-circuit variables that were initialized.
- if ref.Class() == PEXTERN && ref.Name.Defn.Initorder() == InitDone || ok[ref] {
- continue
- }
- findInitLoopAndExit(ref, path, ok)
- }
-
- // n is not involved in a cycle.
- // Record that fact to avoid checking it again when reached another way,
- // or else this traversal will take exponential time traversing all paths
- // through the part of the package's call graph implicated in the cycle.
- ok[n] = true
-
- *path = (*path)[:len(*path)-1]
-}
-
-// reportInitLoopAndExit reports and initialization loop as an error
-// and exits. However, if l is not actually an initialization loop, it
-// simply returns instead.
-func reportInitLoopAndExit(l []*Node) {
- // Rotate loop so that the earliest variable declaration is at
- // the start.
- i := -1
- for j, n := range l {
- if n.Class() == PEXTERN && (i == -1 || n.Pos.Before(l[i].Pos)) {
- i = j
- }
- }
- if i == -1 {
- // False positive: loop only involves recursive
- // functions. Return so that findInitLoop can continue
- // searching.
- return
- }
- l = append(l[i:], l[:i]...)
-
- // TODO(mdempsky): Method values are printed as "T.m-fm"
- // rather than "T.m". Figure out how to avoid that.
-
- var msg bytes.Buffer
- fmt.Fprintf(&msg, "initialization loop:\n")
- for _, n := range l {
- fmt.Fprintf(&msg, "\t%v: %v refers to\n", n.Line(), n)
- }
- fmt.Fprintf(&msg, "\t%v: %v", l[0].Line(), l[0])
-
- yyerrorl(l[0].Pos, msg.String())
- errorexit()
-}
-
-// collectDeps returns all of the package-level functions and
-// variables that declaration n depends on. If transitive is true,
-// then it also includes the transitive dependencies of any depended
-// upon functions (but not variables).
-func collectDeps(n *Node, transitive bool) NodeSet {
- d := initDeps{transitive: transitive}
- switch n.Op {
- case OAS:
- d.inspect(n.Right)
- case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
- d.inspect(n.Right)
- case ODCLFUNC:
- d.inspectList(n.Nbody)
- default:
- Fatalf("unexpected Op: %v", n.Op)
- }
- return d.seen
-}
-
-type initDeps struct {
- transitive bool
- seen NodeSet
-}
-
-func (d *initDeps) inspect(n *Node) { inspect(n, d.visit) }
-func (d *initDeps) inspectList(l Nodes) { inspectList(l, d.visit) }
-
-// visit calls foundDep on any package-level functions or variables
-// referenced by n, if any.
-func (d *initDeps) visit(n *Node) bool {
- switch n.Op {
- case ONAME:
- if n.isMethodExpression() {
- d.foundDep(asNode(n.Type.FuncType().Nname))
- return false
- }
-
- switch n.Class() {
- case PEXTERN, PFUNC:
- d.foundDep(n)
- }
-
- case OCLOSURE:
- d.inspectList(n.Func.Closure.Nbody)
-
- case ODOTMETH, OCALLPART:
- d.foundDep(asNode(n.Type.FuncType().Nname))
- }
-
- return true
-}
-
-// foundDep records that we've found a dependency on n by adding it to
-// seen.
-func (d *initDeps) foundDep(n *Node) {
- // Can happen with method expressions involving interface
- // types; e.g., fixedbugs/issue4495.go.
- if n == nil {
- return
- }
-
- // Names without definitions aren't interesting as far as
- // initialization ordering goes.
- if n.Name.Defn == nil {
- return
- }
-
- if d.seen.Has(n) {
- return
- }
- d.seen.Add(n)
- if d.transitive && n.Class() == PFUNC {
- d.inspectList(n.Name.Defn.Nbody)
- }
-}
-
-// declOrder implements heap.Interface, ordering assignment statements
-// by the position of their first LHS expression.
-//
-// N.B., the Pos of the first LHS expression is used because because
-// an OAS node's Pos may not be unique. For example, given the
-// declaration "var a, b = f(), g()", "a" must be ordered before "b",
-// but both OAS nodes use the "=" token's position as their Pos.
-type declOrder []*Node
-
-func (s declOrder) Len() int { return len(s) }
-func (s declOrder) Less(i, j int) bool { return firstLHS(s[i]).Pos.Before(firstLHS(s[j]).Pos) }
-func (s declOrder) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-
-func (s *declOrder) Push(x interface{}) { *s = append(*s, x.(*Node)) }
-func (s *declOrder) Pop() interface{} {
- n := (*s)[len(*s)-1]
- *s = (*s)[:len(*s)-1]
- return n
-}
-
-// firstLHS returns the first expression on the left-hand side of
-// assignment n.
-func firstLHS(n *Node) *Node {
- switch n.Op {
- case OAS:
- return n.Left
- case OAS2DOTTYPE, OAS2FUNC, OAS2RECV, OAS2MAPR:
- return n.List.First()
- }
-
- Fatalf("unexpected Op: %v", n.Op)
- return nil
-}
->>>>>>> BRANCH (dab3e5 runtime: switch runtime to libc for openbsd/amd64)
diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go
deleted file mode 100644
index b9066e2..0000000
--- a/src/cmd/compile/internal/gc/noder.go
+++ /dev/null
@@ -1,1759 +0,0 @@
-<<<<<<< HEAD (d7e71c [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSe)
-=======
-// 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 gc
-
-import (
- "fmt"
- "os"
- "path/filepath"
- "runtime"
- "strconv"
- "strings"
- "unicode"
- "unicode/utf8"
-
- "cmd/compile/internal/syntax"
- "cmd/compile/internal/types"
- "cmd/internal/obj"
- "cmd/internal/objabi"
- "cmd/internal/src"
-)
-
-// parseFiles concurrently parses files into *syntax.File structures.
-// Each declaration in every *syntax.File is converted to a syntax tree
-// and its root represented by *Node is appended to xtop.
-// Returns the total count of parsed lines.
-func parseFiles(filenames []string) uint {
- noders := make([]*noder, 0, len(filenames))
- // Limit the number of simultaneously open files.
- sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10)
-
- for _, filename := range filenames {
- p := &noder{
- basemap: make(map[*syntax.PosBase]*src.PosBase),
- err: make(chan syntax.Error),
- }
- noders = append(noders, p)
-
- go func(filename string) {
- sem <- struct{}{}
- defer func() { <-sem }()
- defer close(p.err)
- base := 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(base, f, p.error, p.pragma, syntax.CheckBranches) // errors are tracked via p.error
- }(filename)
- }
-
- var lines uint
- for _, p := range noders {
- for e := range p.err {
- p.yyerrorpos(e.Pos, "%s", e.Msg)
- }
-
- p.node()
- lines += p.file.Lines
- p.file = nil // release memory
-
- if nsyntaxerrors != 0 {
- errorexit()
- }
- // Always run testdclstack here, even when debug_dclstack is not set, as a sanity measure.
- testdclstack()
- }
-
- localpkg.Height = myheight
-
- return lines
-}
-
-// makeSrcPosBase translates from a *syntax.PosBase to a *src.PosBase.
-func (p *noder) makeSrcPosBase(b0 *syntax.PosBase) *src.PosBase {
- // fast path: most likely PosBase hasn't changed
- if p.basecache.last == b0 {
- return p.basecache.base
- }
-
- b1, ok := p.basemap[b0]
- if !ok {
- fn := b0.Filename()
- if b0.IsFileBase() {
- b1 = src.NewFileBase(fn, absFilename(fn))
- } else {
- // line directive base
- p0 := b0.Pos()
- p0b := p0.Base()
- if p0b == b0 {
- panic("infinite recursion in makeSrcPosBase")
- }
- p1 := src.MakePos(p.makeSrcPosBase(p0b), p0.Line(), p0.Col())
- b1 = src.NewLinePragmaBase(p1, fn, fileh(fn), b0.Line(), b0.Col())
- }
- p.basemap[b0] = b1
- }
-
- // update cache
- p.basecache.last = b0
- p.basecache.base = b1
-
- return b1
-}
-
-func (p *noder) makeXPos(pos syntax.Pos) (_ src.XPos) {
- return Ctxt.PosTable.XPos(src.MakePos(p.makeSrcPosBase(pos.Base()), pos.Line(), pos.Col()))
-}
-
-func (p *noder) yyerrorpos(pos syntax.Pos, format string, args ...interface{}) {
- yyerrorl(p.makeXPos(pos), format, args...)
-}
-
-var pathPrefix string
-
-// TODO(gri) Can we eliminate fileh in favor of absFilename?
-func fileh(name string) string {
- return objabi.AbsFile("", name, pathPrefix)
-}
-
-func absFilename(name string) string {
- return objabi.AbsFile(Ctxt.Pathname, name, pathPrefix)
-}
-
-// noder transforms package syntax's AST into a Node tree.
-type noder struct {
- basemap map[*syntax.PosBase]*src.PosBase
- basecache struct {
- last *syntax.PosBase
- base *src.PosBase
- }
-
- file *syntax.File
- linknames []linkname
- pragcgobuf [][]string
- err chan syntax.Error
- scope ScopeID
- importedUnsafe bool
- importedEmbed 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
-
- lastCloseScopePos syntax.Pos
-}
-
-func (p *noder) funcBody(fn *Node, block *syntax.BlockStmt) {
- oldScope := p.scope
- p.scope = 0
- funchdr(fn)
-
- if block != nil {
- body := p.stmts(block.List)
- if body == nil {
- body = []*Node{nod(OEMPTY, nil, nil)}
- }
- fn.Nbody.Set(body)
-
- lineno = p.makeXPos(block.Rbrace)
- fn.Func.Endlineno = lineno
- }
-
- funcbody()
- p.scope = oldScope
-}
-
-func (p *noder) openScope(pos syntax.Pos) {
- types.Markdcl()
-
- if trackScopes {
- Curfn.Func.Parents = append(Curfn.Func.Parents, p.scope)
- p.scopeVars = append(p.scopeVars, len(Curfn.Func.Dcl))
- p.scope = ScopeID(len(Curfn.Func.Parents))
-
- p.markScope(pos)
- }
-}
-
-func (p *noder) closeScope(pos syntax.Pos) {
- p.lastCloseScopePos = pos
- types.Popdcl()
-
- if trackScopes {
- scopeVars := p.scopeVars[len(p.scopeVars)-1]
- p.scopeVars = p.scopeVars[:len(p.scopeVars)-1]
- if scopeVars == len(Curfn.Func.Dcl) {
- // no variables were declared in this scope, so we can retract it.
-
- if int(p.scope) != len(Curfn.Func.Parents) {
- Fatalf("scope tracking inconsistency, no variables declared but scopes were not retracted")
- }
-
- p.scope = Curfn.Func.Parents[p.scope-1]
- Curfn.Func.Parents = Curfn.Func.Parents[:len(Curfn.Func.Parents)-1]
-
- nmarks := len(Curfn.Func.Marks)
- Curfn.Func.Marks[nmarks-1].Scope = p.scope
- prevScope := ScopeID(0)
- if nmarks >= 2 {
- prevScope = Curfn.Func.Marks[nmarks-2].Scope
- }
- if Curfn.Func.Marks[nmarks-1].Scope == prevScope {
- Curfn.Func.Marks = Curfn.Func.Marks[:nmarks-1]
- }
- return
- }
-
- p.scope = Curfn.Func.Parents[p.scope-1]
-
- p.markScope(pos)
- }
-}
-
-func (p *noder) markScope(pos syntax.Pos) {
- xpos := p.makeXPos(pos)
- if i := len(Curfn.Func.Marks); i > 0 && Curfn.Func.Marks[i-1].Pos == xpos {
- Curfn.Func.Marks[i-1].Scope = p.scope
- } else {
- Curfn.Func.Marks = append(Curfn.Func.Marks, Mark{xpos, p.scope})
- }
-}
-
-// 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.lastCloseScopePos)
-}
-
-// linkname records a //go:linkname directive.
-type linkname struct {
- pos syntax.Pos
- local string
- remote string
-}
-
-func (p *noder) node() {
- types.Block = 1
- p.importedUnsafe = false
- p.importedEmbed = false
-
- p.setlineno(p.file.PkgName)
- mkpackage(p.file.PkgName.Value)
-
- if pragma, ok := p.file.Pragma.(*Pragma); ok {
- pragma.Flag &^= GoBuildPragma
- p.checkUnused(pragma)
- }
-
- xtop = append(xtop, p.decls(p.file.DeclList)...)
-
- for _, n := range p.linknames {
- if !p.importedUnsafe {
- p.yyerrorpos(n.pos, "//go:linkname only allowed in Go files that import \"unsafe\"")
- continue
- }
- s := lookup(n.local)
- if n.remote != "" {
- s.Linkname = n.remote
- } else {
- // Use the default object symbol name if the
- // user didn't provide one.
- if myimportpath == "" {
- p.yyerrorpos(n.pos, "//go:linkname requires linkname argument or -p compiler flag")
- } else {
- s.Linkname = objabi.PathToPrefix(myimportpath) + "." + n.local
- }
- }
- }
-
- // The linker expects an ABI0 wrapper for all cgo-exported
- // functions.
- for _, prag := range p.pragcgobuf {
- switch prag[0] {
- case "cgo_export_static", "cgo_export_dynamic":
- if symabiRefs == nil {
- symabiRefs = make(map[string]obj.ABI)
- }
- symabiRefs[prag[1]] = obj.ABI0
- }
- }
-
- pragcgobuf = append(pragcgobuf, p.pragcgobuf...)
- lineno = src.NoXPos
- clearImports()
-}
-
-func (p *noder) decls(decls []syntax.Decl) (l []*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.Bad {
- return // avoid follow-on errors if there was a syntax error
- }
-
- if pragma, ok := imp.Pragma.(*Pragma); ok {
- p.checkUnused(pragma)
- }
-
- val := p.basicLit(imp.Path)
- ipkg := importfile(&val)
- if ipkg == nil {
- if nerrors == 0 {
- Fatalf("phase error in import")
- }
- return
- }
-
- if ipkg == unsafepkg {
- p.importedUnsafe = true
- }
- if ipkg.Path == "embed" {
- p.importedEmbed = true
- }
-
- ipkg.Direct = true
-
- var my *types.Sym
- if imp.LocalPkgName != nil {
- my = p.name(imp.LocalPkgName)
- } else {
- my = lookup(ipkg.Name)
- }
-
- pack := p.nod(imp, OPACK, nil, nil)
- pack.Sym = my
- pack.Name.Pkg = ipkg
-
- switch my.Name {
- case ".":
- importdot(ipkg, pack)
- return
- case "init":
- yyerrorl(pack.Pos, "cannot import package as init - init must be a func")
- return
- case "_":
- return
- }
- if my.Def != nil {
- redeclare(pack.Pos, my, "as imported package name")
- }
- my.Def = asTypesNode(pack)
- my.Lastlineno = pack.Pos
- my.Block = 1 // at top level
-}
-
-func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
- names := p.declNames(decl.NameList)
- typ := p.typeExprOrNil(decl.Type)
-
- var exprs []*Node
- if decl.Values != nil {
- exprs = p.exprList(decl.Values)
- }
-
- if pragma, ok := decl.Pragma.(*Pragma); ok {
- if len(pragma.Embeds) > 0 {
- if !p.importedEmbed {
- // This check can't be done when building the list pragma.Embeds
- // because that list is created before the noder starts walking over the file,
- // so at that point it hasn't seen the imports.
- // We're left to check now, just before applying the //go:embed lines.
- for _, e := range pragma.Embeds {
- p.yyerrorpos(e.Pos, "//go:embed only allowed in Go files that import \"embed\"")
- }
- } else {
- varEmbed(p, names, typ, exprs, pragma.Embeds)
- }
- pragma.Embeds = nil
- }
- p.checkUnused(pragma)
- }
-
- p.setlineno(decl)
- return variter(names, typ, exprs)
-}
-
-// 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 *Node
- values []*Node
- iota int64
-}
-
-func (p *noder) constDecl(decl *syntax.ConstDecl, cs *constState) []*Node {
- if decl.Group == nil || decl.Group != cs.group {
- *cs = constState{
- group: decl.Group,
- }
- }
-
- if pragma, ok := decl.Pragma.(*Pragma); ok {
- p.checkUnused(pragma)
- }
-
- names := p.declNames(decl.NameList)
- typ := p.typeExprOrNil(decl.Type)
-
- var values []*Node
- if decl.Values != nil {
- values = p.exprList(decl.Values)
- cs.typ, cs.values = typ, values
- } else {
- if typ != nil {
- yyerror("const declaration cannot have type without expression")
- }
- typ, values = cs.typ, cs.values
- }
-
- nn := make([]*Node, 0, len(names))
- for i, n := range names {
- if i >= len(values) {
- yyerror("missing value in const declaration")
- break
- }
- v := values[i]
- if decl.Values == nil {
- v = treecopy(v, n.Pos)
- }
-
- n.Op = OLITERAL
- declare(n, dclcontext)
-
- n.Name.Param.Ntype = typ
- n.Name.Defn = v
- n.SetIota(cs.iota)
-
- nn = append(nn, p.nod(decl, ODCLCONST, n, nil))
- }
-
- if len(values) > len(names) {
- yyerror("extra expression in const declaration")
- }
-
- cs.iota++
-
- return nn
-}
-
-func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
- n := p.declName(decl.Name)
- n.Op = OTYPE
- declare(n, dclcontext)
-
- // decl.Type may be nil but in that case we got a syntax error during parsing
- typ := p.typeExprOrNil(decl.Type)
-
- param := n.Name.Param
- param.Ntype = typ
- param.SetAlias(decl.Alias)
- if pragma, ok := decl.Pragma.(*Pragma); ok {
- if !decl.Alias {
- param.SetPragma(pragma.Flag & TypePragmas)
- pragma.Flag &^= TypePragmas
- }
- p.checkUnused(pragma)
- }
-
- nod := p.nod(decl, ODCLTYPE, n, nil)
- if param.Alias() && !langSupported(1, 9, localpkg) {
- yyerrorl(nod.Pos, "type aliases only supported as of -lang=go1.9")
- }
- return nod
-}
-
-func (p *noder) declNames(names []*syntax.Name) []*Node {
- nodes := make([]*Node, 0, len(names))
- for _, name := range names {
- nodes = append(nodes, p.declName(name))
- }
- return nodes
-}
-
-func (p *noder) declName(name *syntax.Name) *Node {
- n := dclname(p.name(name))
- n.Pos = p.pos(name)
- return n
-}
-
-func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node {
- name := p.name(fun.Name)
- t := p.signature(fun.Recv, fun.Type)
- f := p.nod(fun, ODCLFUNC, nil, nil)
-
- if fun.Recv == nil {
- if name.Name == "init" {
- name = renameinit()
- if t.List.Len() > 0 || t.Rlist.Len() > 0 {
- yyerrorl(f.Pos, "func init must have no arguments and no return values")
- }
- }
-
- if localpkg.Name == "main" && name.Name == "main" {
- if t.List.Len() > 0 || t.Rlist.Len() > 0 {
- yyerrorl(f.Pos, "func main must have no arguments and no return values")
- }
- }
- } else {
- f.Func.Shortname = name
- name = nblank.Sym // filled in by typecheckfunc
- }
-
- f.Func.Nname = newfuncnamel(p.pos(fun.Name), name)
- f.Func.Nname.Name.Defn = f
- f.Func.Nname.Name.Param.Ntype = t
-
- if pragma, ok := fun.Pragma.(*Pragma); ok {
- f.Func.Pragma = pragma.Flag & FuncPragmas
- if pragma.Flag&Systemstack != 0 && pragma.Flag&Nosplit != 0 {
- yyerrorl(f.Pos, "go:nosplit and go:systemstack cannot be combined")
- }
- pragma.Flag &^= FuncPragmas
- p.checkUnused(pragma)
- }
-
- if fun.Recv == nil {
- declare(f.Func.Nname, PFUNC)
- }
-
- p.funcBody(f, fun.Body)
-
- if fun.Body != nil {
- if f.Func.Pragma&Noescape != 0 {
- yyerrorl(f.Pos, "can only use //go:noescape with external func implementations")
- }
- } else {
- if pure_go || strings.HasPrefix(f.funcname(), "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 f.funcname() == n.local {
- isLinknamed = true
- break
- }
- }
- if !isLinknamed {
- yyerrorl(f.Pos, "missing function body")
- }
- }
- }
-
- return f
-}
-
-func (p *noder) signature(recv *syntax.Field, typ *syntax.FuncType) *Node {
- n := p.nod(typ, OTFUNC, nil, nil)
- if recv != nil {
- n.Left = p.param(recv, false, false)
- }
- n.List.Set(p.params(typ.ParamList, true))
- n.Rlist.Set(p.params(typ.ResultList, false))
- return n
-}
-
-func (p *noder) params(params []*syntax.Field, dddOk bool) []*Node {
- nodes := make([]*Node, 0, len(params))
- for i, param := range params {
- p.setlineno(param)
- nodes = append(nodes, p.param(param, dddOk, i+1 == len(params)))
- }
- return nodes
-}
-
-func (p *noder) param(param *syntax.Field, dddOk, final bool) *Node {
- var name *types.Sym
- if param.Name != nil {
- name = p.name(param.Name)
- }
-
- typ := p.typeExpr(param.Type)
- n := p.nodSym(param, ODCLFIELD, typ, name)
-
- // rewrite ...T parameter
- if typ.Op == ODDD {
- if !dddOk {
- // We mark these as syntax errors to get automatic elimination
- // of multiple such errors per line (see yyerrorl in subr.go).
- yyerror("syntax error: cannot use ... in receiver or result parameter list")
- } else if !final {
- if param.Name == nil {
- yyerror("syntax error: cannot use ... with non-final parameter")
- } else {
- p.yyerrorpos(param.Name.Pos(), "syntax error: cannot use ... with non-final parameter %s", param.Name.Value)
- }
- }
- typ.Op = OTARRAY
- typ.Right = typ.Left
- typ.Left = nil
- n.SetIsDDD(true)
- if n.Left != nil {
- n.Left.SetIsDDD(true)
- }
- }
-
- return n
-}
-
-func (p *noder) exprList(expr syntax.Expr) []*Node {
- if list, ok := expr.(*syntax.ListExpr); ok {
- return p.exprs(list.ElemList)
- }
- return []*Node{p.expr(expr)}
-}
-
-func (p *noder) exprs(exprs []syntax.Expr) []*Node {
- nodes := make([]*Node, 0, len(exprs))
- for _, expr := range exprs {
- nodes = append(nodes, p.expr(expr))
- }
- return nodes
-}
-
-func (p *noder) expr(expr syntax.Expr) *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 := nodlit(p.basicLit(expr))
- n.SetDiag(expr.Bad) // avoid follow-on errors if there was a syntax error
- return n
- case *syntax.CompositeLit:
- n := p.nod(expr, OCOMPLIT, nil, nil)
- if expr.Type != nil {
- n.Right = p.expr(expr.Type)
- }
- l := p.exprs(expr.ElemList)
- for i, e := range l {
- l[i] = p.wrapname(expr.ElemList[i], e)
- }
- n.List.Set(l)
- lineno = p.makeXPos(expr.Rbrace)
- return n
- case *syntax.KeyValueExpr:
- // use position of expr.Key rather than of expr (which has position of ':')
- return p.nod(expr.Key, OKEY, p.expr(expr.Key), p.wrapname(expr.Value, p.expr(expr.Value)))
- case *syntax.FuncLit:
- return p.funcLit(expr)
- case *syntax.ParenExpr:
- return p.nod(expr, OPAREN, p.expr(expr.X), nil)
- case *syntax.SelectorExpr:
- // parser.new_dotname
- obj := p.expr(expr.X)
- if obj.Op == OPACK {
- obj.Name.SetUsed(true)
- return importName(obj.Name.Pkg.Lookup(expr.Sel.Value))
- }
- n := nodSym(OXDOT, obj, p.name(expr.Sel))
- n.Pos = p.pos(expr) // lineno may have been changed by p.expr(expr.X)
- return n
- case *syntax.IndexExpr:
- return p.nod(expr, OINDEX, p.expr(expr.X), p.expr(expr.Index))
- case *syntax.SliceExpr:
- op := OSLICE
- if expr.Full {
- op = OSLICE3
- }
- n := p.nod(expr, op, p.expr(expr.X), nil)
- var index [3]*Node
- for i, x := range &expr.Index {
- if x != nil {
- index[i] = p.expr(x)
- }
- }
- n.SetSliceBounds(index[0], index[1], index[2])
- return n
- case *syntax.AssertExpr:
- return p.nod(expr, ODOTTYPE, 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 {
- return p.nod(expr, p.unOp(expr.Op), x, nil)
- }
- return p.nod(expr, p.binOp(expr.Op), x, p.expr(expr.Y))
- case *syntax.CallExpr:
- n := p.nod(expr, OCALL, p.expr(expr.Fun), nil)
- n.List.Set(p.exprs(expr.ArgList))
- n.SetIsDDD(expr.HasDots)
- return n
-
- case *syntax.ArrayType:
- var len *Node
- if expr.Len != nil {
- len = p.expr(expr.Len)
- } else {
- len = p.nod(expr, ODDD, nil, nil)
- }
- return p.nod(expr, OTARRAY, len, p.typeExpr(expr.Elem))
- case *syntax.SliceType:
- return p.nod(expr, OTARRAY, nil, p.typeExpr(expr.Elem))
- case *syntax.DotsType:
- return p.nod(expr, ODDD, p.typeExpr(expr.Elem), nil)
- 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 p.nod(expr, OTMAP, p.typeExpr(expr.Key), p.typeExpr(expr.Value))
- case *syntax.ChanType:
- n := p.nod(expr, OTCHAN, p.typeExpr(expr.Elem), nil)
- n.SetTChanDir(p.chanDir(expr.Dir))
- return n
-
- case *syntax.TypeSwitchGuard:
- n := p.nod(expr, OTYPESW, nil, p.expr(expr.X))
- if expr.Lhs != nil {
- n.Left = p.declName(expr.Lhs)
- if n.Left.isBlank() {
- yyerror("invalid variable name %v in type switch", n.Left)
- }
- }
- return n
- }
- 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) *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 *Node
- chunks := make([]string, 0, 1)
-
- n := p.expr(x)
- if Isconst(n, CTSTR) && n.Sym == nil {
- nstr = n
- chunks = append(chunks, nstr.StringVal())
- }
-
- for i := len(adds) - 1; i >= 0; i-- {
- add := adds[i]
-
- r := p.expr(add.Y)
- if Isconst(r, CTSTR) && r.Sym == nil {
- if nstr != nil {
- // Collapse r into nstr instead of adding to n.
- chunks = append(chunks, r.StringVal())
- continue
- }
-
- nstr = r
- chunks = append(chunks, nstr.StringVal())
- } else {
- if len(chunks) > 1 {
- nstr.SetVal(Val{U: strings.Join(chunks, "")})
- }
- nstr = nil
- chunks = chunks[:0]
- }
- n = p.nod(add, OADD, n, r)
- }
- if len(chunks) > 1 {
- nstr.SetVal(Val{U: strings.Join(chunks, "")})
- }
-
- return n
-}
-
-func (p *noder) typeExpr(typ syntax.Expr) *Node {
- // TODO(mdempsky): Be stricter? typecheck should handle errors anyway.
- return p.expr(typ)
-}
-
-func (p *noder) typeExprOrNil(typ syntax.Expr) *Node {
- if typ != nil {
- return p.expr(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) *Node {
- l := make([]*Node, 0, len(expr.FieldList))
- for i, field := range expr.FieldList {
- p.setlineno(field)
- var n *Node
- if field.Name == nil {
- n = p.embedded(field.Type)
- } else {
- n = p.nodSym(field, ODCLFIELD, p.typeExpr(field.Type), p.name(field.Name))
- }
- if i < len(expr.TagList) && expr.TagList[i] != nil {
- n.SetVal(p.basicLit(expr.TagList[i]))
- }
- l = append(l, n)
- }
-
- p.setlineno(expr)
- n := p.nod(expr, OTSTRUCT, nil, nil)
- n.List.Set(l)
- return n
-}
-
-func (p *noder) interfaceType(expr *syntax.InterfaceType) *Node {
- l := make([]*Node, 0, len(expr.MethodList))
- for _, method := range expr.MethodList {
- p.setlineno(method)
- var n *Node
- if method.Name == nil {
- n = p.nodSym(method, ODCLFIELD, importName(p.packname(method.Type)), nil)
- } else {
- mname := p.name(method.Name)
- sig := p.typeExpr(method.Type)
- sig.Left = fakeRecv()
- n = p.nodSym(method, ODCLFIELD, sig, mname)
- ifacedcl(n)
- }
- l = append(l, n)
- }
-
- n := p.nod(expr, OTINTER, nil, nil)
- n.List.Set(l)
- return n
-}
-
-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.Pack != nil {
- n.Name.Pack.Name.SetUsed(true)
- }
- return name
- case *syntax.SelectorExpr:
- name := p.name(expr.X.(*syntax.Name))
- def := asNode(name.Def)
- if def == nil {
- yyerror("undefined: %v", name)
- return name
- }
- var pkg *types.Pkg
- if def.Op != OPACK {
- yyerror("%v is not a package", name)
- pkg = localpkg
- } else {
- def.Name.SetUsed(true)
- pkg = def.Name.Pkg
- }
- return pkg.Lookup(expr.Sel.Value)
- }
- panic(fmt.Sprintf("unexpected packname: %#v", expr))
-}
-
-func (p *noder) embedded(typ syntax.Expr) *Node {
- 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 := p.nodSym(typ, ODCLFIELD, importName(sym), lookup(sym.Name))
- n.SetEmbedded(true)
-
- if isStar {
- n.Left = p.nod(op, ODEREF, n.Left, nil)
- }
- return n
-}
-
-func (p *noder) stmts(stmts []syntax.Stmt) []*Node {
- return p.stmtsFall(stmts, false)
-}
-
-func (p *noder) stmtsFall(stmts []syntax.Stmt, fallOK bool) []*Node {
- var nodes []*Node
- for i, stmt := range stmts {
- s := p.stmtFall(stmt, fallOK && i+1 == len(stmts))
- if s == nil {
- } else if s.Op == OBLOCK && s.Ninit.Len() == 0 {
- nodes = append(nodes, s.List.Slice()...)
- } else {
- nodes = append(nodes, s)
- }
- }
- return nodes
-}
-
-func (p *noder) stmt(stmt syntax.Stmt) *Node {
- return p.stmtFall(stmt, false)
-}
-
-func (p *noder) stmtFall(stmt syntax.Stmt, fallOK bool) *Node {
- p.setlineno(stmt)
- switch stmt := stmt.(type) {
- case *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 nod(OEMPTY, nil, nil)
- }
- return liststmt(l)
- case *syntax.ExprStmt:
- return p.wrapname(stmt, p.expr(stmt.X))
- case *syntax.SendStmt:
- return p.nod(stmt, OSEND, p.expr(stmt.Chan), p.expr(stmt.Value))
- case *syntax.DeclStmt:
- return liststmt(p.decls(stmt.DeclList))
- case *syntax.AssignStmt:
- if stmt.Op != 0 && stmt.Op != syntax.Def {
- n := p.nod(stmt, OASOP, p.expr(stmt.Lhs), p.expr(stmt.Rhs))
- n.SetImplicit(stmt.Rhs == syntax.ImplicitOne)
- n.SetSubOp(p.binOp(stmt.Op))
- return n
- }
-
- n := p.nod(stmt, OAS, nil, nil) // assume common case
-
- rhs := p.exprList(stmt.Rhs)
- lhs := p.assignList(stmt.Lhs, n, stmt.Op == syntax.Def)
-
- if len(lhs) == 1 && len(rhs) == 1 {
- // common case
- n.Left = lhs[0]
- n.Right = rhs[0]
- } else {
- n.Op = OAS2
- n.List.Set(lhs)
- n.Rlist.Set(rhs)
- }
- return n
-
- case *syntax.BranchStmt:
- var op Op
- switch stmt.Tok {
- case syntax.Break:
- op = OBREAK
- case syntax.Continue:
- op = OCONTINUE
- case syntax.Fallthrough:
- if !fallOK {
- yyerror("fallthrough statement out of place")
- }
- op = OFALL
- case syntax.Goto:
- op = OGOTO
- default:
- panic("unhandled BranchStmt")
- }
- n := p.nod(stmt, op, nil, nil)
- if stmt.Label != nil {
- n.Sym = p.name(stmt.Label)
- }
- return n
- case *syntax.CallStmt:
- var op Op
- switch stmt.Tok {
- case syntax.Defer:
- op = ODEFER
- case syntax.Go:
- op = OGO
- default:
- panic("unhandled CallStmt")
- }
- return p.nod(stmt, op, p.expr(stmt.Call), nil)
- case *syntax.ReturnStmt:
- var results []*Node
- if stmt.Results != nil {
- results = p.exprList(stmt.Results)
- }
- n := p.nod(stmt, ORETURN, nil, nil)
- n.List.Set(results)
- if n.List.Len() == 0 && Curfn != nil {
- for _, ln := range Curfn.Func.Dcl {
- if ln.Class() == PPARAM {
- continue
- }
- if ln.Class() != PPARAMOUT {
- break
- }
- if asNode(ln.Sym.Def) != ln {
- yyerror("%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 *Node, colas bool) []*Node {
- if !colas {
- return p.exprList(expr)
- }
-
- defn.SetColas(true)
-
- var exprs []syntax.Expr
- if list, ok := expr.(*syntax.ListExpr); ok {
- exprs = list.ElemList
- } else {
- exprs = []syntax.Expr{expr}
- }
-
- res := make([]*Node, len(exprs))
- seen := make(map[*types.Sym]bool, len(exprs))
-
- newOrErr := false
- for i, expr := range exprs {
- p.setlineno(expr)
- res[i] = nblank
-
- name, ok := expr.(*syntax.Name)
- if !ok {
- p.yyerrorpos(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.yyerrorpos(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 := newname(sym)
- declare(n, dclcontext)
- n.Name.Defn = defn
- defn.Ninit.Append(nod(ODCL, n, nil))
- res[i] = n
- }
-
- if !newOrErr {
- yyerrorl(defn.Pos, "no new variables on left side of :=")
- }
- return res
-}
-
-func (p *noder) blockStmt(stmt *syntax.BlockStmt) []*Node {
- p.openScope(stmt.Pos())
- nodes := p.stmts(stmt.List)
- p.closeScope(stmt.Rbrace)
- return nodes
-}
-
-func (p *noder) ifStmt(stmt *syntax.IfStmt) *Node {
- p.openScope(stmt.Pos())
- n := p.nod(stmt, OIF, nil, nil)
- if stmt.Init != nil {
- n.Ninit.Set1(p.stmt(stmt.Init))
- }
- if stmt.Cond != nil {
- n.Left = p.expr(stmt.Cond)
- }
- n.Nbody.Set(p.blockStmt(stmt.Then))
- if stmt.Else != nil {
- e := p.stmt(stmt.Else)
- if e.Op == OBLOCK && e.Ninit.Len() == 0 {
- n.Rlist.Set(e.List.Slice())
- } else {
- n.Rlist.Set1(e)
- }
- }
- p.closeAnotherScope()
- return n
-}
-
-func (p *noder) forStmt(stmt *syntax.ForStmt) *Node {
- p.openScope(stmt.Pos())
- var n *Node
- if r, ok := stmt.Init.(*syntax.RangeClause); ok {
- if stmt.Cond != nil || stmt.Post != nil {
- panic("unexpected RangeClause")
- }
-
- n = p.nod(r, ORANGE, nil, p.expr(r.X))
- if r.Lhs != nil {
- n.List.Set(p.assignList(r.Lhs, n, r.Def))
- }
- } else {
- n = p.nod(stmt, OFOR, nil, nil)
- if stmt.Init != nil {
- n.Ninit.Set1(p.stmt(stmt.Init))
- }
- if stmt.Cond != nil {
- n.Left = p.expr(stmt.Cond)
- }
- if stmt.Post != nil {
- n.Right = p.stmt(stmt.Post)
- }
- }
- n.Nbody.Set(p.blockStmt(stmt.Body))
- p.closeAnotherScope()
- return n
-}
-
-func (p *noder) switchStmt(stmt *syntax.SwitchStmt) *Node {
- p.openScope(stmt.Pos())
- n := p.nod(stmt, OSWITCH, nil, nil)
- if stmt.Init != nil {
- n.Ninit.Set1(p.stmt(stmt.Init))
- }
- if stmt.Tag != nil {
- n.Left = p.expr(stmt.Tag)
- }
-
- tswitch := n.Left
- if tswitch != nil && tswitch.Op != OTYPESW {
- tswitch = nil
- }
- n.List.Set(p.caseClauses(stmt.Body, tswitch, stmt.Rbrace))
-
- p.closeScope(stmt.Rbrace)
- return n
-}
-
-func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *Node, rbrace syntax.Pos) []*Node {
- nodes := make([]*Node, 0, len(clauses))
- for i, clause := range clauses {
- p.setlineno(clause)
- if i > 0 {
- p.closeScope(clause.Pos())
- }
- p.openScope(clause.Pos())
-
- n := p.nod(clause, OCASE, nil, nil)
- if clause.Cases != nil {
- n.List.Set(p.exprList(clause.Cases))
- }
- if tswitch != nil && tswitch.Left != nil {
- nn := newname(tswitch.Left.Sym)
- declare(nn, dclcontext)
- n.Rlist.Set1(nn)
- // keep track of the instances for reporting unused
- nn.Name.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.Nbody.Set(p.stmtsFall(body, true))
- if l := n.Nbody.Len(); l > 0 && n.Nbody.Index(l-1).Op == OFALL {
- if tswitch != nil {
- yyerror("cannot fallthrough in type switch")
- }
- if i+1 == len(clauses) {
- yyerror("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) *Node {
- n := p.nod(stmt, OSELECT, nil, nil)
- n.List.Set(p.commClauses(stmt.Body, stmt.Rbrace))
- return n
-}
-
-func (p *noder) commClauses(clauses []*syntax.CommClause, rbrace syntax.Pos) []*Node {
- nodes := make([]*Node, 0, len(clauses))
- for i, clause := range clauses {
- p.setlineno(clause)
- if i > 0 {
- p.closeScope(clause.Pos())
- }
- p.openScope(clause.Pos())
-
- n := p.nod(clause, OCASE, nil, nil)
- if clause.Comm != nil {
- n.List.Set1(p.stmt(clause.Comm))
- }
- n.Nbody.Set(p.stmts(clause.Body))
- nodes = append(nodes, n)
- }
- if len(clauses) > 0 {
- p.closeScope(rbrace)
- }
- return nodes
-}
-
-func (p *noder) labeledStmt(label *syntax.LabeledStmt, fallOK bool) *Node {
- lhs := p.nodSym(label, OLABEL, nil, p.name(label.Label))
-
- var ls *Node
- if label.Stmt != nil { // TODO(mdempsky): Should always be present.
- ls = p.stmtFall(label.Stmt, fallOK)
- }
-
- lhs.Name.Defn = ls
- l := []*Node{lhs}
- if ls != nil {
- if ls.Op == OBLOCK && ls.Ninit.Len() == 0 {
- l = append(l, ls.List.Slice()...)
- } else {
- l = append(l, ls)
- }
- }
- return liststmt(l)
-}
-
-var unOps = [...]Op{
- syntax.Recv: ORECV,
- syntax.Mul: ODEREF,
- syntax.And: OADDR,
-
- syntax.Not: ONOT,
- syntax.Xor: OBITNOT,
- syntax.Add: OPLUS,
- syntax.Sub: ONEG,
-}
-
-func (p *noder) unOp(op syntax.Operator) Op {
- if uint64(op) >= uint64(len(unOps)) || unOps[op] == 0 {
- panic("invalid Operator")
- }
- return unOps[op]
-}
-
-var binOps = [...]Op{
- syntax.OrOr: OOROR,
- syntax.AndAnd: OANDAND,
-
- syntax.Eql: OEQ,
- syntax.Neq: ONE,
- syntax.Lss: OLT,
- syntax.Leq: OLE,
- syntax.Gtr: OGT,
- syntax.Geq: OGE,
-
- syntax.Add: OADD,
- syntax.Sub: OSUB,
- syntax.Or: OOR,
- syntax.Xor: OXOR,
-
- syntax.Mul: OMUL,
- syntax.Div: ODIV,
- syntax.Rem: OMOD,
- syntax.And: OAND,
- syntax.AndNot: OANDNOT,
- syntax.Shl: OLSH,
- syntax.Shr: ORSH,
-}
-
-func (p *noder) binOp(op syntax.Operator) 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 || langSupported(1, 13, localpkg) {
- return
- }
- // len(s) > 2
- if strings.Contains(s, "_") {
- yyerrorv("go1.13", "underscores in numeric literals")
- return
- }
- if s[0] != '0' {
- return
- }
- base := s[1]
- if base == 'b' || base == 'B' {
- yyerrorv("go1.13", "binary literals")
- return
- }
- if base == 'o' || base == 'O' {
- yyerrorv("go1.13", "0o/0O-style octal literals")
- return
- }
- if lit.Kind != syntax.IntLit && (base == 'x' || base == 'X') {
- yyerrorv("go1.13", "hexadecimal floating-point literals")
- }
-}
-
-func (p *noder) basicLit(lit *syntax.BasicLit) Val {
- // 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.
- switch s := lit.Value; lit.Kind {
- case syntax.IntLit:
- checkLangCompat(lit)
- x := new(Mpint)
- if !lit.Bad {
- x.SetString(s)
- }
- return Val{U: x}
-
- case syntax.FloatLit:
- checkLangCompat(lit)
- x := newMpflt()
- if !lit.Bad {
- x.SetString(s)
- }
- return Val{U: x}
-
- case syntax.ImagLit:
- checkLangCompat(lit)
- x := newMpcmplx()
- if !lit.Bad {
- x.Imag.SetString(strings.TrimSuffix(s, "i"))
- }
- return Val{U: x}
-
- case syntax.RuneLit:
- x := new(Mpint)
- x.Rune = true
- if !lit.Bad {
- u, _ := strconv.Unquote(s)
- var r rune
- if len(u) == 1 {
- r = rune(u[0])
- } else {
- r, _ = utf8.DecodeRuneInString(u)
- }
- x.SetInt64(int64(r))
- }
- return Val{U: x}
-
- case syntax.StringLit:
- var x string
- if !lit.Bad {
- if len(s) > 0 && s[0] == '`' {
- // strip carriage returns from raw string
- s = strings.Replace(s, "\r", "", -1)
- }
- x, _ = strconv.Unquote(s)
- }
- return Val{U: x}
-
- default:
- panic("unhandled BasicLit kind")
- }
-}
-
-func (p *noder) name(name *syntax.Name) *types.Sym {
- return lookup(name.Value)
-}
-
-func (p *noder) mkname(name *syntax.Name) *Node {
- // TODO(mdempsky): Set line number?
- return mkname(p.name(name))
-}
-
-func (p *noder) wrapname(n syntax.Node, x *Node) *Node {
- // These nodes do not carry line numbers.
- // Introduce a wrapper node to give them the correct line.
- switch x.Op {
- case OTYPE, OLITERAL:
- if x.Sym == nil {
- break
- }
- fallthrough
- case ONAME, ONONAME, OPACK:
- x = p.nod(n, OPAREN, x, nil)
- x.SetImplicit(true)
- }
- return x
-}
-
-func (p *noder) nod(orig syntax.Node, op Op, left, right *Node) *Node {
- return nodl(p.pos(orig), op, left, right)
-}
-
-func (p *noder) nodSym(orig syntax.Node, op Op, left *Node, sym *types.Sym) *Node {
- n := nodSym(op, left, sym)
- n.Pos = p.pos(orig)
- return n
-}
-
-func (p *noder) pos(n syntax.Node) src.XPos {
- // TODO(gri): orig.Pos() should always be known - fix package syntax
- xpos := lineno
- if pos := n.Pos(); pos.IsKnown() {
- xpos = p.makeXPos(pos)
- }
- return xpos
-}
-
-func (p *noder) setlineno(n syntax.Node) {
- if n != nil {
- lineno = 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,
-}
-
-// *Pragma is the value stored in a syntax.Pragma during parsing.
-type Pragma struct {
- Flag PragmaFlag // collected bits
- Pos []PragmaPos // position of each individual flag
- Embeds []PragmaEmbed
-}
-
-type PragmaPos struct {
- Flag PragmaFlag
- Pos syntax.Pos
-}
-
-type PragmaEmbed struct {
- Pos syntax.Pos
- Patterns []string
-}
-
-func (p *noder) checkUnused(pragma *Pragma) {
- for _, pos := range pragma.Pos {
- if pos.Flag&pragma.Flag != 0 {
- p.yyerrorpos(pos.Pos, "misplaced compiler directive")
- }
- }
- if len(pragma.Embeds) > 0 {
- for _, e := range pragma.Embeds {
- p.yyerrorpos(e.Pos, "misplaced go:embed directive")
- }
- }
-}
-
-func (p *noder) checkUnusedDuringParse(pragma *Pragma) {
- 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.(*Pragma)
- if pragma == nil {
- pragma = new(Pragma)
- }
-
- 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]
- }
- 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) && !compiling_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 = Systemstack | Nowritebarrier | Nowritebarrierrec | Yeswritebarrierrec
- if !compiling_runtime && flag&runtimePragmas != 0 {
- p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in runtime", verb)})
- }
- if flag == 0 && !allowedStdPragmas[verb] && compiling_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(filepath.Clean(fileh(pos.Base().Filename()))), "_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) *Node {
- n := oldname(sym)
- if n.Name != nil && n.Name.Pack != nil {
- n.Name.Pack.Name.SetUsed(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
-}
->>>>>>> BRANCH (dab3e5 runtime: switch runtime to libc for openbsd/amd64)
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
deleted file mode 100644
index cd42eff..0000000
--- a/src/cmd/compile/internal/gc/syntax.go
+++ /dev/null
@@ -1,1199 +0,0 @@
-<<<<<<< HEAD (d7e71c [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSe)
-=======
-// 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.
-
-// “Abstract” syntax representation.
-
-package gc
-
-import (
- "cmd/compile/internal/ssa"
- "cmd/compile/internal/types"
- "cmd/internal/obj"
- "cmd/internal/objabi"
- "cmd/internal/src"
- "sort"
-)
-
-// A Node is a single node in the syntax tree.
-// Actually the syntax tree is a syntax DAG, because there is only one
-// node with Op=ONAME for a given instance of a variable x.
-// The same is true for Op=OTYPE and Op=OLITERAL. See Node.mayBeShared.
-type Node struct {
- // Tree structure.
- // Generic recursive walks should follow these fields.
- Left *Node
- Right *Node
- Ninit Nodes
- Nbody Nodes
- List Nodes
- Rlist Nodes
-
- // most nodes
- Type *types.Type
- Orig *Node // original form, for printing, and tracking copies of ONAMEs
-
- // func
- Func *Func
-
- // ONAME, OTYPE, OPACK, OLABEL, some OLITERAL
- Name *Name
-
- Sym *types.Sym // various
- E interface{} // Opt or Val, see methods below
-
- // Various. Usually an offset into a struct. For example:
- // - ONAME nodes that refer to local variables use it to identify their stack frame position.
- // - ODOT, ODOTPTR, and ORESULT use it to indicate offset relative to their base address.
- // - OSTRUCTKEY uses it to store the named field's offset.
- // - Named OLITERALs use it to store their ambient iota value.
- // - OINLMARK stores an index into the inlTree data structure.
- // - OCLOSURE uses it to store ambient iota value, if any.
- // Possibly still more uses. If you find any, document them.
- Xoffset int64
-
- Pos src.XPos
-
- flags bitset32
-
- Esc uint16 // EscXXX
-
- Op Op
- aux uint8
-}
-
-func (n *Node) ResetAux() {
- n.aux = 0
-}
-
-func (n *Node) SubOp() Op {
- switch n.Op {
- case OASOP, ONAME:
- default:
- Fatalf("unexpected op: %v", n.Op)
- }
- return Op(n.aux)
-}
-
-func (n *Node) SetSubOp(op Op) {
- switch n.Op {
- case OASOP, ONAME:
- default:
- Fatalf("unexpected op: %v", n.Op)
- }
- n.aux = uint8(op)
-}
-
-func (n *Node) IndexMapLValue() bool {
- if n.Op != OINDEXMAP {
- Fatalf("unexpected op: %v", n.Op)
- }
- return n.aux != 0
-}
-
-func (n *Node) SetIndexMapLValue(b bool) {
- if n.Op != OINDEXMAP {
- Fatalf("unexpected op: %v", n.Op)
- }
- if b {
- n.aux = 1
- } else {
- n.aux = 0
- }
-}
-
-func (n *Node) TChanDir() types.ChanDir {
- if n.Op != OTCHAN {
- Fatalf("unexpected op: %v", n.Op)
- }
- return types.ChanDir(n.aux)
-}
-
-func (n *Node) SetTChanDir(dir types.ChanDir) {
- if n.Op != OTCHAN {
- Fatalf("unexpected op: %v", n.Op)
- }
- n.aux = uint8(dir)
-}
-
-func (n *Node) IsSynthetic() bool {
- name := n.Sym.Name
- return name[0] == '.' || name[0] == '~'
-}
-
-// IsAutoTmp indicates if n was created by the compiler as a temporary,
-// based on the setting of the .AutoTemp flag in n's Name.
-func (n *Node) IsAutoTmp() bool {
- if n == nil || n.Op != ONAME {
- return false
- }
- return n.Name.AutoTemp()
-}
-
-const (
- nodeClass, _ = iota, 1 << iota // PPARAM, PAUTO, PEXTERN, etc; three bits; first in the list because frequently accessed
- _, _ // second nodeClass bit
- _, _ // third nodeClass bit
- nodeWalkdef, _ // tracks state during typecheckdef; 2 == loop detected; two bits
- _, _ // second nodeWalkdef bit
- nodeTypecheck, _ // tracks state during typechecking; 2 == loop detected; two bits
- _, _ // second nodeTypecheck bit
- nodeInitorder, _ // tracks state during init1; two bits
- _, _ // second nodeInitorder bit
- _, nodeHasBreak
- _, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only
- _, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP
- _, nodeIsDDD // is the argument variadic
- _, nodeDiag // already printed error about this
- _, nodeColas // OAS resulting from :=
- _, nodeNonNil // guaranteed to be non-nil
- _, nodeTransient // storage can be reused immediately after this statement
- _, nodeBounded // bounds check unnecessary
- _, nodeHasCall // expression contains a function call
- _, nodeLikely // if statement condition likely
- _, nodeHasVal // node.E contains a Val
- _, nodeHasOpt // node.E contains an Opt
- _, nodeEmbedded // ODCLFIELD embedded type
-)
-
-func (n *Node) Class() Class { return Class(n.flags.get3(nodeClass)) }
-func (n *Node) Walkdef() uint8 { return n.flags.get2(nodeWalkdef) }
-func (n *Node) Typecheck() uint8 { return n.flags.get2(nodeTypecheck) }
-func (n *Node) Initorder() uint8 { return n.flags.get2(nodeInitorder) }
-
-func (n *Node) HasBreak() bool { return n.flags&nodeHasBreak != 0 }
-func (n *Node) NoInline() bool { return n.flags&nodeNoInline != 0 }
-func (n *Node) Implicit() bool { return n.flags&nodeImplicit != 0 }
-func (n *Node) IsDDD() bool { return n.flags&nodeIsDDD != 0 }
-func (n *Node) Diag() bool { return n.flags&nodeDiag != 0 }
-func (n *Node) Colas() bool { return n.flags&nodeColas != 0 }
-func (n *Node) NonNil() bool { return n.flags&nodeNonNil != 0 }
-func (n *Node) Transient() bool { return n.flags&nodeTransient != 0 }
-func (n *Node) Bounded() bool { return n.flags&nodeBounded != 0 }
-func (n *Node) HasCall() bool { return n.flags&nodeHasCall != 0 }
-func (n *Node) Likely() bool { return n.flags&nodeLikely != 0 }
-func (n *Node) HasVal() bool { return n.flags&nodeHasVal != 0 }
-func (n *Node) HasOpt() bool { return n.flags&nodeHasOpt != 0 }
-func (n *Node) Embedded() bool { return n.flags&nodeEmbedded != 0 }
-
-func (n *Node) SetClass(b Class) { n.flags.set3(nodeClass, uint8(b)) }
-func (n *Node) SetWalkdef(b uint8) { n.flags.set2(nodeWalkdef, b) }
-func (n *Node) SetTypecheck(b uint8) { n.flags.set2(nodeTypecheck, b) }
-func (n *Node) SetInitorder(b uint8) { n.flags.set2(nodeInitorder, b) }
-
-func (n *Node) SetHasBreak(b bool) { n.flags.set(nodeHasBreak, b) }
-func (n *Node) SetNoInline(b bool) { n.flags.set(nodeNoInline, b) }
-func (n *Node) SetImplicit(b bool) { n.flags.set(nodeImplicit, b) }
-func (n *Node) SetIsDDD(b bool) { n.flags.set(nodeIsDDD, b) }
-func (n *Node) SetDiag(b bool) { n.flags.set(nodeDiag, b) }
-func (n *Node) SetColas(b bool) { n.flags.set(nodeColas, b) }
-func (n *Node) SetTransient(b bool) { n.flags.set(nodeTransient, b) }
-func (n *Node) SetHasCall(b bool) { n.flags.set(nodeHasCall, b) }
-func (n *Node) SetLikely(b bool) { n.flags.set(nodeLikely, b) }
-func (n *Node) SetHasVal(b bool) { n.flags.set(nodeHasVal, b) }
-func (n *Node) SetHasOpt(b bool) { n.flags.set(nodeHasOpt, b) }
-func (n *Node) SetEmbedded(b bool) { n.flags.set(nodeEmbedded, b) }
-
-// MarkNonNil marks a pointer n as being guaranteed non-nil,
-// on all code paths, at all times.
-// During conversion to SSA, non-nil pointers won't have nil checks
-// inserted before dereferencing. See state.exprPtr.
-func (n *Node) MarkNonNil() {
- if !n.Type.IsPtr() && !n.Type.IsUnsafePtr() {
- Fatalf("MarkNonNil(%v), type %v", n, n.Type)
- }
- n.flags.set(nodeNonNil, true)
-}
-
-// SetBounded indicates whether operation n does not need safety checks.
-// When n is an index or slice operation, n does not need bounds checks.
-// When n is a dereferencing operation, n does not need nil checks.
-// When n is a makeslice+copy operation, n does not need length and cap checks.
-func (n *Node) SetBounded(b bool) {
- switch n.Op {
- case OINDEX, OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR, OSLICESTR:
- // No bounds checks needed.
- case ODOTPTR, ODEREF:
- // No nil check needed.
- case OMAKESLICECOPY:
- // No length and cap checks needed
- // since new slice and copied over slice data have same length.
- default:
- Fatalf("SetBounded(%v)", n)
- }
- n.flags.set(nodeBounded, b)
-}
-
-// MarkReadonly indicates that n is an ONAME with readonly contents.
-func (n *Node) MarkReadonly() {
- if n.Op != ONAME {
- Fatalf("Node.MarkReadonly %v", n.Op)
- }
- n.Name.SetReadonly(true)
- // Mark the linksym as readonly immediately
- // so that the SSA backend can use this information.
- // It will be overridden later during dumpglobls.
- n.Sym.Linksym().Type = objabi.SRODATA
-}
-
-// Val returns the Val for the node.
-func (n *Node) Val() Val {
- if !n.HasVal() {
- return Val{}
- }
- return Val{n.E}
-}
-
-// SetVal sets the Val for the node, which must not have been used with SetOpt.
-func (n *Node) SetVal(v Val) {
- if n.HasOpt() {
- Debug.h = 1
- Dump("have Opt", n)
- Fatalf("have Opt")
- }
- n.SetHasVal(true)
- n.E = v.U
-}
-
-// Opt returns the optimizer data for the node.
-func (n *Node) Opt() interface{} {
- if !n.HasOpt() {
- return nil
- }
- return n.E
-}
-
-// SetOpt sets the optimizer data for the node, which must not have been used with SetVal.
-// SetOpt(nil) is ignored for Vals to simplify call sites that are clearing Opts.
-func (n *Node) SetOpt(x interface{}) {
- if x == nil && n.HasVal() {
- return
- }
- if n.HasVal() {
- Debug.h = 1
- Dump("have Val", n)
- Fatalf("have Val")
- }
- n.SetHasOpt(true)
- n.E = x
-}
-
-func (n *Node) Iota() int64 {
- return n.Xoffset
-}
-
-func (n *Node) SetIota(x int64) {
- n.Xoffset = x
-}
-
-// mayBeShared reports whether n may occur in multiple places in the AST.
-// Extra care must be taken when mutating such a node.
-func (n *Node) mayBeShared() bool {
- switch n.Op {
- case ONAME, OLITERAL, OTYPE:
- return true
- }
- return false
-}
-
-// isMethodExpression reports whether n represents a method expression T.M.
-func (n *Node) isMethodExpression() bool {
- return n.Op == ONAME && n.Left != nil && n.Left.Op == OTYPE && n.Right != nil && n.Right.Op == ONAME
-}
-
-// funcname returns the name (without the package) of the function n.
-func (n *Node) funcname() string {
- if n == nil || n.Func == nil || n.Func.Nname == nil {
- return "<nil>"
- }
- return n.Func.Nname.Sym.Name
-}
-
-// pkgFuncName returns the name of the function referenced by n, with package prepended.
-// This differs from the compiler's internal convention where local functions lack a package
-// because the ultimate consumer of this is a human looking at an IDE; package is only empty
-// if the compilation package is actually the empty string.
-func (n *Node) pkgFuncName() string {
- var s *types.Sym
- if n == nil {
- return "<nil>"
- }
- if n.Op == ONAME {
- s = n.Sym
- } else {
- if n.Func == nil || n.Func.Nname == nil {
- return "<nil>"
- }
- s = n.Func.Nname.Sym
- }
- pkg := s.Pkg
-
- p := myimportpath
- if pkg != nil && pkg.Path != "" {
- p = pkg.Path
- }
- if p == "" {
- return s.Name
- }
- return p + "." + s.Name
-}
-
-// The compiler needs *Node to be assignable to cmd/compile/internal/ssa.Sym.
-func (n *Node) CanBeAnSSASym() {
-}
-
-// Name holds Node fields used only by named nodes (ONAME, OTYPE, OPACK, OLABEL, some OLITERAL).
-type Name struct {
- Pack *Node // real package for import . names
- Pkg *types.Pkg // pkg for OPACK nodes
- // For a local variable (not param) or extern, the initializing assignment (OAS or OAS2).
- // For a closure var, the ONAME node of the outer captured variable
- Defn *Node
- // The ODCLFUNC node (for a static function/method or a closure) in which
- // local variable or param is declared.
- Curfn *Node
- Param *Param // additional fields for ONAME, OTYPE
- Decldepth int32 // declaration loop depth, increased for every loop or label
- // Unique number for ONAME nodes within a function. Function outputs
- // (results) are numbered starting at one, followed by function inputs
- // (parameters), and then local variables. Vargen is used to distinguish
- // local variables/params with the same name.
- Vargen int32
- flags bitset16
-}
-
-const (
- nameCaptured = 1 << iota // is the variable captured by a closure
- nameReadonly
- nameByval // is the variable captured by value or by reference
- nameNeedzero // if it contains pointers, needs to be zeroed on function entry
- nameAutoTemp // is the variable a temporary (implies no dwarf info. reset if escapes to heap)
- nameUsed // for variable declared and not used error
- nameIsClosureVar // PAUTOHEAP closure pseudo-variable; original at n.Name.Defn
- nameIsOutputParamHeapAddr // pointer to a result parameter's heap copy
- nameAssigned // is the variable ever assigned to
- nameAddrtaken // address taken, even if not moved to heap
- nameInlFormal // PAUTO created by inliner, derived from callee formal
- nameInlLocal // PAUTO created by inliner, derived from callee local
- nameOpenDeferSlot // if temporary var storing info for open-coded defers
- nameLibfuzzerExtraCounter // if PEXTERN should be assigned to __libfuzzer_extra_counters section
-)
-
-func (n *Name) Captured() bool { return n.flags&nameCaptured != 0 }
-func (n *Name) Readonly() bool { return n.flags&nameReadonly != 0 }
-func (n *Name) Byval() bool { return n.flags&nameByval != 0 }
-func (n *Name) Needzero() bool { return n.flags&nameNeedzero != 0 }
-func (n *Name) AutoTemp() bool { return n.flags&nameAutoTemp != 0 }
-func (n *Name) Used() bool { return n.flags&nameUsed != 0 }
-func (n *Name) IsClosureVar() bool { return n.flags&nameIsClosureVar != 0 }
-func (n *Name) IsOutputParamHeapAddr() bool { return n.flags&nameIsOutputParamHeapAddr != 0 }
-func (n *Name) Assigned() bool { return n.flags&nameAssigned != 0 }
-func (n *Name) Addrtaken() bool { return n.flags&nameAddrtaken != 0 }
-func (n *Name) InlFormal() bool { return n.flags&nameInlFormal != 0 }
-func (n *Name) InlLocal() bool { return n.flags&nameInlLocal != 0 }
-func (n *Name) OpenDeferSlot() bool { return n.flags&nameOpenDeferSlot != 0 }
-func (n *Name) LibfuzzerExtraCounter() bool { return n.flags&nameLibfuzzerExtraCounter != 0 }
-
-func (n *Name) SetCaptured(b bool) { n.flags.set(nameCaptured, b) }
-func (n *Name) SetReadonly(b bool) { n.flags.set(nameReadonly, b) }
-func (n *Name) SetByval(b bool) { n.flags.set(nameByval, b) }
-func (n *Name) SetNeedzero(b bool) { n.flags.set(nameNeedzero, b) }
-func (n *Name) SetAutoTemp(b bool) { n.flags.set(nameAutoTemp, b) }
-func (n *Name) SetUsed(b bool) { n.flags.set(nameUsed, b) }
-func (n *Name) SetIsClosureVar(b bool) { n.flags.set(nameIsClosureVar, b) }
-func (n *Name) SetIsOutputParamHeapAddr(b bool) { n.flags.set(nameIsOutputParamHeapAddr, b) }
-func (n *Name) SetAssigned(b bool) { n.flags.set(nameAssigned, b) }
-func (n *Name) SetAddrtaken(b bool) { n.flags.set(nameAddrtaken, b) }
-func (n *Name) SetInlFormal(b bool) { n.flags.set(nameInlFormal, b) }
-func (n *Name) SetInlLocal(b bool) { n.flags.set(nameInlLocal, b) }
-func (n *Name) SetOpenDeferSlot(b bool) { n.flags.set(nameOpenDeferSlot, b) }
-func (n *Name) SetLibfuzzerExtraCounter(b bool) { n.flags.set(nameLibfuzzerExtraCounter, b) }
-
-type Param struct {
- Ntype *Node
- Heapaddr *Node // temp holding heap address of param
-
- // ONAME PAUTOHEAP
- Stackcopy *Node // the PPARAM/PPARAMOUT on-stack slot (moved func params only)
-
- // ONAME closure linkage
- // Consider:
- //
- // func f() {
- // x := 1 // x1
- // func() {
- // use(x) // x2
- // func() {
- // use(x) // x3
- // --- parser is here ---
- // }()
- // }()
- // }
- //
- // There is an original declaration of x and then a chain of mentions of x
- // leading into the current function. Each time x is mentioned in a new closure,
- // we create a variable representing x for use in that specific closure,
- // since the way you get to x is different in each closure.
- //
- // Let's number the specific variables as shown in the code:
- // x1 is the original x, x2 is when mentioned in the closure,
- // and x3 is when mentioned in the closure in the closure.
- //
- // We keep these linked (assume N > 1):
- //
- // - x1.Defn = original declaration statement for x (like most variables)
- // - x1.Innermost = current innermost closure x (in this case x3), or nil for none
- // - x1.IsClosureVar() = false
- //
- // - xN.Defn = x1, N > 1
- // - xN.IsClosureVar() = true, N > 1
- // - x2.Outer = nil
- // - xN.Outer = x(N-1), N > 2
- //
- //
- // When we look up x in the symbol table, we always get x1.
- // Then we can use x1.Innermost (if not nil) to get the x
- // for the innermost known closure function,
- // but the first reference in a closure will find either no x1.Innermost
- // or an x1.Innermost with .Funcdepth < Funcdepth.
- // In that case, a new xN must be created, linked in with:
- //
- // xN.Defn = x1
- // xN.Outer = x1.Innermost
- // x1.Innermost = xN
- //
- // When we finish the function, we'll process its closure variables
- // and find xN and pop it off the list using:
- //
- // x1 := xN.Defn
- // x1.Innermost = xN.Outer
- //
- // We leave x1.Innermost set so that we can still get to the original
- // variable quickly. Not shown here, but once we're
- // done parsing a function and no longer need xN.Outer for the
- // lexical x reference links as described above, funcLit
- // recomputes xN.Outer as the semantic x reference link tree,
- // even filling in x in intermediate closures that might not
- // have mentioned it along the way to inner closures that did.
- // See funcLit for details.
- //
- // During the eventual compilation, then, for closure variables we have:
- //
- // xN.Defn = original variable
- // xN.Outer = variable captured in next outward scope
- // to make closure where xN appears
- //
- // Because of the sharding of pieces of the node, x.Defn means x.Name.Defn
- // and x.Innermost/Outer means x.Name.Param.Innermost/Outer.
- Innermost *Node
- Outer *Node
-
- // OTYPE & ONAME //go:embed info,
- // sharing storage to reduce gc.Param size.
- // Extra is nil, or else *Extra is a *paramType or an *embedFileList.
- Extra *interface{}
-}
-
-type paramType struct {
- flag PragmaFlag
- alias bool
-}
-
-type irEmbed struct {
- Pos src.XPos
- Patterns []string
-}
-
-type embedList []irEmbed
-
-// Pragma returns the PragmaFlag for p, which must be for an OTYPE.
-func (p *Param) Pragma() PragmaFlag {
- if p.Extra == nil {
- return 0
- }
- return (*p.Extra).(*paramType).flag
-}
-
-// SetPragma sets the PragmaFlag for p, which must be for an OTYPE.
-func (p *Param) SetPragma(flag PragmaFlag) {
- if p.Extra == nil {
- if flag == 0 {
- return
- }
- p.Extra = new(interface{})
- *p.Extra = ¶mType{flag: flag}
- return
- }
- (*p.Extra).(*paramType).flag = flag
-}
-
-// Alias reports whether p, which must be for an OTYPE, is a type alias.
-func (p *Param) Alias() bool {
- if p.Extra == nil {
- return false
- }
- t, ok := (*p.Extra).(*paramType)
- if !ok {
- return false
- }
- return t.alias
-}
-
-// SetAlias sets whether p, which must be for an OTYPE, is a type alias.
-func (p *Param) SetAlias(alias bool) {
- if p.Extra == nil {
- if !alias {
- return
- }
- p.Extra = new(interface{})
- *p.Extra = ¶mType{alias: alias}
- return
- }
- (*p.Extra).(*paramType).alias = alias
-}
-
-// EmbedList returns the list of embedded files for p,
-// which must be for an ONAME var.
-func (p *Param) EmbedList() []irEmbed {
- if p.Extra == nil {
- return nil
- }
- return *(*p.Extra).(*embedList)
-}
-
-// SetEmbedList sets the list of embedded files for p,
-// which must be for an ONAME var.
-func (p *Param) SetEmbedList(list []irEmbed) {
- if p.Extra == nil {
- if len(list) == 0 {
- return
- }
- f := embedList(list)
- p.Extra = new(interface{})
- *p.Extra = &f
- return
- }
- *(*p.Extra).(*embedList) = list
-}
-
-// Functions
-//
-// A simple function declaration is represented as an ODCLFUNC node f
-// and an ONAME node n. They're linked to one another through
-// f.Func.Nname == n and n.Name.Defn == f. When functions are
-// referenced by name in an expression, the function's ONAME node is
-// used directly.
-//
-// Function names have n.Class() == PFUNC. This distinguishes them
-// from variables of function type.
-//
-// Confusingly, n.Func and f.Func both exist, but commonly point to
-// different Funcs. (Exception: an OCALLPART's Func does point to its
-// ODCLFUNC's Func.)
-//
-// A method declaration is represented like functions, except n.Sym
-// will be the qualified method name (e.g., "T.m") and
-// f.Func.Shortname is the bare method name (e.g., "m").
-//
-// Method expressions are represented as ONAME/PFUNC nodes like
-// function names, but their Left and Right fields still point to the
-// type and method, respectively. They can be distinguished from
-// normal functions with isMethodExpression. Also, unlike function
-// name nodes, method expression nodes exist for each method
-// expression. The declaration ONAME can be accessed with
-// x.Type.Nname(), where x is the method expression ONAME node.
-//
-// Method values are represented by ODOTMETH/ODOTINTER when called
-// immediately, and OCALLPART otherwise. They are like method
-// expressions, except that for ODOTMETH/ODOTINTER the method name is
-// stored in Sym instead of Right.
-//
-// Closures are represented by OCLOSURE node c. They link back and
-// forth with the ODCLFUNC via Func.Closure; that is, c.Func.Closure
-// == f and f.Func.Closure == c.
-//
-// Function bodies are stored in f.Nbody, and inline function bodies
-// are stored in n.Func.Inl. Pragmas are stored in f.Func.Pragma.
-//
-// Imported functions skip the ODCLFUNC, so n.Name.Defn is nil. They
-// also use Dcl instead of Inldcl.
-
-// Func holds Node fields used only with function-like nodes.
-type Func struct {
- Shortname *types.Sym
- // Extra entry code for the function. For example, allocate and initialize
- // memory for escaping parameters. However, just for OCLOSURE, Enter is a
- // list of ONAME nodes of captured variables
- Enter Nodes
- Exit Nodes
- // ONAME nodes for closure params, each should have closurevar set
- Cvars Nodes
- // ONAME nodes for all params/locals for this func/closure, does NOT
- // include closurevars until transformclosure runs.
- Dcl []*Node
-
- // Parents records the parent scope of each scope within a
- // function. The root scope (0) has no parent, so the i'th
- // scope's parent is stored at Parents[i-1].
- Parents []ScopeID
-
- // Marks records scope boundary changes.
- Marks []Mark
-
- // Closgen tracks how many closures have been generated within
- // this function. Used by closurename for creating unique
- // function names.
- Closgen int
-
- FieldTrack map[*types.Sym]struct{}
- DebugInfo *ssa.FuncDebug
- Ntype *Node // signature
- Top int // top context (ctxCallee, etc)
- Closure *Node // OCLOSURE <-> ODCLFUNC (see header comment above)
- Nname *Node // The ONAME node associated with an ODCLFUNC (both have same Type)
- lsym *obj.LSym
-
- Inl *Inline
-
- Label int32 // largest auto-generated label in this function
-
- Endlineno src.XPos
- WBPos src.XPos // position of first write barrier; see SetWBPos
-
- Pragma PragmaFlag // go:xxx function annotations
-
- flags bitset16
- numDefers int // number of defer calls in the function
- numReturns int // number of explicit returns in the function
-
- // nwbrCalls records the LSyms of functions called by this
- // function for go:nowritebarrierrec analysis. Only filled in
- // if nowritebarrierrecCheck != nil.
- nwbrCalls *[]nowritebarrierrecCallSym
-}
-
-// An Inline holds fields used for function bodies that can be inlined.
-type Inline struct {
- Cost int32 // heuristic cost of inlining this function
-
- // Copies of Func.Dcl and Nbody for use during inlining.
- Dcl []*Node
- Body []*Node
-}
-
-// A Mark represents a scope boundary.
-type Mark struct {
- // Pos is the position of the token that marks the scope
- // change.
- Pos src.XPos
-
- // Scope identifies the innermost scope to the right of Pos.
- Scope ScopeID
-}
-
-// A ScopeID represents a lexical scope within a function.
-type ScopeID int32
-
-const (
- funcDupok = 1 << iota // duplicate definitions ok
- funcWrapper // is method wrapper
- funcNeedctxt // function uses context register (has closure variables)
- funcReflectMethod // function calls reflect.Type.Method or MethodByName
- // true if closure inside a function; false if a simple function or a
- // closure in a global variable initialization
- funcIsHiddenClosure
- funcHasDefer // contains a defer statement
- funcNilCheckDisabled // disable nil checks when compiling this function
- funcInlinabilityChecked // inliner has already determined whether the function is inlinable
- funcExportInline // include inline body in export data
- funcInstrumentBody // add race/msan instrumentation during SSA construction
- funcOpenCodedDeferDisallowed // can't do open-coded defers
-)
-
-func (f *Func) Dupok() bool { return f.flags&funcDupok != 0 }
-func (f *Func) Wrapper() bool { return f.flags&funcWrapper != 0 }
-func (f *Func) Needctxt() bool { return f.flags&funcNeedctxt != 0 }
-func (f *Func) ReflectMethod() bool { return f.flags&funcReflectMethod != 0 }
-func (f *Func) IsHiddenClosure() bool { return f.flags&funcIsHiddenClosure != 0 }
-func (f *Func) HasDefer() bool { return f.flags&funcHasDefer != 0 }
-func (f *Func) NilCheckDisabled() bool { return f.flags&funcNilCheckDisabled != 0 }
-func (f *Func) InlinabilityChecked() bool { return f.flags&funcInlinabilityChecked != 0 }
-func (f *Func) ExportInline() bool { return f.flags&funcExportInline != 0 }
-func (f *Func) InstrumentBody() bool { return f.flags&funcInstrumentBody != 0 }
-func (f *Func) OpenCodedDeferDisallowed() bool { return f.flags&funcOpenCodedDeferDisallowed != 0 }
-
-func (f *Func) SetDupok(b bool) { f.flags.set(funcDupok, b) }
-func (f *Func) SetWrapper(b bool) { f.flags.set(funcWrapper, b) }
-func (f *Func) SetNeedctxt(b bool) { f.flags.set(funcNeedctxt, b) }
-func (f *Func) SetReflectMethod(b bool) { f.flags.set(funcReflectMethod, b) }
-func (f *Func) SetIsHiddenClosure(b bool) { f.flags.set(funcIsHiddenClosure, b) }
-func (f *Func) SetHasDefer(b bool) { f.flags.set(funcHasDefer, b) }
-func (f *Func) SetNilCheckDisabled(b bool) { f.flags.set(funcNilCheckDisabled, b) }
-func (f *Func) SetInlinabilityChecked(b bool) { f.flags.set(funcInlinabilityChecked, b) }
-func (f *Func) SetExportInline(b bool) { f.flags.set(funcExportInline, b) }
-func (f *Func) SetInstrumentBody(b bool) { f.flags.set(funcInstrumentBody, b) }
-func (f *Func) SetOpenCodedDeferDisallowed(b bool) { f.flags.set(funcOpenCodedDeferDisallowed, b) }
-
-func (f *Func) setWBPos(pos src.XPos) {
- if Debug_wb != 0 {
- Warnl(pos, "write barrier")
- }
- if !f.WBPos.IsKnown() {
- f.WBPos = pos
- }
-}
-
-//go:generate stringer -type=Op -trimprefix=O
-
-type Op uint8
-
-// Node ops.
-const (
- OXXX Op = iota
-
- // names
- ONAME // var or func name
- // Unnamed arg or return value: f(int, string) (int, error) { etc }
- // Also used for a qualified package identifier that hasn't been resolved yet.
- ONONAME
- OTYPE // type name
- OPACK // import
- OLITERAL // literal
-
- // expressions
- OADD // Left + Right
- OSUB // Left - Right
- OOR // Left | Right
- OXOR // Left ^ Right
- OADDSTR // +{List} (string addition, list elements are strings)
- OADDR // &Left
- OANDAND // Left && Right
- OAPPEND // append(List); after walk, Left may contain elem type descriptor
- OBYTES2STR // Type(Left) (Type is string, Left is a []byte)
- OBYTES2STRTMP // Type(Left) (Type is string, Left is a []byte, ephemeral)
- ORUNES2STR // Type(Left) (Type is string, Left is a []rune)
- OSTR2BYTES // Type(Left) (Type is []byte, Left is a string)
- OSTR2BYTESTMP // Type(Left) (Type is []byte, Left is a string, ephemeral)
- OSTR2RUNES // Type(Left) (Type is []rune, Left is a string)
- // Left = Right or (if Colas=true) Left := Right
- // If Colas, then Ninit includes a DCL node for Left.
- OAS
- // List = Rlist (x, y, z = a, b, c) or (if Colas=true) List := Rlist
- // If Colas, then Ninit includes DCL nodes for List
- OAS2
- OAS2DOTTYPE // List = Right (x, ok = I.(int))
- OAS2FUNC // List = Right (x, y = f())
- OAS2MAPR // List = Right (x, ok = m["foo"])
- OAS2RECV // List = Right (x, ok = <-c)
- OASOP // Left Etype= Right (x += y)
- OCALL // Left(List) (function call, method call or type conversion)
-
- // OCALLFUNC, OCALLMETH, and OCALLINTER have the same structure.
- // Prior to walk, they are: Left(List), where List is all regular arguments.
- // After walk, List is a series of assignments to temporaries,
- // and Rlist is an updated set of arguments.
- // Nbody is all OVARLIVE nodes that are attached to OCALLxxx.
- // TODO(josharian/khr): Use Ninit instead of List for the assignments to temporaries. See CL 114797.
- OCALLFUNC // Left(List/Rlist) (function call f(args))
- OCALLMETH // Left(List/Rlist) (direct method call x.Method(args))
- OCALLINTER // Left(List/Rlist) (interface method call x.Method(args))
- OCALLPART // Left.Right (method expression x.Method, not called)
- OCAP // cap(Left)
- OCLOSE // close(Left)
- OCLOSURE // func Type { Func.Closure.Nbody } (func literal)
- OCOMPLIT // Right{List} (composite literal, not yet lowered to specific form)
- OMAPLIT // Type{List} (composite literal, Type is map)
- OSTRUCTLIT // Type{List} (composite literal, Type is struct)
- OARRAYLIT // Type{List} (composite literal, Type is array)
- OSLICELIT // Type{List} (composite literal, Type is slice) Right.Int64() = slice length.
- OPTRLIT // &Left (left is composite literal)
- OCONV // Type(Left) (type conversion)
- OCONVIFACE // Type(Left) (type conversion, to interface)
- OCONVNOP // Type(Left) (type conversion, no effect)
- OCOPY // copy(Left, Right)
- ODCL // var Left (declares Left of type Left.Type)
-
- // Used during parsing but don't last.
- ODCLFUNC // func f() or func (r) f()
- ODCLFIELD // struct field, interface field, or func/method argument/return value.
- ODCLCONST // const pi = 3.14
- ODCLTYPE // type Int int or type Int = int
-
- ODELETE // delete(List)
- ODOT // Left.Sym (Left is of struct type)
- ODOTPTR // Left.Sym (Left is of pointer to struct type)
- ODOTMETH // Left.Sym (Left is non-interface, Right is method name)
- ODOTINTER // Left.Sym (Left is interface, Right is method name)
- OXDOT // Left.Sym (before rewrite to one of the preceding)
- ODOTTYPE // Left.Right or Left.Type (.Right during parsing, .Type once resolved); after walk, .Right contains address of interface type descriptor and .Right.Right contains address of concrete type descriptor
- ODOTTYPE2 // Left.Right or Left.Type (.Right during parsing, .Type once resolved; on rhs of OAS2DOTTYPE); after walk, .Right contains address of interface type descriptor
- OEQ // Left == Right
- ONE // Left != Right
- OLT // Left < Right
- OLE // Left <= Right
- OGE // Left >= Right
- OGT // Left > Right
- ODEREF // *Left
- OINDEX // Left[Right] (index of array or slice)
- OINDEXMAP // Left[Right] (index of map)
- OKEY // Left:Right (key:value in struct/array/map literal)
- OSTRUCTKEY // Sym:Left (key:value in struct literal, after type checking)
- OLEN // len(Left)
- OMAKE // make(List) (before type checking converts to one of the following)
- OMAKECHAN // make(Type, Left) (type is chan)
- OMAKEMAP // make(Type, Left) (type is map)
- OMAKESLICE // make(Type, Left, Right) (type is slice)
- OMAKESLICECOPY // makeslicecopy(Type, Left, Right) (type is slice; Left is length and Right is the copied from slice)
- // OMAKESLICECOPY is created by the order pass and corresponds to:
- // s = make(Type, Left); copy(s, Right)
- //
- // Bounded can be set on the node when Left == len(Right) is known at compile time.
- //
- // This node is created so the walk pass can optimize this pattern which would
- // otherwise be hard to detect after the order pass.
- OMUL // Left * Right
- ODIV // Left / Right
- OMOD // Left % Right
- OLSH // Left << Right
- ORSH // Left >> Right
- OAND // Left & Right
- OANDNOT // Left &^ Right
- ONEW // new(Left); corresponds to calls to new in source code
- ONEWOBJ // runtime.newobject(n.Type); introduced by walk; Left is type descriptor
- ONOT // !Left
- OBITNOT // ^Left
- OPLUS // +Left
- ONEG // -Left
- OOROR // Left || Right
- OPANIC // panic(Left)
- OPRINT // print(List)
- OPRINTN // println(List)
- OPAREN // (Left)
- OSEND // Left <- Right
- OSLICE // Left[List[0] : List[1]] (Left is untypechecked or slice)
- OSLICEARR // Left[List[0] : List[1]] (Left is array)
- OSLICESTR // Left[List[0] : List[1]] (Left is string)
- OSLICE3 // Left[List[0] : List[1] : List[2]] (Left is untypedchecked or slice)
- OSLICE3ARR // Left[List[0] : List[1] : List[2]] (Left is array)
- OSLICEHEADER // sliceheader{Left, List[0], List[1]} (Left is unsafe.Pointer, List[0] is length, List[1] is capacity)
- ORECOVER // recover()
- ORECV // <-Left
- ORUNESTR // Type(Left) (Type is string, Left is rune)
- OSELRECV // Left = <-Right.Left: (appears as .Left of OCASE; Right.Op == ORECV)
- OSELRECV2 // List = <-Right.Left: (appears as .Left of OCASE; count(List) == 2, Right.Op == ORECV)
- OIOTA // iota
- OREAL // real(Left)
- OIMAG // imag(Left)
- OCOMPLEX // complex(Left, Right) or complex(List[0]) where List[0] is a 2-result function call
- OALIGNOF // unsafe.Alignof(Left)
- OOFFSETOF // unsafe.Offsetof(Left)
- OSIZEOF // unsafe.Sizeof(Left)
-
- // statements
- OBLOCK // { List } (block of code)
- OBREAK // break [Sym]
- // OCASE: case List: Nbody (List==nil means default)
- // For OTYPESW, List is a OTYPE node for the specified type (or OLITERAL
- // for nil), and, if a type-switch variable is specified, Rlist is an
- // ONAME for the version of the type-switch variable with the specified
- // type.
- OCASE
- OCONTINUE // continue [Sym]
- ODEFER // defer Left (Left must be call)
- OEMPTY // no-op (empty statement)
- OFALL // fallthrough
- OFOR // for Ninit; Left; Right { Nbody }
- // OFORUNTIL is like OFOR, but the test (Left) is applied after the body:
- // Ninit
- // top: { Nbody } // Execute the body at least once
- // cont: Right
- // if Left { // And then test the loop condition
- // List // Before looping to top, execute List
- // goto top
- // }
- // OFORUNTIL is created by walk. There's no way to write this in Go code.
- OFORUNTIL
- OGOTO // goto Sym
- OIF // if Ninit; Left { Nbody } else { Rlist }
- OLABEL // Sym:
- OGO // go Left (Left must be call)
- ORANGE // for List = range Right { Nbody }
- ORETURN // return List
- OSELECT // select { List } (List is list of OCASE)
- OSWITCH // switch Ninit; Left { List } (List is a list of OCASE)
- // OTYPESW: Left := Right.(type) (appears as .Left of OSWITCH)
- // Left is nil if there is no type-switch variable
- OTYPESW
-
- // types
- OTCHAN // chan int
- OTMAP // map[string]int
- OTSTRUCT // struct{}
- OTINTER // interface{}
- // OTFUNC: func() - Left is receiver field, List is list of param fields, Rlist is
- // list of result fields.
- OTFUNC
- OTARRAY // []int, [8]int, [N]int or [...]int
-
- // misc
- ODDD // func f(args ...int) or f(l...) or var a = [...]int{0, 1, 2}.
- OINLCALL // intermediary representation of an inlined call.
- OEFACE // itable and data words of an empty-interface value.
- OITAB // itable word of an interface value.
- OIDATA // data word of an interface value in Left
- OSPTR // base pointer of a slice or string.
- OCLOSUREVAR // variable reference at beginning of closure function
- OCFUNC // reference to c function pointer (not go func value)
- OCHECKNIL // emit code to ensure pointer/interface not nil
- OVARDEF // variable is about to be fully initialized
- OVARKILL // variable is dead
- OVARLIVE // variable is alive
- ORESULT // result of a function call; Xoffset is stack offset
- OINLMARK // start of an inlined body, with file/line of caller. Xoffset is an index into the inline tree.
-
- // arch-specific opcodes
- ORETJMP // return to other function
- OGETG // runtime.getg() (read g pointer)
-
- OEND
-)
-
-// Nodes is a pointer to a slice of *Node.
-// For fields that are not used in most nodes, this is used instead of
-// a slice to save space.
-type Nodes struct{ slice *[]*Node }
-
-// asNodes returns a slice of *Node as a Nodes value.
-func asNodes(s []*Node) Nodes {
- return Nodes{&s}
-}
-
-// Slice returns the entries in Nodes as a slice.
-// Changes to the slice entries (as in s[i] = n) will be reflected in
-// the Nodes.
-func (n Nodes) Slice() []*Node {
- if n.slice == nil {
- return nil
- }
- return *n.slice
-}
-
-// Len returns the number of entries in Nodes.
-func (n Nodes) Len() int {
- if n.slice == nil {
- return 0
- }
- return len(*n.slice)
-}
-
-// Index returns the i'th element of Nodes.
-// It panics if n does not have at least i+1 elements.
-func (n Nodes) Index(i int) *Node {
- return (*n.slice)[i]
-}
-
-// First returns the first element of Nodes (same as n.Index(0)).
-// It panics if n has no elements.
-func (n Nodes) First() *Node {
- return (*n.slice)[0]
-}
-
-// Second returns the second element of Nodes (same as n.Index(1)).
-// It panics if n has fewer than two elements.
-func (n Nodes) Second() *Node {
- return (*n.slice)[1]
-}
-
-// Set sets n to a slice.
-// This takes ownership of the slice.
-func (n *Nodes) Set(s []*Node) {
- if len(s) == 0 {
- n.slice = nil
- } else {
- // Copy s and take address of t rather than s to avoid
- // allocation in the case where len(s) == 0 (which is
- // over 3x more common, dynamically, for make.bash).
- t := s
- n.slice = &t
- }
-}
-
-// Set1 sets n to a slice containing a single node.
-func (n *Nodes) Set1(n1 *Node) {
- n.slice = &[]*Node{n1}
-}
-
-// Set2 sets n to a slice containing two nodes.
-func (n *Nodes) Set2(n1, n2 *Node) {
- n.slice = &[]*Node{n1, n2}
-}
-
-// Set3 sets n to a slice containing three nodes.
-func (n *Nodes) Set3(n1, n2, n3 *Node) {
- n.slice = &[]*Node{n1, n2, n3}
-}
-
-// MoveNodes sets n to the contents of n2, then clears n2.
-func (n *Nodes) MoveNodes(n2 *Nodes) {
- n.slice = n2.slice
- n2.slice = nil
-}
-
-// SetIndex sets the i'th element of Nodes to node.
-// It panics if n does not have at least i+1 elements.
-func (n Nodes) SetIndex(i int, node *Node) {
- (*n.slice)[i] = node
-}
-
-// SetFirst sets the first element of Nodes to node.
-// It panics if n does not have at least one elements.
-func (n Nodes) SetFirst(node *Node) {
- (*n.slice)[0] = node
-}
-
-// SetSecond sets the second element of Nodes to node.
-// It panics if n does not have at least two elements.
-func (n Nodes) SetSecond(node *Node) {
- (*n.slice)[1] = node
-}
-
-// Addr returns the address of the i'th element of Nodes.
-// It panics if n does not have at least i+1 elements.
-func (n Nodes) Addr(i int) **Node {
- return &(*n.slice)[i]
-}
-
-// Append appends entries to Nodes.
-func (n *Nodes) Append(a ...*Node) {
- if len(a) == 0 {
- return
- }
- if n.slice == nil {
- s := make([]*Node, len(a))
- copy(s, a)
- n.slice = &s
- return
- }
- *n.slice = append(*n.slice, a...)
-}
-
-// Prepend prepends entries to Nodes.
-// If a slice is passed in, this will take ownership of it.
-func (n *Nodes) Prepend(a ...*Node) {
- if len(a) == 0 {
- return
- }
- if n.slice == nil {
- n.slice = &a
- } else {
- *n.slice = append(a, *n.slice...)
- }
-}
-
-// AppendNodes appends the contents of *n2 to n, then clears n2.
-func (n *Nodes) AppendNodes(n2 *Nodes) {
- switch {
- case n2.slice == nil:
- case n.slice == nil:
- n.slice = n2.slice
- default:
- *n.slice = append(*n.slice, *n2.slice...)
- }
- n2.slice = nil
-}
-
-// inspect invokes f on each node in an AST in depth-first order.
-// If f(n) returns false, inspect skips visiting n's children.
-func inspect(n *Node, f func(*Node) bool) {
- if n == nil || !f(n) {
- return
- }
- inspectList(n.Ninit, f)
- inspect(n.Left, f)
- inspect(n.Right, f)
- inspectList(n.List, f)
- inspectList(n.Nbody, f)
- inspectList(n.Rlist, f)
-}
-
-func inspectList(l Nodes, f func(*Node) bool) {
- for _, n := range l.Slice() {
- inspect(n, f)
- }
-}
-
-// nodeQueue is a FIFO queue of *Node. The zero value of nodeQueue is
-// a ready-to-use empty queue.
-type nodeQueue struct {
- ring []*Node
- head, tail int
-}
-
-// empty reports whether q contains no Nodes.
-func (q *nodeQueue) empty() bool {
- return q.head == q.tail
-}
-
-// pushRight appends n to the right of the queue.
-func (q *nodeQueue) pushRight(n *Node) {
- if len(q.ring) == 0 {
- q.ring = make([]*Node, 16)
- } else if q.head+len(q.ring) == q.tail {
- // Grow the ring.
- nring := make([]*Node, len(q.ring)*2)
- // Copy the old elements.
- part := q.ring[q.head%len(q.ring):]
- if q.tail-q.head <= len(part) {
- part = part[:q.tail-q.head]
- copy(nring, part)
- } else {
- pos := copy(nring, part)
- copy(nring[pos:], q.ring[:q.tail%len(q.ring)])
- }
- q.ring, q.head, q.tail = nring, 0, q.tail-q.head
- }
-
- q.ring[q.tail%len(q.ring)] = n
- q.tail++
-}
-
-// popLeft pops a node from the left of the queue. It panics if q is
-// empty.
-func (q *nodeQueue) popLeft() *Node {
- if q.empty() {
- panic("dequeue empty")
- }
- n := q.ring[q.head%len(q.ring)]
- q.head++
- return n
-}
-
-// NodeSet is a set of Nodes.
-type NodeSet map[*Node]struct{}
-
-// Has reports whether s contains n.
-func (s NodeSet) Has(n *Node) bool {
- _, isPresent := s[n]
- return isPresent
-}
-
-// Add adds n to s.
-func (s *NodeSet) Add(n *Node) {
- if *s == nil {
- *s = make(map[*Node]struct{})
- }
- (*s)[n] = struct{}{}
-}
-
-// Sorted returns s sorted according to less.
-func (s NodeSet) Sorted(less func(*Node, *Node) bool) []*Node {
- var res []*Node
- for n := range s {
- res = append(res, n)
- }
- sort.Slice(res, func(i, j int) bool { return less(res[i], res[j]) })
- return res
-}
->>>>>>> BRANCH (dab3e5 runtime: switch runtime to libc for openbsd/amd64)
diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go
index 0ea72a2..5bb0189 100644
--- a/src/cmd/compile/internal/noder/noder.go
+++ b/src/cmd/compile/internal/noder/noder.go
@@ -475,7 +475,7 @@
p.errorAt(e.Pos, "//go:embed only allowed in Go files that import \"embed\"")
}
} else {
- exprs = varEmbed(p, names, typ, exprs, pragma.Embeds)
+ varEmbed(p, names, typ, exprs, pragma.Embeds)
}
pragma.Embeds = nil
}
@@ -1923,7 +1923,7 @@
return n
}
-func varEmbed(p *noder, names []*ir.Name, typ ir.Ntype, exprs []ir.Node, embeds []pragmaEmbed) (newExprs []ir.Node) {
+func varEmbed(p *noder, names []*ir.Name, typ ir.Ntype, exprs []ir.Node, embeds []pragmaEmbed) {
haveEmbed := false
for _, decl := range p.file.DeclList {
imp, ok := decl.(*syntax.ImportDecl)
@@ -1941,28 +1941,24 @@
pos := embeds[0].Pos
if !haveEmbed {
p.errorAt(pos, "invalid go:embed: missing import \"embed\"")
- return exprs
- }
- if base.Flag.Cfg.Embed.Patterns == nil {
- p.errorAt(pos, "invalid go:embed: build system did not supply embed configuration")
- return exprs
+ return
}
if len(names) > 1 {
p.errorAt(pos, "go:embed cannot apply to multiple vars")
- return exprs
+ return
}
if len(exprs) > 0 {
p.errorAt(pos, "go:embed cannot apply to var with initializer")
- return exprs
+ return
}
if typ == nil {
// Should not happen, since len(exprs) == 0 now.
p.errorAt(pos, "go:embed cannot apply to var without type")
- return exprs
+ return
}
if typecheck.DeclContext != ir.PEXTERN {
p.errorAt(pos, "go:embed cannot apply to var inside func")
- return exprs
+ return
}
v := names[0]
@@ -1971,5 +1967,4 @@
for _, e := range embeds {
*v.Embed = append(*v.Embed, ir.Embed{Pos: p.makeXPos(e.Pos), Patterns: e.Patterns})
}
- return exprs
}
diff --git a/src/cmd/compile/internal/pkginit/initorder.go b/src/cmd/compile/internal/pkginit/initorder.go
index bdefd59..97d6962 100644
--- a/src/cmd/compile/internal/pkginit/initorder.go
+++ b/src/cmd/compile/internal/pkginit/initorder.go
@@ -113,7 +113,7 @@
// first.
base.ExitIfErrors()
- o.findInitLoopAndExit(firstLHS(n), new([]*ir.Name))
+ o.findInitLoopAndExit(firstLHS(n), new([]*ir.Name), new(ir.NameSet))
base.Fatalf("initialization unfinished, but failed to identify loop")
}
}
@@ -184,10 +184,7 @@
// path points to a slice used for tracking the sequence of
// variables/functions visited. Using a pointer to a slice allows the
// slice capacity to grow and limit reallocations.
-func (o *InitOrder) findInitLoopAndExit(n *ir.Name, path *[]*ir.Name) {
- // We implement a simple DFS loop-finding algorithm. This
- // could be faster, but initialization cycles are rare.
-
+func (o *InitOrder) findInitLoopAndExit(n *ir.Name, path *[]*ir.Name, ok *ir.NameSet) {
for i, x := range *path {
if x == n {
reportInitLoopAndExit((*path)[i:])
@@ -204,12 +201,19 @@
*path = append(*path, n)
for _, ref := range refers {
// Short-circuit variables that were initialized.
- if ref.Class == ir.PEXTERN && o.order[ref.Defn] == orderDone {
+ if ref.Class == ir.PEXTERN && o.order[ref.Defn] == orderDone || ok.Has(ref) {
continue
}
- o.findInitLoopAndExit(ref, path)
+ o.findInitLoopAndExit(ref, path, ok)
}
+
+ // n is not involved in a cycle.
+ // Record that fact to avoid checking it again when reached another way,
+ // or else this traversal will take exponential time traversing all paths
+ // through the part of the package's call graph implicated in the cycle.
+ ok.Add(n)
+
*path = (*path)[:len(*path)-1]
}
diff --git a/src/cmd/compile/internal/staticdata/embed.go b/src/cmd/compile/internal/staticdata/embed.go
index 2e15841..8936c4f 100644
--- a/src/cmd/compile/internal/staticdata/embed.go
+++ b/src/cmd/compile/internal/staticdata/embed.go
@@ -23,13 +23,7 @@
embedFiles
)
-func embedFileList(v *ir.Name) []string {
- kind := embedKind(v.Type())
- if kind == embedUnknown {
- base.ErrorfAt(v.Pos(), "go:embed cannot apply to var of type %v", v.Type())
- return nil
- }
-
+func embedFileList(v *ir.Name, kind int) []string {
// Build list of files to store.
have := make(map[string]bool)
var list []string
@@ -71,38 +65,15 @@
return list
}
-// embedKindApprox determines the kind of embedding variable, approximately.
-// The match is approximate because we haven't done scope resolution yet and
-// can't tell whether "string" and "byte" really mean "string" and "byte".
-// The result must be confirmed later, after type checking, using embedKind.
-func embedKindApprox(typ ir.Node) int {
- if typ.Sym() != nil && typ.Sym().Name == "FS" && (typ.Sym().Pkg.Path == "embed" || (typ.Sym().Pkg == types.LocalPkg && base.Ctxt.Pkgpath == "embed")) {
- return embedFiles
- }
- // These are not guaranteed to match only string and []byte -
- // maybe the local package has redefined one of those words.
- // But it's the best we can do now during the noder.
- // The stricter check happens later, in WriteEmbed calling embedKind.
- if typ.Sym() != nil && typ.Sym().Name == "string" && typ.Sym().Pkg == types.LocalPkg {
- return embedString
- }
- if typ, ok := typ.(*ir.SliceType); ok {
- if sym := typ.Elem.Sym(); sym != nil && sym.Name == "byte" && sym.Pkg == types.LocalPkg {
- return embedBytes
- }
- }
- return embedUnknown
-}
-
// embedKind determines the kind of embedding variable.
func embedKind(typ *types.Type) int {
if typ.Sym() != nil && typ.Sym().Name == "FS" && (typ.Sym().Pkg.Path == "embed" || (typ.Sym().Pkg == types.LocalPkg && base.Ctxt.Pkgpath == "embed")) {
return embedFiles
}
- if typ == types.Types[types.TSTRING] {
+ if typ.Kind() == types.TSTRING {
return embedString
}
- if typ.Sym() == nil && typ.IsSlice() && typ.Elem() == types.ByteType {
+ if typ.Sym() == nil && typ.IsSlice() && typ.Elem().Kind() == types.TUINT8 {
return embedBytes
}
return embedUnknown
@@ -134,11 +105,28 @@
// WriteEmbed emits the init data for a //go:embed variable,
// which is either a string, a []byte, or an embed.FS.
func WriteEmbed(v *ir.Name) {
- files := embedFileList(v)
- switch kind := embedKind(v.Type()); kind {
- case embedUnknown:
- base.ErrorfAt(v.Pos(), "go:embed cannot apply to var of type %v", v.Type())
+ // TODO(mdempsky): User errors should be reported by the frontend.
+ commentPos := (*v.Embed)[0].Pos
+ if !types.AllowsGoVersion(types.LocalPkg, 1, 16) {
+ prevPos := base.Pos
+ base.Pos = commentPos
+ base.ErrorfVers("go1.16", "go:embed")
+ base.Pos = prevPos
+ return
+ }
+ if base.Flag.Cfg.Embed.Patterns == nil {
+ base.ErrorfAt(commentPos, "invalid go:embed: build system did not supply embed configuration")
+ return
+ }
+ kind := embedKind(v.Type())
+ if kind == embedUnknown {
+ base.ErrorfAt(v.Pos(), "go:embed cannot apply to var of type %v", v.Type())
+ return
+ }
+
+ files := embedFileList(v, kind)
+ switch kind {
case embedString, embedBytes:
file := files[0]
fsym, size, err := fileStringSym(v.Pos(), base.Flag.Cfg.Embed.Files[file], kind == embedString, nil)
diff --git a/src/embed/internal/embedtest/embed_test.go b/src/embed/internal/embedtest/embed_test.go
index a0da6e6..43ae5c7 100644
--- a/src/embed/internal/embedtest/embed_test.go
+++ b/src/embed/internal/embedtest/embed_test.go
@@ -74,21 +74,10 @@
}
//go:embed testdata
-<<<<<<< HEAD (d7e71c [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSe)
-var dir embed.FS
-
-//go:embed testdata/*
-var star embed.FS
-=======
var testDirAll embed.FS
->>>>>>> BRANCH (dab3e5 runtime: switch runtime to libc for openbsd/amd64)
func TestDir(t *testing.T) {
-<<<<<<< HEAD (d7e71c [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSe)
- all := dir
-=======
all := testDirAll
->>>>>>> BRANCH (dab3e5 runtime: switch runtime to libc for openbsd/amd64)
testFiles(t, all, "testdata/hello.txt", "hello, world\n")
testFiles(t, all, "testdata/i/i18n.txt", "internationalization\n")
testFiles(t, all, "testdata/i/j/k/k8s.txt", "kubernetes\n")
@@ -100,9 +89,6 @@
testDir(t, all, "testdata/i/j/k", "k8s.txt")
}
-<<<<<<< HEAD (d7e71c [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSe)
-func TestHidden(t *testing.T) {
-=======
//go:embed testdata
var testHiddenDir embed.FS
@@ -113,7 +99,6 @@
dir := testHiddenDir
star := testHiddenStar
->>>>>>> BRANCH (dab3e5 runtime: switch runtime to libc for openbsd/amd64)
t.Logf("//go:embed testdata")
testDir(t, dir, "testdata",
diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go
index 76104b9..63e31f3 100644
--- a/src/go/types/stdlib_test.go
+++ b/src/go/types/stdlib_test.go
@@ -155,12 +155,9 @@
testTestDir(t, filepath.Join(runtime.GOROOT(), "test"),
"cmplxdivide.go", // also needs file cmplxdivide1.go - ignore
"directive.go", // tests compiler rejection of bad directive placement - ignore
-<<<<<<< HEAD (d7e71c [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSe)
- "linkname2.go", // go/types doesn't check validity of //go:xxx directives
-=======
"embedfunc.go", // tests //go:embed
"embedvers.go", // tests //go:embed
->>>>>>> BRANCH (dab3e5 runtime: switch runtime to libc for openbsd/amd64)
+ "linkname2.go", // go/types doesn't check validity of //go:xxx directives
)
}
To view, visit change 285875. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Robert Griesemer.
Patch set 1:Trust +1
1 comment:
Patchset:
Reminder to take advantage of Gerrit's "Auto Merge" diff-base option.
Also, this page shows the package gc changes included in the merge set: https://go.googlesource.com/go/+/4fd9455882%5E..dab3e5affe/src/cmd/compile/internal/gc
(Since Gerrit just shows the entire files as conflict/deleted, without highlighting any of the actual changes that happened in them on master.)
To view, visit change 285875. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Robert Griesemer.
1 comment:
Patchset:
1 of 20 TryBots failed:
Failed on windows-amd64-2016: https://storage.googleapis.com/go-build-log/7e0a81d2/windows-amd64-2016_7050b3a6.log
This failure is https://github.com/golang/go/issues/42637.
To view, visit change 285875. To unsubscribe, or for help writing mail filters, visit settings.
Patchset:
[+rsc for FYI, or review]
To view, visit change 285875. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Robert Griesemer, Matthew Dempsky.
1 comment:
Patchset:
Thanks for taking care of this. I took a stab at it myself last night and eventually gave up (the conflicts were very messy/tricky).
To view, visit change 285875. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Robert Griesemer, Than McIntosh.
1 comment:
Patchset:
Thanks for taking care of this. […]
Ack. Sorry it took so long, but I'm sure you can appreciate the delay then. :)
gri reports his Internet is out at the moment. Do you mind reviewing the CL? (Make sure to see my earlier comment about "Auto Merge" and the diffs on master, if you missed it.)
To view, visit change 285875. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Robert Griesemer, Matthew Dempsky.
1 comment:
Patchset:
Ack. Sorry it took so long, but I'm sure you can appreciate the delay then. :) […]
Sure, I will look it over.
To view, visit change 285875. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Robert Griesemer, Matthew Dempsky.
Patch set 1:Code-Review +2
1 comment:
Patchset:
LGTM
To view, visit change 285875. To unsubscribe, or for help writing mail filters, visit settings.
Matthew Dempsky submitted this change.
To view, visit change 285875. To unsubscribe, or for help writing mail filters, visit settings.