libgo: update to Go 1.12.1
authorIan Lance Taylor <ian@gcc.gnu.org>
Mon, 18 Mar 2019 20:27:59 +0000 (20:27 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Mon, 18 Mar 2019 20:27:59 +0000 (20:27 +0000)
    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/167749

From-SVN: r269780

23 files changed:
gcc/go/gofrontend/MERGE
libgo/MERGE
libgo/VERSION
libgo/go/cmd/cgo/ast.go
libgo/go/cmd/cgo/gcc.go
libgo/go/cmd/cgo/main.go
libgo/go/cmd/go/internal/cache/default.go
libgo/go/cmd/go/internal/load/test.go
libgo/go/cmd/go/internal/modfetch/coderepo.go
libgo/go/cmd/go/internal/modfetch/coderepo_test.go
libgo/go/internal/fmtsort/sort.go
libgo/go/internal/fmtsort/sort_test.go
libgo/go/os/removeall_at.go
libgo/go/os/removeall_test.go
libgo/go/path/filepath/path.go
libgo/go/path/filepath/path_test.go
libgo/go/path/filepath/symlink.go
libgo/go/runtime/testdata/testprog/gc.go
libgo/go/text/template/exec.go
libgo/go/text/template/exec_test.go
libgo/misc/cgo/test/testdata/issue30527.go [new file with mode: 0644]
libgo/misc/cgo/test/testdata/issue30527/a.go [new file with mode: 0644]
libgo/misc/cgo/test/testdata/issue30527/b.go [new file with mode: 0644]

index 13c12e7de53c5e7cee1eaed48cd7cc4fde06694e..a09733f8c467407d5420e62dba01c5fe5bfbc6c0 100644 (file)
@@ -1,4 +1,4 @@
-cc70be24502faeffefb66fd0abeb7f20a6c7792a
+87945b620b2100d33e27f33e6276a4e4e5890659
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 1f0d2d4629a7a84900adc48f2dd97fd04da084f5..d456361af0b159bac11ea8947d434308c0615ca7 100644 (file)
@@ -1,4 +1,4 @@
-05e77d41914d247a1e7caf37d7125ccaa5a53505
+0380c9ad38843d523d9c9804fe300cb7edd7cd3c
 
 The first line of this file holds the git revision number of the
 last merge done from the master library sources.
index 18a48c30a2f514bdb1cbcd09b5e27b74f6a1667e..f325447817bc7d75c1be02cc1546a70673304f0d 100644 (file)
@@ -1 +1 @@
-go1.12
+go1.12.1
index 83d727a8a5da6846865076daff262df8ef7b01f9..54d6bc2559dc9d901d7ac8ba6b59a6bb30426351 100644 (file)
@@ -200,18 +200,6 @@ func (f *File) saveExprs(x interface{}, context astContext) {
                }
        case *ast.CallExpr:
                f.saveCall(x, context)
-       case *ast.GenDecl:
-               if x.Tok == token.CONST {
-                       for _, spec := range x.Specs {
-                               vs := spec.(*ast.ValueSpec)
-                               if vs.Type == nil {
-                                       for _, name := range spec.(*ast.ValueSpec).Names {
-                                               consts[name.Name] = true
-                                       }
-                               }
-                       }
-               }
-
        }
 }
 
index 7db702f1266883a50cbc8d50f6167f030697b3ce..e56207acaa7f32daf9c719b9396b41b87950b239 100644 (file)
@@ -915,21 +915,16 @@ func (p *Package) rewriteCall(f *File, call *Call) (string, bool) {
                        needsUnsafe = true
                }
 
