libgo patch committed: Update to 1.12.1 release

Ian Lance Taylor iant@golang.org
Mon Mar 18 21:03:00 GMT 2019


This patch updates libgo to the 1.12.1 release.  Bootstrapped and ran
Go testsuite on x86_64-pc-linux-gnu.  Committed to mainline.

Ian
-------------- next part --------------
Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 269713)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -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: libgo/MERGE
===================================================================
--- libgo/MERGE	(revision 269619)
+++ libgo/MERGE	(working copy)
@@ -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: libgo/VERSION
===================================================================
--- libgo/VERSION	(revision 269619)
+++ libgo/VERSION	(working copy)
@@ -1 +1 @@
-go1.12
+go1.12.1
Index: libgo/go/cmd/cgo/ast.go
===================================================================
--- libgo/go/cmd/cgo/ast.go	(revision 269619)
+++ libgo/go/cmd/cgo/ast.go	(working copy)
@@ -200,18 +200,6 @@ func (f *File) saveExprs(x interface{},
 		}
 	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: libgo/go/cmd/cgo/gcc.go
===================================================================
--- libgo/go/cmd/cgo/gcc.go	(revision 269619)
+++ libgo/go/cmd/cgo/gcc.go	(working copy)
@@ -915,21 +915,16 @@ func (p *Package) rewriteCall(f *File, c
 			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) boo
 	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
 			// 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
 		if oldType == nil {
 			tt := *t
 			tt.Go = sub.Go
+			tt.BadPointer = sub.BadPointer
 			typedef[name.Name] = &tt
 		}
 
Index: libgo/go/cmd/cgo/main.go
===================================================================
--- libgo/go/cmd/cgo/main.go	(revision 269619)
+++ libgo/go/cmd/cgo/main.go	(working copy)
@@ -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: libgo/go/cmd/go/internal/cache/default.go
===================================================================
--- libgo/go/cmd/go/internal/cache/default.go	(revision 269619)
+++ libgo/go/cmd/go/internal/cache/default.go	(working copy)
@@ -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: libgo/go/cmd/go/internal/load/test.go
===================================================================
--- libgo/go/cmd/go/internal/load/test.go	(revision 269619)
+++ libgo/go/cmd/go/internal/load/test.go	(working copy)
@@ -129,6 +129,7 @@ func GetTestPackagesFor(p *Package, cove
 		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, cove
 		},
 		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, ptes
 			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, ptes
 				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: libgo/go/cmd/go/internal/modfetch/coderepo.go
===================================================================
--- libgo/go/cmd/go/internal/modfetch/coderepo.go	(revision 269619)
+++ libgo/go/cmd/go/internal/modfetch/coderepo.go	(working copy)
@@ -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) (*Re
 		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 strin
 	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 strin
 		// 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, ve
 	}
 	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: libgo/go/cmd/go/internal/modfetch/coderepo_test.go
===================================================================
--- libgo/go/cmd/go/internal/modfetch/coderepo_test.go	(revision 269619)
+++ libgo/go/cmd/go/internal/modfetch/coderepo_test.go	(working copy)
@@ -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: libgo/go/internal/fmtsort/sort.go
===================================================================
--- libgo/go/internal/fmtsort/sort.go	(revision 269619)
+++ libgo/go/internal/fmtsort/sort.go	(working copy)
@@ -167,7 +167,7 @@ func compare(aVal, bVal reflect.Value) i
 		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: libgo/go/internal/fmtsort/sort_test.go
===================================================================
--- libgo/go/internal/fmtsort/sort_test.go	(revision 269619)
+++ libgo/go/internal/fmtsort/sort_test.go	(working copy)
@@ -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: libgo/go/os/removeall_at.go
===================================================================
--- libgo/go/os/removeall_at.go	(revision 269619)
+++ libgo/go/os/removeall_at.go	(working copy)
@@ -92,7 +92,8 @@ func removeAllFrom(parent *File, path st
 			if IsNotExist(err) {
 				return nil
 			}
-			return err
+			recurseErr = err
+			break
 		}
 
 		names, readErr := file.Readdirnames(request)
Index: libgo/go/os/removeall_test.go
===================================================================
--- libgo/go/os/removeall_test.go	(revision 269619)
+++ libgo/go/os/removeall_test.go	(working copy)
@@ -372,3 +372,33 @@ func TestRemoveAllButReadOnly(t *testing
 		}
 	}
 }
+
+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: libgo/go/path/filepath/path.go
===================================================================
--- libgo/go/path/filepath/path.go	(revision 269619)
+++ libgo/go/path/filepath/path.go	(working copy)
@@ -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: libgo/go/path/filepath/path_test.go
===================================================================
--- libgo/go/path/filepath/path_test.go	(revision 269619)
+++ libgo/go/path/filepath/path_test.go	(working copy)
@@ -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: libgo/go/path/filepath/symlink.go
===================================================================
--- libgo/go/path/filepath/symlink.go	(revision 269619)
+++ libgo/go/path/filepath/symlink.go	(working copy)
@@ -44,18 +44,26 @@ func walkSymlinks(path string) (string,
 		} 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: libgo/go/runtime/testdata/testprog/gc.go
===================================================================
--- libgo/go/runtime/testdata/testprog/gc.go	(revision 269619)
+++ libgo/go/runtime/testdata/testprog/gc.go	(working copy)
@@ -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: libgo/go/text/template/exec.go
===================================================================
--- libgo/go/text/template/exec.go	(revision 269619)
+++ libgo/go/text/template/exec.go	(working copy)
@@ -578,6 +578,13 @@ func (s *state) evalField(dot reflect.Va
 	}
 	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: libgo/go/text/template/exec_test.go
===================================================================
--- libgo/go/text/template/exec_test.go	(revision 269619)
+++ libgo/go/text/template/exec_test.go	(working copy)
@@ -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 *testi
 			"{{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)
Index: libgo/misc/cgo/test/testdata/issue30527/a.go
===================================================================
--- libgo/misc/cgo/test/testdata/issue30527/a.go	(nonexistent)
+++ libgo/misc/cgo/test/testdata/issue30527/a.go	(working copy)
@@ -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)
+}
Index: libgo/misc/cgo/test/testdata/issue30527/b.go
===================================================================
--- libgo/misc/cgo/test/testdata/issue30527/b.go	(nonexistent)
+++ libgo/misc/cgo/test/testdata/issue30527/b.go	(working copy)
@@ -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
+)
Index: libgo/misc/cgo/test/testdata/issue30527.go
===================================================================
--- libgo/misc/cgo/test/testdata/issue30527.go	(nonexistent)
+++ libgo/misc/cgo/test/testdata/issue30527.go	(working copy)
@@ -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)
+}


More information about the Gcc-patches mailing list