From a8b58d84bf4fd9c925a835ba39c1c552383bc61b Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 18 Mar 2019 20:27:59 +0000 Subject: [PATCH] libgo: update to Go 1.12.1 Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/167749 From-SVN: r269780 --- gcc/go/gofrontend/MERGE | 2 +- libgo/MERGE | 2 +- libgo/VERSION | 2 +- libgo/go/cmd/cgo/ast.go | 12 -- libgo/go/cmd/cgo/gcc.go | 64 ++--------- libgo/go/cmd/cgo/main.go | 4 +- libgo/go/cmd/go/internal/cache/default.go | 7 +- libgo/go/cmd/go/internal/load/test.go | 10 ++ libgo/go/cmd/go/internal/modfetch/coderepo.go | 100 ++++++++++++----- .../cmd/go/internal/modfetch/coderepo_test.go | 9 ++ libgo/go/internal/fmtsort/sort.go | 2 +- libgo/go/internal/fmtsort/sort_test.go | 42 ++++++- libgo/go/os/removeall_at.go | 3 +- libgo/go/os/removeall_test.go | 30 +++++ libgo/go/path/filepath/path.go | 11 +- libgo/go/path/filepath/path_test.go | 103 +++++++++++++++++- libgo/go/path/filepath/symlink.go | 10 +- libgo/go/runtime/testdata/testprog/gc.go | 23 ++++ libgo/go/text/template/exec.go | 7 ++ libgo/go/text/template/exec_test.go | 49 +++++---- libgo/misc/cgo/test/testdata/issue30527.go | 14 +++ libgo/misc/cgo/test/testdata/issue30527/a.go | 19 ++++ libgo/misc/cgo/test/testdata/issue30527/b.go | 11 ++ 23 files changed, 398 insertions(+), 138 deletions(-) create mode 100644 libgo/misc/cgo/test/testdata/issue30527.go create mode 100644 libgo/misc/cgo/test/testdata/issue30527/a.go create mode 100644 libgo/misc/cgo/test/testdata/issue30527/b.go diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 13c12e7de53..a09733f8c46 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -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. diff --git a/libgo/MERGE b/libgo/MERGE index 1f0d2d4629a..d456361af0b 100644 --- a/libgo/MERGE +++ b/libgo/MERGE @@ -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. diff --git a/libgo/VERSION b/libgo/VERSION index 18a48c30a2f..f325447817b 100644 --- a/libgo/VERSION +++ b/libgo/VERSION @@ -1 +1 @@ -go1.12 +go1.12.1 diff --git a/libgo/go/cmd/cgo/ast.go b/libgo/go/cmd/cgo/ast.go index 83d727a8a5d..54d6bc2559d 100644 --- a/libgo/go/cmd/cgo/ast.go +++ b/libgo/go/cmd/cgo/ast.go @@ -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 - } - } - } - } - } } diff --git a/libgo/go/cmd/cgo/gcc.go b/libgo/go/cmd/cgo/gcc.go index 7db702f1266..e56207acaa7 100644 --- a/libgo/go/cmd/cgo/gcc.go +++ b/libgo/go/cmd/cgo/gcc.go @@ -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 } diff --git a/libgo/go/cmd/cgo/main.go b/libgo/go/cmd/cgo/main.go index 4a5c0ca2bab..80f35681d75 100644 --- a/libgo/go/cmd/cgo/main.go +++ b/libgo/go/cmd/cgo/main.go @@ -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. diff --git a/libgo/go/cmd/go/internal/cache/default.go b/libgo/go/cmd/go/internal/cache/default.go index f545c147009..7d389c3c1af 100644 --- a/libgo/go/cmd/go/internal/cache/default.go +++ b/libgo/go/cmd/go/internal/cache/default.go @@ -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 } diff --git a/libgo/go/cmd/go/internal/load/test.go b/libgo/go/cmd/go/internal/load/test.go index 5f9daa49578..c0e06676df1 100644 --- a/libgo/go/cmd/go/internal/load/test.go +++ b/libgo/go/cmd/go/internal/load/test.go @@ -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() + } } } diff --git a/libgo/go/cmd/go/internal/modfetch/coderepo.go b/libgo/go/cmd/go/internal/modfetch/coderepo.go index 5018b6d8af7..da9f63fec9c 100644 --- a/libgo/go/cmd/go/internal/modfetch/coderepo.go +++ b/libgo/go/cmd/go/internal/modfetch/coderepo.go @@ -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), "/") diff --git a/libgo/go/cmd/go/internal/modfetch/coderepo_test.go b/libgo/go/cmd/go/internal/modfetch/coderepo_test.go index c93d8dbe442..7a419576ced 100644 --- a/libgo/go/cmd/go/internal/modfetch/coderepo_test.go +++ b/libgo/go/cmd/go/internal/modfetch/coderepo_test.go @@ -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) { diff --git a/libgo/go/internal/fmtsort/sort.go b/libgo/go/internal/fmtsort/sort.go index c959cbee1f8..70a305a3a10 100644 --- a/libgo/go/internal/fmtsort/sort.go +++ b/libgo/go/internal/fmtsort/sort.go @@ -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 } diff --git a/libgo/go/internal/fmtsort/sort_test.go b/libgo/go/internal/fmtsort/sort_test.go index 6b10c775b0e..e060d4bf515 100644 --- a/libgo/go/internal/fmtsort/sort_test.go +++ b/libgo/go/internal/fmtsort/sort_test.go @@ -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 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) + } + } +} diff --git a/libgo/go/os/removeall_at.go b/libgo/go/os/removeall_at.go index abdcb66c1f8..512a891017d 100644 --- a/libgo/go/os/removeall_at.go +++ b/libgo/go/os/removeall_at.go @@ -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) diff --git a/libgo/go/os/removeall_test.go b/libgo/go/os/removeall_test.go index 9dab0d4bb10..21371d8776a 100644 --- a/libgo/go/os/removeall_test.go +++ b/libgo/go/os/removeall_test.go @@ -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) + } +} diff --git a/libgo/go/path/filepath/path.go b/libgo/go/path/filepath/path.go index bbb90306a7f..aba1717e7d5 100644 --- a/libgo/go/path/filepath/path.go +++ b/libgo/go/path/filepath/path.go @@ -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 { diff --git a/libgo/go/path/filepath/path_test.go b/libgo/go/path/filepath/path_test.go index deeb47ed719..22632a0b63a 100644 --- a/libgo/go/path/filepath/path_test.go +++ b/libgo/go/path/filepath/path_test.go @@ -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) + } +} diff --git a/libgo/go/path/filepath/symlink.go b/libgo/go/path/filepath/symlink.go index 4b41039e25f..de043c1677b 100644 --- a/libgo/go/path/filepath/symlink.go +++ b/libgo/go/path/filepath/symlink.go @@ -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 diff --git a/libgo/go/runtime/testdata/testprog/gc.go b/libgo/go/runtime/testdata/testprog/gc.go index 6b308e073b9..629cf2f04fb 100644 --- a/libgo/go/runtime/testdata/testprog/gc.go +++ b/libgo/go/runtime/testdata/testprog/gc.go @@ -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{} diff --git a/libgo/go/text/template/exec.go b/libgo/go/text/template/exec.go index d29721b7f35..8d39e3478d7 100644 --- a/libgo/go/text/template/exec.go +++ b/libgo/go/text/template/exec.go @@ -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 diff --git a/libgo/go/text/template/exec_test.go b/libgo/go/text/template/exec_test.go index 085f7c411da..3cf9496db45 100644 --- a/libgo/go/text/template/exec_test.go +++ b/libgo/go/text/template/exec_test.go @@ -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 : 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 index 00000000000..4ea7d3177a5 --- /dev/null +++ b/libgo/misc/cgo/test/testdata/issue30527.go @@ -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 index 00000000000..eb50147b39f --- /dev/null +++ b/libgo/misc/cgo/test/testdata/issue30527/a.go @@ -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 + +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 index 00000000000..87e8255bd8d --- /dev/null +++ b/libgo/misc/cgo/test/testdata/issue30527/b.go @@ -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 +) -- 2.30.2