-               // Explicitly convert untyped constants to the
-               // parameter type, to avoid a type mismatch.
-               if p.isConst(f, arg) {
-                       ptype := p.rewriteUnsafe(param.Go)
+               // Use "var x T = ..." syntax to explicitly convert untyped
+               // constants to the parameter type, to avoid a type mismatch.
+               ptype := p.rewriteUnsafe(param.Go)
+
+               if !p.needsPointerCheck(f, param.Go, args[i]) || param.BadPointer {
                        if ptype != param.Go {
                                needsUnsafe = true
                        }
-                       arg = &ast.CallExpr{
-                               Fun:  ptype,
-                               Args: []ast.Expr{arg},
-                       }
-               }
-
-               if !p.needsPointerCheck(f, param.Go, args[i]) {
-                       fmt.Fprintf(&sb, "_cgo%d := %s; ", i, gofmtPos(arg, origArg.Pos()))
+                       fmt.Fprintf(&sb, "var _cgo%d %s = %s; ", i,
+                               gofmtLine(ptype), gofmtPos(arg, origArg.Pos()))
                        continue
                }
 
@@ -1272,47 +1267,6 @@ func (p *Package) isType(t ast.Expr) bool {
        return false
 }
 
-// isConst reports whether x is an untyped constant expression.
-func (p *Package) isConst(f *File, x ast.Expr) bool {
-       switch x := x.(type) {
-       case *ast.BasicLit:
-               return true
-       case *ast.SelectorExpr:
-               id, ok := x.X.(*ast.Ident)
-               if !ok || id.Name != "C" {
-                       return false
-               }
-               name := f.Name[x.Sel.Name]
-               if name != nil {
-                       return name.IsConst()
-               }
-       case *ast.Ident:
-               return x.Name == "nil" ||
-                       strings.HasPrefix(x.Name, "_Ciconst_") ||
-                       strings.HasPrefix(x.Name, "_Cfconst_") ||
-                       strings.HasPrefix(x.Name, "_Csconst_") ||
-                       consts[x.Name]
-       case *ast.UnaryExpr:
-               return p.isConst(f, x.X)
-       case *ast.BinaryExpr:
-               return p.isConst(f, x.X) && p.isConst(f, x.Y)
-       case *ast.ParenExpr:
-               return p.isConst(f, x.X)
-       case *ast.CallExpr:
-               // Calling the builtin function complex on two untyped
-               // constants returns an untyped constant.
-               // TODO: It's possible to construct a case that will
-               // erroneously succeed if there is a local function
-               // named "complex", shadowing the builtin, that returns
-               // a numeric type. I can't think of any cases that will
-               // erroneously fail.
-               if id, ok := x.Fun.(*ast.Ident); ok && id.Name == "complex" && len(x.Args) == 2 {
-                       return p.isConst(f, x.Args[0]) && p.isConst(f, x.Args[1])
-               }
-       }
-       return false
-}
-
 // isVariable reports whether x is a variable, possibly with field references.
 func (p *Package) isVariable(x ast.Expr) bool {
        switch x := x.(type) {
@@ -2533,13 +2487,16 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
                        // Treat this typedef as a uintptr.
                        s := *sub
                        s.Go = c.uintptr
+                       s.BadPointer = true
                        sub = &s
                        // Make sure we update any previously computed type.
                        if oldType := typedef[name.Name]; oldType != nil {
                                oldType.Go = sub.Go
+                               oldType.BadPointer = true
                        }
                }
                t.Go = name
+               t.BadPointer = sub.BadPointer
                if unionWithPointer[sub.Go] {
                        unionWithPointer[t.Go] = true
                }
@@ -2549,6 +2506,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
                if oldType == nil {
                        tt := *t
                        tt.Go = sub.Go
+                       tt.BadPointer = sub.BadPointer
                        typedef[name.Name] = &tt
                }
 
index 4a5c0ca2babb7360eb7f5c2e271dacd8168c9481..80f35681d75f0dfcd91cf1781ff1d4f74c5167fd 100644 (file)
@@ -71,9 +71,6 @@ type File struct {
        Edit     *edit.Buffer
 }
 
-// Untyped constants in the current package.
-var consts = make(map[string]bool)
-
 func (f *File) offset(p token.Pos) int {
        return fset.Position(p).Offset
 }
@@ -154,6 +151,7 @@ type Type struct {
        Go         ast.Expr
        EnumValues map[string]int64
        Typedef    string
+       BadPointer bool
 }
 
 // A FuncType collects information about a function type in both the C and Go worlds.
index f545c147009f247e3dbc25c76efe68940391bc11..7d389c3c1af9f13b16429480924f6d1709b9aa42 100644 (file)
@@ -37,7 +37,7 @@ See golang.org to learn more about Go.
 // the first time Default is called.
 func initDefaultCache() {
        dir := DefaultDir()
-       if dir == "off" || dir == "" {
+       if dir == "off" {
                if defaultDirErr != nil {
                        base.Fatalf("build cache is required, but could not be located: %v", defaultDirErr)
                }
@@ -74,7 +74,12 @@ func DefaultDir() string {
 
        defaultDirOnce.Do(func() {
                defaultDir = os.Getenv("GOCACHE")
+               if filepath.IsAbs(defaultDir) || defaultDir == "off" {
+                       return
+               }
                if defaultDir != "" {
+                       defaultDir = "off"
+                       defaultDirErr = fmt.Errorf("GOCACHE is not an absolute path")
                        return
                }
 
index 5f9daa4957845d4a54f3d561f0cb9fdfe3f00c4d..c0e06676df1eb22403f2bf53fc71f48c8468a87a 100644 (file)
@@ -129,6 +129,7 @@ func GetTestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Pac
                ptest.Internal.Imports = append(imports, p.Internal.Imports...)
                ptest.Internal.RawImports = str.StringList(rawTestImports, p.Internal.RawImports)
                ptest.Internal.ForceLibrary = true
+               ptest.Internal.BuildInfo = ""
                ptest.Internal.Build = new(build.Package)
                *ptest.Internal.Build = *p.Internal.Build
                m := map[string][]token.Position{}
@@ -186,6 +187,7 @@ func GetTestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Pac
                },
                Internal: PackageInternal{
                        Build:      &build.Package{Name: "main"},
+                       BuildInfo:  p.Internal.BuildInfo,
                        Asmflags:   p.Internal.Asmflags,
                        Gcflags:    p.Internal.Gcflags,
                        Ldflags:    p.Internal.Ldflags,
@@ -352,6 +354,7 @@ func recompileForTest(pmain, preal, ptest, pxtest *Package) {
                        copy(p1.Imports, p.Imports)
                        p = p1
                        p.Target = ""
+                       p.Internal.BuildInfo = ""
                }
 
                // Update p.Internal.Imports to use test copies.
@@ -361,6 +364,13 @@ func recompileForTest(pmain, preal, ptest, pxtest *Package) {
                                p.Internal.Imports[i] = p1
                        }
                }
+
+               // Don't compile build info from a main package. This can happen
+               // if -coverpkg patterns include main packages, since those packages
+               // are imported by pmain.
+               if p.Internal.BuildInfo != "" && p != pmain {
+                       split()
+               }
        }
 }
 
index 5018b6d8af7c7f7f7d4690277ac572f4649d3965..da9f63fec9c0fb4d1efe81ea3035abc125032181 100644 (file)
@@ -23,55 +23,99 @@ import (
 type codeRepo struct {
        modPath string
 
-       code     codehost.Repo
+       // code is the repository containing this module.
+       code codehost.Repo
+       // codeRoot is the import path at the root of code.
        codeRoot string
-       codeDir  string
+       // codeDir is the directory (relative to root) at which we expect to find the module.
+       // If pathMajor is non-empty and codeRoot is not the full modPath,
+       // then we look in both codeDir and codeDir+modPath
+       codeDir string
 
-       path        string
-       pathPrefix  string
-       pathMajor   string
+       // pathMajor is the suffix of modPath that indicates its major version,
+       // or the empty string if modPath is at major version 0 or 1.
+       //
+       // pathMajor is typically of the form "/vN", but possibly ".vN", or
+       // ".vN-unstable" for modules resolved using gopkg.in.
+       pathMajor string
+       // pathPrefix is the prefix of modPath that excludes pathMajor.
+       // It is used only for logging.
+       pathPrefix string
+
+       // pseudoMajor is the major version prefix to use when generating
+       // pseudo-versions for this module, derived from the module path.
+       //
+       // TODO(golang.org/issue/29262): We can't distinguish v0 from v1 using the
+       // path alone: we have to compute it by examining the tags at a particular
+       // revision.
        pseudoMajor string
 }
 
-func newCodeRepo(code codehost.Repo, root, path string) (Repo, error) {
-       if !hasPathPrefix(path, root) {
-               return nil, fmt.Errorf("mismatched repo: found %s for %s", root, path)
+// newCodeRepo returns a Repo that reads the source code for the module with the
+// given path, from the repo stored in code, with the root of the repo
+// containing the path given by codeRoot.
+func newCodeRepo(code codehost.Repo, codeRoot, path string) (Repo, error) {
+       if !hasPathPrefix(path, codeRoot) {
+               return nil, fmt.Errorf("mismatched repo: found %s for %s", codeRoot, path)
        }
        pathPrefix, pathMajor, ok := module.SplitPathVersion(path)
        if !ok {
                return nil, fmt.Errorf("invalid module path %q", path)
        }
+       if codeRoot == path {
+               pathPrefix = path
+       }
        pseudoMajor := "v0"
        if pathMajor != "" {
                pseudoMajor = pathMajor[1:]
        }
 
+       // Compute codeDir = bar, the subdirectory within the repo
+       // corresponding to the module root.
+       //
        // At this point we might have:
-       //      codeRoot = github.com/rsc/foo
        //      path = github.com/rsc/foo/bar/v2
+       //      codeRoot = github.com/rsc/foo
        //      pathPrefix = github.com/rsc/foo/bar
        //      pathMajor = /v2
        //      pseudoMajor = v2
        //
-       // Compute codeDir = bar, the subdirectory within the repo
-       // corresponding to the module root.
-       codeDir := strings.Trim(strings.TrimPrefix(pathPrefix, root), "/")
-       if strings.HasPrefix(path, "gopkg.in/") {
-               // But gopkg.in is a special legacy case, in which pathPrefix does not start with codeRoot.
-               // For example we might have:
-               //      codeRoot = gopkg.in/yaml.v2
-               //      pathPrefix = gopkg.in/yaml
-               //      pathMajor = .v2
-               //      pseudoMajor = v2
-               //      codeDir = pathPrefix (because codeRoot is not a prefix of pathPrefix)
-               // Clear codeDir - the module root is the repo root for gopkg.in repos.
-               codeDir = ""
+       // which gives
+       //      codeDir = bar
+       //
+       // We know that pathPrefix is a prefix of path, and codeRoot is a prefix of
+       // path, but codeRoot may or may not be a prefix of pathPrefix, because
+       // codeRoot may be the entire path (in which case codeDir should be empty).
+       // That occurs in two situations.
+       //
+       // One is when a go-import meta tag resolves the complete module path,
+       // including the pathMajor suffix:
+       //      path = nanomsg.org/go/mangos/v2
+       //      codeRoot = nanomsg.org/go/mangos/v2
+       //      pathPrefix = nanomsg.org/go/mangos
+       //      pathMajor = /v2
+       //      pseudoMajor = v2
+       //
+       // The other is similar: for gopkg.in only, the major version is encoded
+       // with a dot rather than a slash, and thus can't be in a subdirectory.
+       //      path = gopkg.in/yaml.v2
+       //      codeRoot = gopkg.in/yaml.v2
+       //      pathPrefix = gopkg.in/yaml
+       //      pathMajor = .v2
+       //      pseudoMajor = v2
+       //
+       codeDir := ""
+       if codeRoot != path {
+               if !hasPathPrefix(pathPrefix, codeRoot) {
+                       return nil, fmt.Errorf("repository rooted at %s cannot contain module %s", codeRoot, path)
+               }
+               codeDir = strings.Trim(pathPrefix[len(codeRoot):], "/")
        }
 
        r := &codeRepo{
                modPath:     path,
                code:        code,
-               codeRoot:    root,
+               codeRoot:    codeRoot,
                codeDir:     codeDir,
                pathPrefix:  pathPrefix,
                pathMajor:   pathMajor,
@@ -149,9 +193,6 @@ func (r *codeRepo) Stat(rev string) (*RevInfo, error) {
                return r.Latest()
        }
        codeRev := r.revToRev(rev)
-       if semver.IsValid(codeRev) && r.codeDir != "" {
-               codeRev = r.codeDir + "/" + codeRev
-       }
        info, err := r.code.Stat(codeRev)
        if err != nil {
                return nil, err
@@ -290,7 +331,7 @@ func (r *codeRepo) findDir(version string) (rev, dir string, gomod []byte, err e
        found1 := err1 == nil && isMajor(mpath1, r.pathMajor)
 
        var file2 string
-       if r.pathMajor != "" && !strings.HasPrefix(r.pathMajor, ".") {
+       if r.pathMajor != "" && r.codeRoot != r.modPath && !strings.HasPrefix(r.pathMajor, ".") {
                // Suppose pathMajor is "/v2".
                // Either go.mod should claim v2 and v2/go.mod should not exist,
                // or v2/go.mod should exist and claim v2. Not both.
@@ -298,6 +339,9 @@ func (r *codeRepo) findDir(version string) (rev, dir string, gomod []byte, err e
                // because of replacement modules. This might be a fork of
                // the real module, found at a different path, usable only in
                // a replace directive.
+               //
+               // TODO(bcmills): This doesn't seem right. Investigate futher.
+               // (Notably: why can't we replace foo/v2 with fork-of-foo/v3?)
                dir2 := path.Join(r.codeDir, r.pathMajor[1:])
                file2 = path.Join(dir2, "go.mod")
                gomod2, err2 := r.code.ReadFile(rev, file2, codehost.MaxGoMod)
@@ -418,7 +462,7 @@ func (r *codeRepo) Zip(dst io.Writer, version string) error {
        }
        defer dl.Close()
        if actualDir != "" && !hasPathPrefix(dir, actualDir) {
-               return fmt.Errorf("internal error: downloading %v %v: dir=%q but actualDir=%q", r.path, rev, dir, actualDir)
+               return fmt.Errorf("internal error: downloading %v %v: dir=%q but actualDir=%q", r.modPath, rev, dir, actualDir)
        }
        subdir := strings.Trim(strings.TrimPrefix(dir, actualDir), "/")
 
index c93d8dbe44284f84eef58d0a7dc35f00179c8189..7a419576ced03ce767259e03a374ce19931d615b 100644 (file)
@@ -323,6 +323,15 @@ var codeRepoTests = []struct {
                time:    time.Date(2017, 5, 31, 16, 3, 50, 0, time.UTC),
                gomod:   "module gopkg.in/natefinch/lumberjack.v2\n",
        },
+       {
+               path:    "nanomsg.org/go/mangos/v2",
+               rev:     "v2.0.2",
+               version: "v2.0.2",
+               name:    "63f66a65137b9a648ac9f7bf0160b4a4d17d7999",
+               short:   "63f66a65137b",
+               time:    time.Date(2018, 12, 1, 15, 7, 40, 0, time.UTC),
+               gomod:   "module nanomsg.org/go/mangos/v2\n\nrequire (\n\tgithub.com/Microsoft/go-winio v0.4.11\n\tgithub.com/droundy/goopt v0.0.0-20170604162106-0b8effe182da\n\tgithub.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect\n\tgithub.com/gorilla/websocket v1.4.0\n\tgithub.com/jtolds/gls v4.2.1+incompatible // indirect\n\tgithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect\n\tgithub.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c\n\tgolang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 // indirect\n)\n",
+       },
 }
 
 func TestCodeRepo(t *testing.T) {
index c959cbee1f8af7fa9f365e0587cceacdca9dfe82..70a305a3a10330674e285fe34c54d5a084c5d305 100644 (file)
@@ -167,7 +167,7 @@ func compare(aVal, bVal reflect.Value) int {
                if c, ok := nilCompare(aVal, bVal); ok {
                        return c
                }
-               c := compare(reflect.ValueOf(aType), reflect.ValueOf(bType))
+               c := compare(reflect.ValueOf(aVal.Elem().Type()), reflect.ValueOf(bVal.Elem().Type()))
                if c != 0 {
                        return c
                }
index 6b10c775b0e93f87ff1c3537048cbae03d502824..e060d4bf5157aa4b81d5c2b27dae01dfe5c1581b 100644 (file)
@@ -126,10 +126,6 @@ var sortTests = []sortTest{
                map[[2]int]string{{7, 2}: "72", {7, 1}: "71", {3, 4}: "34"},
                "[3 4]:34 [7 1]:71 [7 2]:72",
        },
-       {
-               map[interface{}]string{7: "7", 4: "4", 3: "3", nil: "nil"},
-               "<nil>:nil 3:3 4:4 7:7",
-       },
 }
 
 func sprint(data interface{}) string {
@@ -210,3 +206,41 @@ func TestOrder(t *testing.T) {
                }
        }
 }
+
+func TestInterface(t *testing.T) {
+       // A map containing multiple concrete types should be sorted by type,
+       // then value. However, the relative ordering of types is unspecified,
+       // so test this by checking the presence of sorted subgroups.
+       m := map[interface{}]string{
+               [2]int{1, 0}:             "",
+               [2]int{0, 1}:             "",
+               true:                     "",
+               false:                    "",
+               3.1:                      "",
+               2.1:                      "",
+               1.1:                      "",
+               math.NaN():               "",
+               3:                        "",
+               2:                        "",
+               1:                        "",
+               "c":                      "",
+               "b":                      "",
+               "a":                      "",
+               struct{ x, y int }{1, 0}: "",
+               struct{ x, y int }{0, 1}: "",
+       }
+       got := sprint(m)
+       typeGroups := []string{
+               "NaN: 1.1: 2.1: 3.1:", // float64
+               "false: true:",        // bool
+               "1: 2: 3:",            // int
+               "a: b: c:",            // string
+               "[0 1]: [1 0]:",       // [2]int
+               "{0 1}: {1 0}:",       // struct{ x int; y int }
+       }
+       for _, g := range typeGroups {
+               if !strings.Contains(got, g) {
+                       t.Errorf("sorted map should contain %q", g)
+               }
+       }
+}
index abdcb66c1f8737c8e56a85be543400a01823ba28..512a891017dc34225f3e39f05c5a8f8de75cb64e 100644 (file)
@@ -92,7 +92,8 @@ func removeAllFrom(parent *File, path string) error {
                        if IsNotExist(err) {
                                return nil
                        }
-                       return err
+                       recurseErr = err
+                       break
                }
 
                names, readErr := file.Readdirnames(request)
index 9dab0d4bb1079a8881e4a7d1437d822aed2696d2..21371d8776a68ccf54f739d1fb654b7dd2a90034 100644 (file)
@@ -372,3 +372,33 @@ func TestRemoveAllButReadOnly(t *testing.T) {
                }
        }
 }
+
+func TestRemoveUnreadableDir(t *testing.T) {
+       switch runtime.GOOS {
+       case "nacl", "js", "windows":
+               t.Skipf("skipping test on %s", runtime.GOOS)
+       }
+
+       if Getuid() == 0 {
+               t.Skip("skipping test when running as root")
+       }
+
+       t.Parallel()
+
+       tempDir, err := ioutil.TempDir("", "TestRemoveAllButReadOnly-")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer RemoveAll(tempDir)
+
+       target := filepath.Join(tempDir, "d0", "d1", "d2")
+       if err := MkdirAll(target, 0755); err != nil {
+               t.Fatal(err)
+       }
+       if err := Chmod(target, 0300); err != nil {
+               t.Fatal(err)
+       }
+       if err := RemoveAll(filepath.Join(tempDir, "d0")); err != nil {
+               t.Fatal(err)
+       }
+}
index bbb90306a7f3891cac0492f9510f458b5d2cc9e5..aba1717e7d5e0c7186a0e2a91adb011730d71f0e 100644 (file)
@@ -96,19 +96,14 @@ func Clean(path string) string {
                }
                return originalPath + "."
        }
-
-       n := len(path)
-       if volLen > 2 && n == 1 && os.IsPathSeparator(path[0]) {
-               // UNC volume name with trailing slash.
-               return FromSlash(originalPath[:volLen])
-       }
        rooted := os.IsPathSeparator(path[0])
 
        // Invariants:
        //      reading from path; r is index of next byte to process.
-       //      writing to out; w is index of next byte to write.
-       //      dotdot is index in out where .. must stop, either because
+       //      writing to buf; w is index of next byte to write.
+       //      dotdot is index in buf where .. must stop, either because
        //              it is the leading slash or it is a leading ../../.. prefix.
+       n := len(path)
        out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen}
        r, dotdot := 0, 0
        if rooted {
index deeb47ed7190802427b40964ab06b03f990375e4..22632a0b63a90db85eb250e0739c73d4f508ee5d 100644 (file)
@@ -93,9 +93,6 @@ var wincleantests = []PathTest{
        {`//host/share/foo/../baz`, `\\host\share\baz`},
        {`\\a\b\..\c`, `\\a\b\c`},
        {`\\a\b`, `\\a\b`},
-       {`\\a\b\`, `\\a\b`},
-       {`\\folder\share\foo`, `\\folder\share\foo`},
-       {`\\folder\share\foo\`, `\\folder\share\foo`},
 }
 
 func TestClean(t *testing.T) {
@@ -1417,3 +1414,103 @@ func TestIssue29372(t *testing.T) {
                }
        }
 }
+
+// Issue 30520 part 1.
+func TestEvalSymlinksAboveRoot(t *testing.T) {
+       testenv.MustHaveSymlink(t)
+
+       t.Parallel()
+
+       tmpDir, err := ioutil.TempDir("", "TestEvalSymlinksAboveRoot")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(tmpDir)
+
+       evalTmpDir, err := filepath.EvalSymlinks(tmpDir)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if err := os.Mkdir(filepath.Join(evalTmpDir, "a"), 0777); err != nil {
+               t.Fatal(err)
+       }
+       if err := os.Symlink(filepath.Join(evalTmpDir, "a"), filepath.Join(evalTmpDir, "b")); err != nil {
+               t.Fatal(err)
+       }
+       if err := ioutil.WriteFile(filepath.Join(evalTmpDir, "a", "file"), nil, 0666); err != nil {
+               t.Fatal(err)
+       }
+
+       // Count the number of ".." elements to get to the root directory.
+       vol := filepath.VolumeName(evalTmpDir)
+       c := strings.Count(evalTmpDir[len(vol):], string(os.PathSeparator))
+       var dd []string
+       for i := 0; i < c+2; i++ {
+               dd = append(dd, "..")
+       }
+
+       wantSuffix := strings.Join([]string{"a", "file"}, string(os.PathSeparator))
+
+       // Try different numbers of "..".
+       for _, i := range []int{c, c + 1, c + 2} {
+               check := strings.Join([]string{evalTmpDir, strings.Join(dd[:i], string(os.PathSeparator)), evalTmpDir[len(vol)+1:], "b", "file"}, string(os.PathSeparator))
+               if resolved, err := filepath.EvalSymlinks(check); err != nil {
+                       t.Errorf("EvalSymlinks(%q) failed: %v", check, err)
+               } else if !strings.HasSuffix(resolved, wantSuffix) {
+                       t.Errorf("EvalSymlinks(%q) = %q does not end with %q", check, resolved, wantSuffix)
+               } else {
+                       t.Logf("EvalSymlinks(%q) = %q", check, resolved)
+               }
+       }
+}
+
+// Issue 30520 part 2.
+func TestEvalSymlinksAboveRootChdir(t *testing.T) {
+       testenv.MustHaveSymlink(t)
+
+       tmpDir, err := ioutil.TempDir("", "TestEvalSymlinksAboveRootChdir")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(tmpDir)
+
+       wd, err := os.Getwd()
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.Chdir(wd)
+
+       if err := os.Chdir(tmpDir); err != nil {
+               t.Fatal(err)
+       }
+
+       subdir := filepath.Join("a", "b")
+       if err := os.MkdirAll(subdir, 0777); err != nil {
+               t.Fatal(err)
+       }
+       if err := os.Symlink(subdir, "c"); err != nil {
+               t.Fatal(err)
+       }
+       if err := ioutil.WriteFile(filepath.Join(subdir, "file"), nil, 0666); err != nil {
+               t.Fatal(err)
+       }
+
+       subdir = filepath.Join("d", "e", "f")
+       if err := os.MkdirAll(subdir, 0777); err != nil {
+               t.Fatal(err)
+       }
+       if err := os.Chdir(subdir); err != nil {
+               t.Fatal(err)
+       }
+
+       check := filepath.Join("..", "..", "..", "c", "file")
+       wantSuffix := filepath.Join("a", "b", "file")
+       if resolved, err := filepath.EvalSymlinks(check); err != nil {
+               t.Errorf("EvalSymlinks(%q) failed: %v", check, err)
+       } else if !strings.HasSuffix(resolved, wantSuffix) {
+               t.Errorf("EvalSymlinks(%q) = %q does not end with %q", check, resolved, wantSuffix)
+       } else {
+               t.Logf("EvalSymlinks(%q) = %q", check, resolved)
+       }
+}
index 4b41039e25f7592f6a142e2dc372078f05b980a9..de043c1677ba492a57659bfc1b94f3a4d3c8bdcd 100644 (file)
@@ -44,18 +44,26 @@ func walkSymlinks(path string) (string, error) {
                } else if path[start:end] == ".." {
                        // Back up to previous component if possible.
                        // Note that volLen includes any leading slash.
+
+                       // Set r to the index of the last slash in dest,
+                       // after the volume.
                        var r int
                        for r = len(dest) - 1; r >= volLen; r-- {
                                if os.IsPathSeparator(dest[r]) {
                                        break
                                }
                        }
-                       if r < volLen {
+                       if r < volLen || dest[r+1:] == ".." {
+                               // Either path has no slashes
+                               // (it's empty or just "C:")
+                               // or it ends in a ".." we had to keep.
+                               // Either way, keep this "..".
                                if len(dest) > volLen {
                                        dest += pathSeparator
                                }
                                dest += ".."
                        } else {
+                               // Discard everything since the last slash.
                                dest = dest[:r]
                        }
                        continue
index 6b308e073b980973973b05ff2972bf551d008780..629cf2f04fbe6a36bca17c52c30478f93fc27f56 100644 (file)
@@ -18,6 +18,7 @@ func init() {
        register("GCFairness2", GCFairness2)
        register("GCSys", GCSys)
        register("GCPhys", GCPhys)
+       register("DeferLiveness", DeferLiveness)
 }
 
 func GCSys() {
@@ -210,3 +211,25 @@ func GCPhys() {
        fmt.Println("OK")
        runtime.KeepAlive(saved)
 }
+
+// Test that defer closure is correctly scanned when the stack is scanned.
+func DeferLiveness() {
+       var x [10]int
+       escape(&x)
+       fn := func() {
+               if x[0] != 42 {
+                       panic("FAIL")
+               }
+       }
+       defer fn()
+
+       x[0] = 42
+       runtime.GC()
+       runtime.GC()
+       runtime.GC()
+}
+
+//go:noinline
+func escape(x interface{}) { sink2 = x; sink2 = nil }
+
+var sink2 interface{}
index d29721b7f3586bd0c7e48a7d03dda03c0f3e1331..8d39e3478d7e839a07fbd2df97bad8096fe433b4 100644 (file)
@@ -578,6 +578,13 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node,
        }
        typ := receiver.Type()
        receiver, isNil := indirect(receiver)
+       if receiver.Kind() == reflect.Interface && isNil {
+               // Calling a method on a nil interface can't work. The
+               // MethodByName method call below would panic.
+               s.errorf("nil pointer evaluating %s.%s", typ, fieldName)
+               return zero
+       }
+
        // Unless it's an interface, need to get to a value of type *T to guarantee
        // we see all methods of T and *T.
        ptr := receiver
index 085f7c411daad2f842bf27b6c534dedfb4372169..3cf9496db45c53a17ef9777052a193f08b9a9312 100644 (file)
@@ -58,8 +58,10 @@ type T struct {
        Empty3 interface{}
        Empty4 interface{}
        // Non-empty interfaces.
-       NonEmptyInterface    I
-       NonEmptyInterfacePtS *I
+       NonEmptyInterface         I
+       NonEmptyInterfacePtS      *I
+       NonEmptyInterfaceNil      I
+       NonEmptyInterfaceTypedNil I
        // Stringer.
        Str fmt.Stringer
        Err error
@@ -141,24 +143,25 @@ var tVal = &T{
                {"one": 1, "two": 2},
                {"eleven": 11, "twelve": 12},
        },
-       Empty1:               3,
-       Empty2:               "empty2",
-       Empty3:               []int{7, 8},
-       Empty4:               &U{"UinEmpty"},
-       NonEmptyInterface:    &T{X: "x"},
-       NonEmptyInterfacePtS: &siVal,
-       Str:                  bytes.NewBuffer([]byte("foozle")),
-       Err:                  errors.New("erroozle"),
-       PI:                   newInt(23),
-       PS:                   newString("a string"),
-       PSI:                  newIntSlice(21, 22, 23),
-       BinaryFunc:           func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) },
-       VariadicFunc:         func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") },
-       VariadicFuncInt:      func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") },
-       NilOKFunc:            func(s *int) bool { return s == nil },
-       ErrFunc:              func() (string, error) { return "bla", nil },
-       PanicFunc:            func() string { panic("test panic") },
-       Tmpl:                 Must(New("x").Parse("test template")), // "x" is the value of .X
+       Empty1:                    3,
+       Empty2:                    "empty2",
+       Empty3:                    []int{7, 8},
+       Empty4:                    &U{"UinEmpty"},
+       NonEmptyInterface:         &T{X: "x"},
+       NonEmptyInterfacePtS:      &siVal,
+       NonEmptyInterfaceTypedNil: (*T)(nil),
+       Str:                       bytes.NewBuffer([]byte("foozle")),
+       Err:                       errors.New("erroozle"),
+       PI:                        newInt(23),
+       PS:                        newString("a string"),
+       PSI:                       newIntSlice(21, 22, 23),
+       BinaryFunc:                func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) },
+       VariadicFunc:              func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") },
+       VariadicFuncInt:           func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") },
+       NilOKFunc:                 func(s *int) bool { return s == nil },
+       ErrFunc:                   func() (string, error) { return "bla", nil },
+       PanicFunc:                 func() string { panic("test panic") },
+       Tmpl:                      Must(New("x").Parse("test template")), // "x" is the value of .X
 }
 
 var tSliceOfNil = []*T{nil}
@@ -365,6 +368,7 @@ var execTests = []execTest{
        {".NilOKFunc not nil", "{{call .NilOKFunc .PI}}", "false", tVal, true},
        {".NilOKFunc nil", "{{call .NilOKFunc nil}}", "true", tVal, true},
        {"method on nil value from slice", "-{{range .}}{{.Method1 1234}}{{end}}-", "-1234-", tSliceOfNil, true},
+       {"method on typed nil interface value", "{{.NonEmptyInterfaceTypedNil.Method0}}", "M0", tVal, true},
 
        // Function call builtin.
        {".BinaryFunc", "{{call .BinaryFunc `1` `2`}}", "[1=2]", tVal, true},
@@ -1492,6 +1496,11 @@ func TestExecutePanicDuringCall(t *testing.T) {
                        "{{call .PanicFunc}}", tVal,
                        `template: t:1:2: executing "t" at <call .PanicFunc>: error calling call: test panic`,
                },
+               {
+                       "method call on nil interface",
+                       "{{.NonEmptyInterfaceNil.Method0}}", tVal,
+                       `template: t:1:23: executing "t" at <.NonEmptyInterfaceNil.Method0>: nil pointer evaluating template.I.Method0`,
+               },
        }
        for _, tc := range tests {
                b := new(bytes.Buffer)
diff --git a/libgo/misc/cgo/test/testdata/issue30527.go b/libgo/misc/cgo/test/testdata/issue30527.go
new file mode 100644 (file)
index 0000000..4ea7d31
--- /dev/null
@@ -0,0 +1,14 @@
+// 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.
+
+// Issue 30527: function call rewriting casts untyped
+// constants to int because of ":=" usage.
+
+package cgotest
+
+import "cgotest/issue30527"
+
+func issue30527G() {
+       issue30527.G(nil)
+}
diff --git a/libgo/misc/cgo/test/testdata/issue30527/a.go b/libgo/misc/cgo/test/testdata/issue30527/a.go
new file mode 100644 (file)
index 0000000..eb50147
--- /dev/null
@@ -0,0 +1,19 @@
+// 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 issue30527
+
+import "math"
+
+/*
+#include <inttypes.h>
+
+static void issue30527F(char **p, uint64_t mod, uint32_t unused) {}
+*/
+import "C"
+
+func G(p **C.char) {
+       C.issue30527F(p, math.MaxUint64, 1)
+       C.issue30527F(p, 1<<64-1, Z)
+}
diff --git a/libgo/misc/cgo/test/testdata/issue30527/b.go b/libgo/misc/cgo/test/testdata/issue30527/b.go
new file mode 100644 (file)
index 0000000..87e8255
--- /dev/null
@@ -0,0 +1,11 @@
+// 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 issue30527
+
+const (
+       X = 1 << iota
+       Y
+       Z
+)