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
merge done from the gofrontend repository.
05e77d41914d247a1e7caf37d7125ccaa5a53505
0380c9ad38843d523d9c9804fe300cb7edd7cd3c
The first line of this file holds the git revision number of the
last merge done from the master library sources.
......@@ -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
}
}
}
}
}
}
......
......@@ -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
}
......
......@@ -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.
......
......@@ -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
}
......
......@@ -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()
}
}
}
......
......@@ -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), "/")
......
......@@ -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) {
......
......@@ -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
}
......
......@@ -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)
}
}
}
......@@ -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)
......
......@@ -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 {
}
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 {
......
......@@ -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)
}
}
......@@ -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
......
......@@ -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{}
......@@ -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
......
......@@ -58,8 +58,10 @@ type T struct {
Empty3 interface{}
Empty4 interface{}
// Non-empty interfaces.
NonEmptyInterface I
NonEmptyInterfacePtS *I
NonEmptyInterface I
NonEmptyInterfacePtS *I
NonEmptyInterfaceNil I
NonEmptyInterfaceTypedNil I
// Stringer.
Str fmt.Stringer
Err error
......@@ -141,24 +143,25 @@ var tVal = &T{
{"one": 1, "two": 2},
{"eleven": 11, "twelve": 12},
},
Empty1: 3,
Empty2: "empty2",
Empty3: []int{7, 8},
Empty4: &U{"UinEmpty"},
NonEmptyInterface: &T{X: "x"},
NonEmptyInterfacePtS: &siVal,
Str: bytes.NewBuffer([]byte("foozle")),
Err: errors.New("erroozle"),
PI: newInt(23),
PS: newString("a string"),
PSI: newIntSlice(21, 22, 23),
BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) },
VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") },
VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") },
NilOKFunc: func(s *int) bool { return s == nil },
ErrFunc: func() (string, error) { return "bla", nil },
PanicFunc: func() string { panic("test panic") },
Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X
Empty1: 3,
Empty2: "empty2",
Empty3: []int{7, 8},
Empty4: &U{"UinEmpty"},
NonEmptyInterface: &T{X: "x"},
NonEmptyInterfacePtS: &siVal,
NonEmptyInterfaceTypedNil: (*T)(nil),
Str: bytes.NewBuffer([]byte("foozle")),
Err: errors.New("erroozle"),
PI: newInt(23),
PS: newString("a string"),
PSI: newIntSlice(21, 22, 23),
BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) },
VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") },
VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") },
NilOKFunc: func(s *int) bool { return s == nil },
ErrFunc: func() (string, error) { return "bla", nil },
PanicFunc: func() string { panic("test panic") },
Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X
}
var tSliceOfNil = []*T{nil}
......@@ -365,6 +368,7 @@ var execTests = []execTest{
{".NilOKFunc not nil", "{{call .NilOKFunc .PI}}", "false", tVal, true},
{".NilOKFunc nil", "{{call .NilOKFunc nil}}", "true", tVal, true},
{"method on nil value from slice", "-{{range .}}{{.Method1 1234}}{{end}}-", "-1234-", tSliceOfNil, true},
{"method on typed nil interface value", "{{.NonEmptyInterfaceTypedNil.Method0}}", "M0", tVal, true},
// Function call builtin.
{".BinaryFunc", "{{call .BinaryFunc `1` `2`}}", "[1=2]", tVal, true},
......@@ -1492,6 +1496,11 @@ func TestExecutePanicDuringCall(t *testing.T) {
"{{call .PanicFunc}}", tVal,
`template: t:1:2: executing "t" at <call .PanicFunc>: error calling call: test panic`,
},
{
"method call on nil interface",
"{{.NonEmptyInterfaceNil.Method0}}", tVal,
`template: t:1:23: executing "t" at <.NonEmptyInterfaceNil.Method0>: nil pointer evaluating template.I.Method0`,
},
}
for _, tc := range tests {
b := new(bytes.Buffer)
......
// 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