Commit a8b58d84 by Ian Lance Taylor

libgo: update to Go 1.12.1

    
    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/167749

From-SVN: r269780
parent 891cd9e3
cc70be24502faeffefb66fd0abeb7f20a6c7792a 87945b620b2100d33e27f33e6276a4e4e5890659
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
05e77d41914d247a1e7caf37d7125ccaa5a53505 0380c9ad38843d523d9c9804fe300cb7edd7cd3c
The first line of this file holds the git revision number of the The first line of this file holds the git revision number of the
last merge done from the master library sources. last merge done from the master library sources.
...@@ -200,18 +200,6 @@ func (f *File) saveExprs(x interface{}, context astContext) { ...@@ -200,18 +200,6 @@ func (f *File) saveExprs(x interface{}, context astContext) {
} }
case *ast.CallExpr: case *ast.CallExpr:
f.saveCall(x, context) 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
}
}
}
}
} }
} }
......
...@@ -915,21 +915,16 @@ func (p *Package) rewriteCall(f *File, call *Call) (string, bool) { ...@@ -915,21 +915,16 @@ func (p *Package) rewriteCall(f *File, call *Call) (string, bool) {
needsUnsafe = true needsUnsafe = true
} }
// Explicitly convert untyped constants to the // Use "var x T = ..." syntax to explicitly convert untyped
// parameter type, to avoid a type mismatch. // constants to the parameter type, to avoid a type mismatch.
if p.isConst(f, arg) { ptype := p.rewriteUnsafe(param.Go)
ptype := p.rewriteUnsafe(param.Go)
if !p.needsPointerCheck(f, param.Go, args[i]) || param.BadPointer {
if ptype != param.Go { if ptype != param.Go {
needsUnsafe = true needsUnsafe = true
} }
arg = &ast.CallExpr{ fmt.Fprintf(&sb, "var _cgo%d %s = %s; ", i,
Fun: ptype, gofmtLine(ptype), gofmtPos(arg, origArg.Pos()))
Args: []ast.Expr{arg},
}
}
if !p.needsPointerCheck(f, param.Go, args[i]) {
fmt.Fprintf(&sb, "_cgo%d := %s; ", i, gofmtPos(arg, origArg.Pos()))
continue continue
} }
...@@ -1272,47 +1267,6 @@ func (p *Package) isType(t ast.Expr) bool { ...@@ -1272,47 +1267,6 @@ func (p *Package) isType(t ast.Expr) bool {
return false 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. // isVariable reports whether x is a variable, possibly with field references.
func (p *Package) isVariable(x ast.Expr) bool { func (p *Package) isVariable(x ast.Expr) bool {
switch x := x.(type) { switch x := x.(type) {
...@@ -2533,13 +2487,16 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { ...@@ -2533,13 +2487,16 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
// Treat this typedef as a uintptr. // Treat this typedef as a uintptr.
s := *sub s := *sub
s.Go = c.uintptr s.Go = c.uintptr
s.BadPointer = true
sub = &s sub = &s
// Make sure we update any previously computed type. // Make sure we update any previously computed type.
if oldType := typedef[name.Name]; oldType != nil { if oldType := typedef[name.Name]; oldType != nil {
oldType.Go = sub.Go oldType.Go = sub.Go
oldType.BadPointer = true
} }
} }
t.Go = name t.Go = name
t.BadPointer = sub.BadPointer
if unionWithPointer[sub.Go] { if unionWithPointer[sub.Go] {
unionWithPointer[t.Go] = true unionWithPointer[t.Go] = true
} }
...@@ -2549,6 +2506,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { ...@@ -2549,6 +2506,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
if oldType == nil { if oldType == nil {
tt := *t tt := *t
tt.Go = sub.Go tt.Go = sub.Go
tt.BadPointer = sub.BadPointer
typedef[name.Name] = &tt typedef[name.Name] = &tt
} }
......
...@@ -71,9 +71,6 @@ type File struct { ...@@ -71,9 +71,6 @@ type File struct {
Edit *edit.Buffer Edit *edit.Buffer
} }
// Untyped constants in the current package.
var consts = make(map[string]bool)
func (f *File) offset(p token.Pos) int { func (f *File) offset(p token.Pos) int {
return fset.Position(p).Offset return fset.Position(p).Offset
} }
...@@ -154,6 +151,7 @@ type Type struct { ...@@ -154,6 +151,7 @@ type Type struct {
Go ast.Expr Go ast.Expr
EnumValues map[string]int64 EnumValues map[string]int64
Typedef string Typedef string
BadPointer bool
} }
// A FuncType collects information about a function type in both the C and Go worlds. // A FuncType collects information about a function type in both the C and Go worlds.
......
...@@ -37,7 +37,7 @@ See golang.org to learn more about Go. ...@@ -37,7 +37,7 @@ See golang.org to learn more about Go.
// the first time Default is called. // the first time Default is called.
func initDefaultCache() { func initDefaultCache() {
dir := DefaultDir() dir := DefaultDir()
if dir == "off" || dir == "" { if dir == "off" {
if defaultDirErr != nil { if defaultDirErr != nil {
base.Fatalf("build cache is required, but could not be located: %v", defaultDirErr) base.Fatalf("build cache is required, but could not be located: %v", defaultDirErr)
} }
...@@ -74,7 +74,12 @@ func DefaultDir() string { ...@@ -74,7 +74,12 @@ func DefaultDir() string {
defaultDirOnce.Do(func() { defaultDirOnce.Do(func() {
defaultDir = os.Getenv("GOCACHE") defaultDir = os.Getenv("GOCACHE")
if filepath.IsAbs(defaultDir) || defaultDir == "off" {
return
}
if defaultDir != "" { if defaultDir != "" {
defaultDir = "off"
defaultDirErr = fmt.Errorf("GOCACHE is not an absolute path")
return return
} }
......
...@@ -129,6 +129,7 @@ func GetTestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Pac ...@@ -129,6 +129,7 @@ func GetTestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Pac
ptest.Internal.Imports = append(imports, p.Internal.Imports...) ptest.Internal.Imports = append(imports, p.Internal.Imports...)
ptest.Internal.RawImports = str.StringList(rawTestImports, p.Internal.RawImports) ptest.Internal.RawImports = str.StringList(rawTestImports, p.Internal.RawImports)
ptest.Internal.ForceLibrary = true ptest.Internal.ForceLibrary = true
ptest.Internal.BuildInfo = ""
ptest.Internal.Build = new(build.Package) ptest.Internal.Build = new(build.Package)
*ptest.Internal.Build = *p.Internal.Build *ptest.Internal.Build = *p.Internal.Build
m := map[string][]token.Position{} m := map[string][]token.Position{}
...@@ -186,6 +187,7 @@ func GetTestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Pac ...@@ -186,6 +187,7 @@ func GetTestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Pac
}, },
Internal: PackageInternal{ Internal: PackageInternal{
Build: &build.Package{Name: "main"}, Build: &build.Package{Name: "main"},
BuildInfo: p.Internal.BuildInfo,
Asmflags: p.Internal.Asmflags, Asmflags: p.Internal.Asmflags,
Gcflags: p.Internal.Gcflags, Gcflags: p.Internal.Gcflags,
Ldflags: p.Internal.Ldflags, Ldflags: p.Internal.Ldflags,
...@@ -352,6 +354,7 @@ func recompileForTest(pmain, preal, ptest, pxtest *Package) { ...@@ -352,6 +354,7 @@ func recompileForTest(pmain, preal, ptest, pxtest *Package) {
copy(p1.Imports, p.Imports) copy(p1.Imports, p.Imports)
p = p1 p = p1
p.Target = "" p.Target = ""
p.Internal.BuildInfo = ""
} }
// Update p.Internal.Imports to use test copies. // Update p.Internal.Imports to use test copies.
...@@ -361,6 +364,13 @@ func recompileForTest(pmain, preal, ptest, pxtest *Package) { ...@@ -361,6 +364,13 @@ func recompileForTest(pmain, preal, ptest, pxtest *Package) {
p.Internal.Imports[i] = p1 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()
}
} }
} }
......
...@@ -23,55 +23,99 @@ import ( ...@@ -23,55 +23,99 @@ import (
type codeRepo struct { type codeRepo struct {
modPath string 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 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 // pathMajor is the suffix of modPath that indicates its major version,
pathPrefix string // or the empty string if modPath is at major version 0 or 1.
pathMajor string //
// 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 pseudoMajor string
} }
func newCodeRepo(code codehost.Repo, root, path string) (Repo, error) { // newCodeRepo returns a Repo that reads the source code for the module with the
if !hasPathPrefix(path, root) { // given path, from the repo stored in code, with the root of the repo
return nil, fmt.Errorf("mismatched repo: found %s for %s", root, path) // 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) pathPrefix, pathMajor, ok := module.SplitPathVersion(path)
if !ok { if !ok {
return nil, fmt.Errorf("invalid module path %q", path) return nil, fmt.Errorf("invalid module path %q", path)
} }
if codeRoot == path {
pathPrefix = path
}
pseudoMajor := "v0" pseudoMajor := "v0"
if pathMajor != "" { if pathMajor != "" {
pseudoMajor = pathMajor[1:] pseudoMajor = pathMajor[1:]
} }
// Compute codeDir = bar, the subdirectory within the repo
// corresponding to the module root.
//
// At this point we might have: // At this point we might have:
// codeRoot = github.com/rsc/foo
// path = github.com/rsc/foo/bar/v2 // path = github.com/rsc/foo/bar/v2
// codeRoot = github.com/rsc/foo
// pathPrefix = github.com/rsc/foo/bar // pathPrefix = github.com/rsc/foo/bar
// pathMajor = /v2 // pathMajor = /v2
// pseudoMajor = v2 // pseudoMajor = v2
// //
// Compute codeDir = bar, the subdirectory within the repo // which gives
// corresponding to the module root. // codeDir = bar
codeDir := strings.Trim(strings.TrimPrefix(pathPrefix, root), "/") //
if strings.HasPrefix(path, "gopkg.in/") { // We know that pathPrefix is a prefix of path, and codeRoot is a prefix of
// But gopkg.in is a special legacy case, in which pathPrefix does not start with codeRoot. // path, but codeRoot may or may not be a prefix of pathPrefix, because
// For example we might have: // codeRoot may be the entire path (in which case codeDir should be empty).
// codeRoot = gopkg.in/yaml.v2 // That occurs in two situations.
// pathPrefix = gopkg.in/yaml //
// pathMajor = .v2 // One is when a go-import meta tag resolves the complete module path,
// pseudoMajor = v2 // including the pathMajor suffix:
// codeDir = pathPrefix (because codeRoot is not a prefix of pathPrefix) // path = nanomsg.org/go/mangos/v2
// Clear codeDir - the module root is the repo root for gopkg.in repos. // codeRoot = nanomsg.org/go/mangos/v2
codeDir = "" // 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{ r := &codeRepo{
modPath: path, modPath: path,
code: code, code: code,
codeRoot: root, codeRoot: codeRoot,
codeDir: codeDir, codeDir: codeDir,
pathPrefix: pathPrefix, pathPrefix: pathPrefix,
pathMajor: pathMajor, pathMajor: pathMajor,
...@@ -149,9 +193,6 @@ func (r *codeRepo) Stat(rev string) (*RevInfo, error) { ...@@ -149,9 +193,6 @@ func (r *codeRepo) Stat(rev string) (*RevInfo, error) {
return r.Latest() return r.Latest()
} }
codeRev := r.revToRev(rev) codeRev := r.revToRev(rev)
if semver.IsValid(codeRev) && r.codeDir != "" {
codeRev = r.codeDir + "/" + codeRev
}
info, err := r.code.Stat(codeRev) info, err := r.code.Stat(codeRev)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -290,7 +331,7 @@ func (r *codeRepo) findDir(version string) (rev, dir string, gomod []byte, err e ...@@ -290,7 +331,7 @@ func (r *codeRepo) findDir(version string) (rev, dir string, gomod []byte, err e
found1 := err1 == nil && isMajor(mpath1, r.pathMajor) found1 := err1 == nil && isMajor(mpath1, r.pathMajor)
var file2 string 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". // Suppose pathMajor is "/v2".
// Either go.mod should claim v2 and v2/go.mod should not exist, // Either go.mod should claim v2 and v2/go.mod should not exist,
// or v2/go.mod should exist and claim v2. Not both. // 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 ...@@ -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 // because of replacement modules. This might be a fork of
// the real module, found at a different path, usable only in // the real module, found at a different path, usable only in
// a replace directive. // 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:]) dir2 := path.Join(r.codeDir, r.pathMajor[1:])
file2 = path.Join(dir2, "go.mod") file2 = path.Join(dir2, "go.mod")
gomod2, err2 := r.code.ReadFile(rev, file2, codehost.MaxGoMod) gomod2, err2 := r.code.ReadFile(rev, file2, codehost.MaxGoMod)
...@@ -418,7 +462,7 @@ func (r *codeRepo) Zip(dst io.Writer, version string) error { ...@@ -418,7 +462,7 @@ func (r *codeRepo) Zip(dst io.Writer, version string) error {
} }
defer dl.Close() defer dl.Close()
if actualDir != "" && !hasPathPrefix(dir, actualDir) { 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), "/") subdir := strings.Trim(strings.TrimPrefix(dir, actualDir), "/")
......
...@@ -323,6 +323,15 @@ var codeRepoTests = []struct { ...@@ -323,6 +323,15 @@ var codeRepoTests = []struct {
time: time.Date(2017, 5, 31, 16, 3, 50, 0, time.UTC), time: time.Date(2017, 5, 31, 16, 3, 50, 0, time.UTC),
gomod: "module gopkg.in/natefinch/lumberjack.v2\n", 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) { func TestCodeRepo(t *testing.T) {
......
...@@ -167,7 +167,7 @@ func compare(aVal, bVal reflect.Value) int { ...@@ -167,7 +167,7 @@ func compare(aVal, bVal reflect.Value) int {
if c, ok := nilCompare(aVal, bVal); ok { if c, ok := nilCompare(aVal, bVal); ok {
return c 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 { if c != 0 {
return c return c
} }
......
...@@ -126,10 +126,6 @@ var sortTests = []sortTest{ ...@@ -126,10 +126,6 @@ var sortTests = []sortTest{
map[[2]int]string{{7, 2}: "72", {7, 1}: "71", {3, 4}: "34"}, map[[2]int]string{{7, 2}: "72", {7, 1}: "71", {3, 4}: "34"},
"[3 4]:34 [7 1]:71 [7 2]:72", "[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 { func sprint(data interface{}) string {
...@@ -210,3 +206,41 @@ func TestOrder(t *testing.T) { ...@@ -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)
}
}
}
...@@ -92,7 +92,8 @@ func removeAllFrom(parent *File, path string) error { ...@@ -92,7 +92,8 @@ func removeAllFrom(parent *File, path string) error {
if IsNotExist(err) { if IsNotExist(err) {
return nil return nil
} }
return err recurseErr = err
break
} }
names, readErr := file.Readdirnames(request) names, readErr := file.Readdirnames(request)
......
...@@ -372,3 +372,33 @@ func TestRemoveAllButReadOnly(t *testing.T) { ...@@ -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)
}
}
...@@ -96,19 +96,14 @@ func Clean(path string) string { ...@@ -96,19 +96,14 @@ func Clean(path string) string {
} }
return originalPath + "." 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]) rooted := os.IsPathSeparator(path[0])
// Invariants: // Invariants:
// reading from path; r is index of next byte to process. // reading from path; r is index of next byte to process.
// writing to out; w is index of next byte to write. // writing to buf; w is index of next byte to write.
// dotdot is index in out where .. must stop, either because // dotdot is index in buf where .. must stop, either because
// it is the leading slash or it is a leading ../../.. prefix. // it is the leading slash or it is a leading ../../.. prefix.
n := len(path)
out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen} out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen}
r, dotdot := 0, 0 r, dotdot := 0, 0
if rooted { if rooted {
......
...@@ -93,9 +93,6 @@ var wincleantests = []PathTest{ ...@@ -93,9 +93,6 @@ var wincleantests = []PathTest{
{`//host/share/foo/../baz`, `\\host\share\baz`}, {`//host/share/foo/../baz`, `\\host\share\baz`},
{`\\a\b\..\c`, `\\a\b\c`}, {`\\a\b\..\c`, `\\a\b\c`},
{`\\a\b`, `\\a\b`}, {`\\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) { func TestClean(t *testing.T) {
...@@ -1417,3 +1414,103 @@ func TestIssue29372(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)
}
}
...@@ -44,18 +44,26 @@ func walkSymlinks(path string) (string, error) { ...@@ -44,18 +44,26 @@ func walkSymlinks(path string) (string, error) {
} else if path[start:end] == ".." { } else if path[start:end] == ".." {
// Back up to previous component if possible. // Back up to previous component if possible.
// Note that volLen includes any leading slash. // Note that volLen includes any leading slash.
// Set r to the index of the last slash in dest,
// after the volume.
var r int var r int
for r = len(dest) - 1; r >= volLen; r-- { for r = len(dest) - 1; r >= volLen; r-- {
if os.IsPathSeparator(dest[r]) { if os.IsPathSeparator(dest[r]) {
break 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 { if len(dest) > volLen {
dest += pathSeparator dest += pathSeparator
} }
dest += ".." dest += ".."
} else { } else {
// Discard everything since the last slash.
dest = dest[:r] dest = dest[:r]
} }
continue continue
......
...@@ -18,6 +18,7 @@ func init() { ...@@ -18,6 +18,7 @@ func init() {
register("GCFairness2", GCFairness2) register("GCFairness2", GCFairness2)
register("GCSys", GCSys) register("GCSys", GCSys)
register("GCPhys", GCPhys) register("GCPhys", GCPhys)
register("DeferLiveness", DeferLiveness)
} }
func GCSys() { func GCSys() {
...@@ -210,3 +211,25 @@ func GCPhys() { ...@@ -210,3 +211,25 @@ func GCPhys() {
fmt.Println("OK") fmt.Println("OK")
runtime.KeepAlive(saved) 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{}
...@@ -578,6 +578,13 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, ...@@ -578,6 +578,13 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node,
} }
typ := receiver.Type() typ := receiver.Type()
receiver, isNil := indirect(receiver) 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 // Unless it's an interface, need to get to a value of type *T to guarantee
// we see all methods of T and *T. // we see all methods of T and *T.
ptr := receiver ptr := receiver
......
...@@ -58,8 +58,10 @@ type T struct { ...@@ -58,8 +58,10 @@ type T struct {
Empty3 interface{} Empty3 interface{}
Empty4 interface{} Empty4 interface{}
// Non-empty interfaces. // Non-empty interfaces.
NonEmptyInterface I NonEmptyInterface I
NonEmptyInterfacePtS *I NonEmptyInterfacePtS *I
NonEmptyInterfaceNil I
NonEmptyInterfaceTypedNil I
// Stringer. // Stringer.
Str fmt.Stringer Str fmt.Stringer
Err error Err error
...@@ -141,24 +143,25 @@ var tVal = &T{ ...@@ -141,24 +143,25 @@ var tVal = &T{
{"one": 1, "two": 2}, {"one": 1, "two": 2},
{"eleven": 11, "twelve": 12}, {"eleven": 11, "twelve": 12},
}, },
Empty1: 3, Empty1: 3,
Empty2: "empty2", Empty2: "empty2",
Empty3: []int{7, 8}, Empty3: []int{7, 8},
Empty4: &U{"UinEmpty"}, Empty4: &U{"UinEmpty"},
NonEmptyInterface: &T{X: "x"}, NonEmptyInterface: &T{X: "x"},
NonEmptyInterfacePtS: &siVal, NonEmptyInterfacePtS: &siVal,
Str: bytes.NewBuffer([]byte("foozle")), NonEmptyInterfaceTypedNil: (*T)(nil),
Err: errors.New("erroozle"), Str: bytes.NewBuffer([]byte("foozle")),
PI: newInt(23), Err: errors.New("erroozle"),
PS: newString("a string"), PI: newInt(23),
PSI: newIntSlice(21, 22, 23), PS: newString("a string"),
BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) }, PSI: newIntSlice(21, 22, 23),
VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") }, BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) },
VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") }, VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") },
NilOKFunc: func(s *int) bool { return s == nil }, VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") },
ErrFunc: func() (string, error) { return "bla", nil }, NilOKFunc: func(s *int) bool { return s == nil },
PanicFunc: func() string { panic("test panic") }, ErrFunc: func() (string, error) { return "bla", nil },
Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X PanicFunc: func() string { panic("test panic") },
Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X
} }
var tSliceOfNil = []*T{nil} var tSliceOfNil = []*T{nil}
...@@ -365,6 +368,7 @@ var execTests = []execTest{ ...@@ -365,6 +368,7 @@ var execTests = []execTest{
{".NilOKFunc not nil", "{{call .NilOKFunc .PI}}", "false", tVal, true}, {".NilOKFunc not nil", "{{call .NilOKFunc .PI}}", "false", tVal, true},
{".NilOKFunc nil", "{{call .NilOKFunc nil}}", "true", 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 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. // Function call builtin.
{".BinaryFunc", "{{call .BinaryFunc `1` `2`}}", "[1=2]", tVal, true}, {".BinaryFunc", "{{call .BinaryFunc `1` `2`}}", "[1=2]", tVal, true},
...@@ -1492,6 +1496,11 @@ func TestExecutePanicDuringCall(t *testing.T) { ...@@ -1492,6 +1496,11 @@ func TestExecutePanicDuringCall(t *testing.T) {
"{{call .PanicFunc}}", tVal, "{{call .PanicFunc}}", tVal,
`template: t:1:2: executing "t" at <call .PanicFunc>: error calling call: test panic`, `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 { for _, tc := range tests {
b := new(bytes.Buffer) b := new(bytes.Buffer)
......
// 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)
}
// 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)
}
// 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
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment