diff options
author | Chris Broadfoot <cbro@golang.org> | 2017-01-19 12:36:22 -0800 |
---|---|---|
committer | Chris Broadfoot <cbro@golang.org> | 2017-01-19 12:36:53 -0800 |
commit | d18087cb25d05b13f43e8bf83d0320e67a1d75a3 (patch) | |
tree | 2b1ae774643671d71b95c5068c95045366fab604 | |
parent | 3de6e96e4b8147f5267a2e8218a7c780b09a434f (diff) | |
parent | 6593d8650da5d19787bea9383dabe94f36fa04be (diff) | |
download | go-d18087cb25d05b13f43e8bf83d0320e67a1d75a3.tar.gz go-d18087cb25d05b13f43e8bf83d0320e67a1d75a3.zip |
[release-branch.go1.8] all: merge master into release-branch.go1.8
6593d8650d go/ast: fix Object's doc comment about Data
c1730ae424 runtime: force workers out before checking mark roots
d10eddcba3 testing: make parallel t.Run safe again
2c8b70eacf crypto/x509: revert SystemCertPool implementation for Windows
fcfd91858b doc/go1.8: document Plan 9 requirements
81a61a96c9 runtime: for plugins, don't add duplicate itabs
f674537cc9 README.md: update and simplify
d8711919db cmd/go: fix bug help message
48d8edb5b2 crypto/tls: disable CBC cipher suites with SHA-256 by default
92ecd78933 cmd/compile: add ZeroWB case in writebarrier
787125abab doc: 2017 is the Year of the Gopher
5b708a6b6a cmd/compile: lvalues are only required for == when calling runtime fns
e83d506714 vendor/golang_org/x/crypto/poly1305: revendor to pick up fix for #18673
76f981c8d8 net/http: skip TestServerHijackGetsBackgroundByte on Plan 9
e395e3246a net/http: skip TestServerHijackGetsBackgroundByte_big on Plan 9
6a3c6c0de8 net/http: add another hijack-after-background-read test
467109bf56 all: test adjustments for the iOS builder
b2a3b54b95 net/http: make sure Hijack's bufio.Reader includes pre-read background byte
593ea3b360 cmd/go, misc: rework cwd handling for iOS tests
0642b8a2f1 syscall: export Fsid.X__val on s390x
4601eae6ba doc/gdb: mention GOTRACEBACK=crash
4c4c5fc7a3 misc/cgo/testplugin: test that types and itabs are unique
22689c4450 reflect: keep makeFuncImpl live across makeFuncStub
9cf06ed6cd cmd/link: only exclude C-only symbols on darwin
9c3630f578 compress/flate: avoid large stack growth in fillDeflate
4f0aac52d9 cmd/go: add comment about SIGUSR2 on iOS
333f764df3 cmd/go, misc: switch from breakpoint to SIGUSR2
39e31d5ec0 doc/go1.8: update timezone database version
08da8201ca misc/cgo/testshared: test that types and itabs are unique
fdde7ba2a2 runtime: avoid clobbering C callee-save register in cgoSigtramp
f65abf6ddc cmd/compile: hide testdclstack behind debug flag
641ef2a733 compress/gzip: skip TestGZIPFilesHaveZeroMTimes on non-builders
0724aa813f crypto/dsa: gofmt
ac05542985 net/http: deflake TestRetryIdempotentRequestsOnError
b842c9aac7 doc: remove inline styles
Change-Id: I642c056732fe1e8081e9d73e086e38ea0b2568cc
60 files changed, 855 insertions, 192 deletions
@@ -5,39 +5,37 @@ reliable, and efficient software. ![Gopher image](doc/gopher/fiveyears.jpg) -For documentation about how to install and use Go, -visit https://golang.org/ or load doc/install-source.html -in your web browser. - Our canonical Git repository is located at https://go.googlesource.com/go. There is a mirror of the repository at https://github.com/golang/go. -Go is the work of hundreds of contributors. We appreciate your help! +Unless otherwise noted, the Go source files are distributed under the +BSD-style license found in the LICENSE file. -To contribute, please read the contribution guidelines: - https://golang.org/doc/contribute.html +### Download and Install -##### Note that we do not accept pull requests and that we use the issue tracker for bug reports and proposals only. Please ask questions on https://forum.golangbridge.org or https://groups.google.com/forum/#!forum/golang-nuts. +#### Binary Distributions -Unless otherwise noted, the Go source files are distributed -under the BSD-style license found in the LICENSE file. +Official binary distributions are available at https://golang.org/dl/. --- +After downloading a binary release, visit https://golang.org/doc/install +or load doc/install.html in your web browser for installation +instructions. -## Binary Distribution Notes +#### Install From Source -If you have just untarred a binary Go distribution, you need to set -the environment variable $GOROOT to the full path of the go -directory (the one containing this file). You can omit the -variable if you unpack it into /usr/local/go, or if you rebuild -from sources by running all.bash (see doc/install-source.html). -You should also add the Go binary directory $GOROOT/bin -to your shell's path. +If a binary distribution is not available for your combination of +operating system and architecture, visit +https://golang.org/doc/install/source or load doc/install-source.html +in your web browser for source installation instructions. -For example, if you extracted the tar file into $HOME/go, you might -put the following in your .profile: +### Contributing - export GOROOT=$HOME/go - export PATH=$PATH:$GOROOT/bin +Go is the work of hundreds of contributors. We appreciate your help! + +To contribute, please read the contribution guidelines: + https://golang.org/doc/contribute.html -See https://golang.org/doc/install or doc/install.html for more details. +Note that the Go project does not use GitHub pull requests, and that +we use the issue tracker for bug reports and proposals only. See +https://golang.org/wiki/Questions for a list of places to ask +questions about the Go language. diff --git a/doc/contribute.html b/doc/contribute.html index bcf7b25c51..f1a5b27998 100644 --- a/doc/contribute.html +++ b/doc/contribute.html @@ -698,7 +698,7 @@ These files will be periodically updated based on the commit logs. <p>Code that you contribute should use the standard copyright header:</p> <pre> -// Copyright 2016 The Go Authors. All rights reserved. +// Copyright 2017 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. </pre> diff --git a/doc/debugging_with_gdb.html b/doc/debugging_with_gdb.html index 52a6e76723..f0e65ea291 100644 --- a/doc/debugging_with_gdb.html +++ b/doc/debugging_with_gdb.html @@ -4,7 +4,8 @@ }--> <p><i> -This applies to the <code>gc</code> toolchain. Gccgo has native gdb support. +This applies to the standard toolchain (the <code>gc</code> Go +compiler and tools). Gccgo has native gdb support. Besides this overview you might want to consult the <a href="http://sourceware.org/gdb/current/onlinedocs/gdb/">GDB manual</a>. </i></p> @@ -49,6 +50,14 @@ when debugging, pass the flags <code>-gcflags "-N -l"</code> to the debugged. </p> +<p> +If you want to use gdb to inspect a core dump, you can trigger a dump +on a program crash, on systems that permit it, by setting +<code>GOTRACEBACK=crash</code> in the environment (see the +<a href="/pkg/runtime/#hdr-Environment_Variables"> runtime package +documentation</a> for more info). +</p> + <h3 id="Common_Operations">Common Operations</h3> <ul> @@ -130,7 +139,7 @@ the DWARF code. <p> If you're interested in what the debugging information looks like, run -'<code>objdump -W 6.out</code>' and browse through the <code>.debug_*</code> +'<code>objdump -W a.out</code>' and browse through the <code>.debug_*</code> sections. </p> @@ -377,7 +386,9 @@ $3 = struct hchan<*testing.T> </pre> <p> -That <code>struct hchan<*testing.T></code> is the runtime-internal representation of a channel. It is currently empty, or gdb would have pretty-printed it's contents. +That <code>struct hchan<*testing.T></code> is the +runtime-internal representation of a channel. It is currently empty, +or gdb would have pretty-printed its contents. </p> <p> diff --git a/doc/go1.8.html b/doc/go1.8.html index 2ac478632e..337f13d630 100644 --- a/doc/go1.8.html +++ b/doc/go1.8.html @@ -93,7 +93,8 @@ On OpenBSD, Go now requires OpenBSD 5.9 or later. <!-- CL 34093 --> <p> The Plan 9 port's networking support is now much more complete and matches the behavior of Unix and Windows with respect to deadlines -and cancelation. +and cancelation. For Plan 9 kernel requirements, see the +<a href="https://golang.org/wiki/Plan9">Plan 9 wiki page</a>. </p> <p> @@ -808,11 +809,6 @@ Optimizations and minor bug fixes are not listed. <dl id="crypto_x509"><dt><a href="/pkg/crypto/x509/">crypto/x509</a></dt> <dd> - <p> <!-- CL 30578 --> - <a href="/pkg/crypto/x509/#SystemCertPool"><code>SystemCertPool</code></a> - is now implemented on Windows. - </p> - <p> <!-- CL 24743 --> PSS signatures are now supported. </p> @@ -1617,9 +1613,9 @@ crypto/x509: return error for missing SerialNumber (CL 27238) June 31 and July 32. </p> - <p> <!-- CL 33029 --> + <p> <!-- CL 33029 --> <!-- CL 34816 --> The <code>tzdata</code> database has been updated to version - 2016i for systems that don't already have a local time zone + 2016j for systems that don't already have a local time zone database. </p> diff --git a/doc/install.html b/doc/install.html index 2143d591cb..6bff75c5a0 100644 --- a/doc/install.html +++ b/doc/install.html @@ -250,7 +250,7 @@ $ <b>cd $HOME/go/src/hello</b> $ <b>go build</b> </pre> -<pre class="testWindows" style="display: none"> +<pre class="testWindows"> C:\> <b>cd %USERPROFILE%\go\src\hello</b> C:\Users\Gopher\go\src\hello> <b>go build</b> </pre> @@ -267,7 +267,7 @@ $ <b>./hello</b> hello, world </pre> -<pre class="testWindows" style="display: none"> +<pre class="testWindows"> C:\Users\Gopher\go\src\hello> <b>hello</b> hello, world </pre> diff --git a/misc/cgo/testplugin/src/iface/main.go b/misc/cgo/testplugin/src/iface/main.go new file mode 100644 index 0000000000..5e7e4d8b48 --- /dev/null +++ b/misc/cgo/testplugin/src/iface/main.go @@ -0,0 +1,46 @@ +// Copyright 2017 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 main + +import ( + "iface_i" + "log" + "plugin" +) + +func main() { + a, err := plugin.Open("iface_a.so") + if err != nil { + log.Fatalf(`plugin.Open("iface_a.so"): %v`, err) + } + b, err := plugin.Open("iface_b.so") + if err != nil { + log.Fatalf(`plugin.Open("iface_b.so"): %v`, err) + } + + af, err := a.Lookup("F") + if err != nil { + log.Fatalf(`a.Lookup("F") failed: %v`, err) + } + bf, err := b.Lookup("F") + if err != nil { + log.Fatalf(`b.Lookup("F") failed: %v`, err) + } + if af.(func() interface{})() != bf.(func() interface{})() { + panic("empty interfaces not equal") + } + + ag, err := a.Lookup("G") + if err != nil { + log.Fatalf(`a.Lookup("G") failed: %v`, err) + } + bg, err := b.Lookup("G") + if err != nil { + log.Fatalf(`b.Lookup("G") failed: %v`, err) + } + if ag.(func() iface_i.I)() != bg.(func() iface_i.I)() { + panic("nonempty interfaces not equal") + } +} diff --git a/misc/cgo/testplugin/src/iface_a/a.go b/misc/cgo/testplugin/src/iface_a/a.go new file mode 100644 index 0000000000..29d2e27764 --- /dev/null +++ b/misc/cgo/testplugin/src/iface_a/a.go @@ -0,0 +1,17 @@ +// Copyright 2017 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 main + +import "iface_i" + +//go:noinline +func F() interface{} { + return (*iface_i.T)(nil) +} + +//go:noinline +func G() iface_i.I { + return (*iface_i.T)(nil) +} diff --git a/misc/cgo/testplugin/src/iface_b/b.go b/misc/cgo/testplugin/src/iface_b/b.go new file mode 100644 index 0000000000..29d2e27764 --- /dev/null +++ b/misc/cgo/testplugin/src/iface_b/b.go @@ -0,0 +1,17 @@ +// Copyright 2017 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 main + +import "iface_i" + +//go:noinline +func F() interface{} { + return (*iface_i.T)(nil) +} + +//go:noinline +func G() iface_i.I { + return (*iface_i.T)(nil) +} diff --git a/misc/cgo/testplugin/src/iface_i/i.go b/misc/cgo/testplugin/src/iface_i/i.go new file mode 100644 index 0000000000..31c80387c7 --- /dev/null +++ b/misc/cgo/testplugin/src/iface_i/i.go @@ -0,0 +1,17 @@ +// Copyright 2017 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 iface_i + +type I interface { + M() +} + +type T struct { +} + +func (t *T) M() { +} + +// *T implements I diff --git a/misc/cgo/testplugin/src/issue18676/dynamodbstreamsevt/definition.go b/misc/cgo/testplugin/src/issue18676/dynamodbstreamsevt/definition.go new file mode 100644 index 0000000000..70fd054d08 --- /dev/null +++ b/misc/cgo/testplugin/src/issue18676/dynamodbstreamsevt/definition.go @@ -0,0 +1,13 @@ +// Copyright 2017 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 dynamodbstreamsevt + +import "encoding/json" + +var foo json.RawMessage + +type Event struct{} + +func (e *Event) Dummy() {} diff --git a/misc/cgo/testplugin/src/issue18676/main.go b/misc/cgo/testplugin/src/issue18676/main.go new file mode 100644 index 0000000000..c75409dafe --- /dev/null +++ b/misc/cgo/testplugin/src/issue18676/main.go @@ -0,0 +1,31 @@ +// Copyright 2017 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. + +// The bug happened like this: +// 1) The main binary adds an itab for *json.UnsupportedValueError / error +// (concrete type / interface type). This itab goes in hash bucket 0x111. +// 2) The plugin adds that same itab again. That makes a cycle in the itab +// chain rooted at hash bucket 0x111. +// 3) The main binary then asks for the itab for *dynamodbstreamsevt.Event / +// json.Unmarshaler. This itab happens to also live in bucket 0x111. +// The lookup code goes into an infinite loop searching for this itab. +// The code is carefully crafted so that the two itabs are both from the +// same bucket, and so that the second itab doesn't exist in +// the itab hashmap yet (so the entire linked list must be searched). +package main + +import ( + "encoding/json" + "issue18676/dynamodbstreamsevt" + "plugin" +) + +func main() { + plugin.Open("plugin.so") + + var x interface{} = (*dynamodbstreamsevt.Event)(nil) + if _, ok := x.(json.Unmarshaler); !ok { + println("something") + } +} diff --git a/misc/cgo/testplugin/src/issue18676/plugin.go b/misc/cgo/testplugin/src/issue18676/plugin.go new file mode 100644 index 0000000000..8a3b85a75c --- /dev/null +++ b/misc/cgo/testplugin/src/issue18676/plugin.go @@ -0,0 +1,11 @@ +// Copyright 2017 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 main + +import "C" + +import "issue18676/dynamodbstreamsevt" + +func F(evt *dynamodbstreamsevt.Event) {} diff --git a/misc/cgo/testplugin/test.bash b/misc/cgo/testplugin/test.bash index fee99a758c..ab7430acc3 100755 --- a/misc/cgo/testplugin/test.bash +++ b/misc/cgo/testplugin/test.bash @@ -15,8 +15,8 @@ goos=$(go env GOOS) goarch=$(go env GOARCH) function cleanup() { - rm -f plugin*.so unnamed*.so - rm -rf host pkg sub + rm -f plugin*.so unnamed*.so iface*.so + rm -rf host pkg sub iface issue18676 } trap cleanup EXIT @@ -32,3 +32,15 @@ GOPATH=$(pwd) go build -buildmode=plugin unnamed2.go GOPATH=$(pwd) go build host LD_LIBRARY_PATH=$(pwd) ./host + +# Test that types and itabs get properly uniqified. +GOPATH=$(pwd) go build -buildmode=plugin iface_a +GOPATH=$(pwd) go build -buildmode=plugin iface_b +GOPATH=$(pwd) go build iface +LD_LIBRARY_PATH=$(pwd) ./iface + +# Test for issue 18676 - make sure we don't add the same itab twice. +# The buggy code hangs forever, so use a timeout to check for that. +GOPATH=$(pwd) go build -buildmode=plugin -o plugin.so src/issue18676/plugin.go +GOPATH=$(pwd) go build -o issue18676 src/issue18676/main.go +timeout 10s ./issue18676 diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go index af4f91550f..f0766e511e 100644 --- a/misc/cgo/testshared/shared_test.go +++ b/misc/cgo/testshared/shared_test.go @@ -815,3 +815,14 @@ func TestImplicitInclusion(t *testing.T) { goCmd(t, "install", "-linkshared", "implicitcmd") run(t, "running executable linked against library that contains same package as it", "./bin/implicitcmd") } + +// Tests to make sure that the type fields of empty interfaces and itab +// fields of nonempty interfaces are unique even across modules, +// so that interface equality works correctly. +func TestInterface(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "iface_a") + // Note: iface_i gets installed implicitly as a dependency of iface_a. + goCmd(t, "install", "-buildmode=shared", "-linkshared", "iface_b") + goCmd(t, "install", "-linkshared", "iface") + run(t, "running type/itab uniqueness tester", "./bin/iface") +} diff --git a/misc/cgo/testshared/src/iface/main.go b/misc/cgo/testshared/src/iface/main.go new file mode 100644 index 0000000000..3d5b54e73b --- /dev/null +++ b/misc/cgo/testshared/src/iface/main.go @@ -0,0 +1,17 @@ +// Copyright 2017 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 main + +import "iface_a" +import "iface_b" + +func main() { + if iface_a.F() != iface_b.F() { + panic("empty interfaces not equal") + } + if iface_a.G() != iface_b.G() { + panic("non-empty interfaces not equal") + } +} diff --git a/misc/cgo/testshared/src/iface_a/a.go b/misc/cgo/testshared/src/iface_a/a.go new file mode 100644 index 0000000000..e11047c166 --- /dev/null +++ b/misc/cgo/testshared/src/iface_a/a.go @@ -0,0 +1,17 @@ +// Copyright 2017 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 iface_a + +import "iface_i" + +//go:noinline +func F() interface{} { + return (*iface_i.T)(nil) +} + +//go:noinline +func G() iface_i.I { + return (*iface_i.T)(nil) +} diff --git a/misc/cgo/testshared/src/iface_b/b.go b/misc/cgo/testshared/src/iface_b/b.go new file mode 100644 index 0000000000..47aee2e77e --- /dev/null +++ b/misc/cgo/testshared/src/iface_b/b.go @@ -0,0 +1,17 @@ +// Copyright 2017 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 iface_b + +import "iface_i" + +//go:noinline +func F() interface{} { + return (*iface_i.T)(nil) +} + +//go:noinline +func G() iface_i.I { + return (*iface_i.T)(nil) +} diff --git a/misc/cgo/testshared/src/iface_i/i.go b/misc/cgo/testshared/src/iface_i/i.go new file mode 100644 index 0000000000..31c80387c7 --- /dev/null +++ b/misc/cgo/testshared/src/iface_i/i.go @@ -0,0 +1,17 @@ +// Copyright 2017 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 iface_i + +type I interface { + M() +} + +type T struct { +} + +func (t *T) M() { +} + +// *T implements I diff --git a/misc/ios/go_darwin_arm_exec.go b/misc/ios/go_darwin_arm_exec.go index 1eeb289c7d..3de341b9c5 100644 --- a/misc/ios/go_darwin_arm_exec.go +++ b/misc/ios/go_darwin_arm_exec.go @@ -99,7 +99,7 @@ func main() { // Approximately 1 in a 100 binaries fail to start. If it happens, // try again. These failures happen for several reasons beyond // our control, but all of them are safe to retry as they happen - // before lldb encounters the initial getwd breakpoint. As we + // before lldb encounters the initial SIGUSR2 stop. As we // know the tests haven't started, we are not hiding flaky tests // with this retry. for i := 0; i < 5; i++ { @@ -204,6 +204,11 @@ func run(bin string, args []string) (err error) { var opts options opts, args = parseArgs(args) + // Pass the suffix for the current working directory as the + // first argument to the test. For iOS, cmd/go generates + // special handling of this argument. + args = append([]string{"cwdSuffix=" + pkgpath}, args...) + // ios-deploy invokes lldb to give us a shell session with the app. s, err := newSession(appdir, args, opts) if err != nil { @@ -224,6 +229,7 @@ func run(bin string, args []string) (err error) { s.do(`process handle SIGHUP --stop false --pass true --notify false`) s.do(`process handle SIGPIPE --stop false --pass true --notify false`) s.do(`process handle SIGUSR1 --stop false --pass true --notify false`) + s.do(`process handle SIGUSR2 --stop true --pass false --notify true`) // sent by test harness s.do(`process handle SIGCONT --stop false --pass true --notify false`) s.do(`process handle SIGSEGV --stop false --pass true --notify false`) // does not work s.do(`process handle SIGBUS --stop false --pass true --notify false`) // does not work @@ -236,20 +242,9 @@ func run(bin string, args []string) (err error) { return nil } - s.do(`breakpoint set -n getwd`) // in runtime/cgo/gcc_darwin_arm.go - started = true - s.doCmd("run", "stop reason = breakpoint", 20*time.Second) - - // Move the current working directory into the faux gopath. - if pkgpath != "src" { - s.do(`breakpoint delete 1`) - s.do(`expr char* $mem = (char*)malloc(512)`) - s.do(`expr $mem = (char*)getwd($mem, 512)`) - s.do(`expr $mem = (char*)strcat($mem, "/` + pkgpath + `")`) - s.do(`call (void)chdir($mem)`) - } + s.doCmd("run", "stop reason = signal SIGUSR2", 20*time.Second) startTestsLen := s.out.Len() fmt.Fprintln(s.in, `process continue`) @@ -520,13 +515,11 @@ func copyLocalData(dstbase string) (pkgpath string, err error) { // Copy timezone file. // - // Typical apps have the zoneinfo.zip in the root of their app bundle, + // Apps have the zoneinfo.zip in the root of their app bundle, // read by the time package as the working directory at initialization. - // As we move the working directory to the GOROOT pkg directory, we - // install the zoneinfo.zip file in the pkgpath. if underGoRoot { err := cp( - filepath.Join(dstbase, pkgpath), + dstbase, filepath.Join(cwd, "lib", "time", "zoneinfo.zip"), ) if err != nil { diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go index 8113710e39..d4f3d9884e 100644 --- a/src/cmd/compile/internal/gc/alg.go +++ b/src/cmd/compile/internal/gc/alg.go @@ -303,7 +303,9 @@ func genhash(sym *Sym, t *Type) { typecheckslice(fn.Nbody.Slice(), Etop) Curfn = nil popdcl() - testdclstack() + if debug_dclstack != 0 { + testdclstack() + } // Disable safemode while compiling this code: the code we // generate internally can refer to unsafe.Pointer. @@ -493,7 +495,9 @@ func geneq(sym *Sym, t *Type) { typecheckslice(fn.Nbody.Slice(), Etop) Curfn = nil popdcl() - testdclstack() + if debug_dclstack != 0 { + testdclstack() + } // Disable safemode while compiling this code: the code we // generate internally can refer to unsafe.Pointer. diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go index 1d668412a1..94c1184138 100644 --- a/src/cmd/compile/internal/gc/bimport.go +++ b/src/cmd/compile/internal/gc/bimport.go @@ -217,7 +217,9 @@ func Import(in *bufio.Reader) { typecheckok = tcok resumecheckwidth() - testdclstack() // debugging only + if debug_dclstack != 0 { + testdclstack() + } } func formatErrorf(format string, args ...interface{}) { diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 75f58a731c..1690944b3d 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -30,11 +30,12 @@ var ( ) var ( - Debug_append int - Debug_closure int - Debug_panic int - Debug_slice int - Debug_wb int + Debug_append int + Debug_closure int + debug_dclstack int + Debug_panic int + Debug_slice int + Debug_wb int ) // Debug arguments. @@ -48,6 +49,7 @@ var debugtab = []struct { {"append", &Debug_append}, // print information about append compilation {"closure", &Debug_closure}, // print information about closure compilation {"disablenil", &disable_checknil}, // disable nil checks + {"dclstack", &debug_dclstack}, // run internal dclstack checks {"gcprog", &Debug_gcprog}, // print dump of GC programs {"nil", &Debug_checknil}, // print information about nil checks {"panic", &Debug_panic}, // do not hide any compiler panic @@ -325,7 +327,6 @@ func Main() { timings.Stop() timings.AddEvent(int64(lexlineno-lexlineno0), "lines") - testdclstack() mkpackage(localpkg.Name) // final import not used checks finishUniverse() diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index ca99adea27..ce18297ac3 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -34,6 +34,7 @@ func parseFile(filename string) { } if nsyntaxerrors == 0 { + // Always run testdclstack here, even when debug_dclstack is not set, as a sanity measure. testdclstack() } } diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index 9b9a3f1210..7d008dfa65 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -1833,7 +1833,9 @@ func genwrapper(rcvr *Type, method *Field, newnam *Sym, iface int) { funcbody(fn) Curfn = fn popdcl() - testdclstack() + if debug_dclstack != 0 { + testdclstack() + } // wrappers where T is anonymous (struct or interface) can be duplicated. if rcvr.IsStruct() || rcvr.IsInterface() || rcvr.IsPtr() && rcvr.Elem().IsStruct() { diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index efe2016e46..7c2e2ab442 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -3117,12 +3117,12 @@ func walkcompare(n *Node, init *Nodes) *Node { cmpr = cmpr.Left } - if !islvalue(cmpl) || !islvalue(cmpr) { - Fatalf("arguments of comparison must be lvalues - %v %v", cmpl, cmpr) - } - // Chose not to inline. Call equality function directly. if !inline { + if !islvalue(cmpl) || !islvalue(cmpr) { + Fatalf("arguments of comparison must be lvalues - %v %v", cmpl, cmpr) + } + // eq algs take pointers pl := temp(ptrto(t)) al := nod(OAS, pl, nod(OADDR, cmpl, nil)) diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go index 1eb4d7bb1a..054ba1f85c 100644 --- a/src/cmd/compile/internal/ssa/writebarrier.go +++ b/src/cmd/compile/internal/ssa/writebarrier.go @@ -35,7 +35,7 @@ func writebarrier(f *Func) { valueLoop: for i, v := range b.Values { switch v.Op { - case OpStoreWB, OpMoveWB, OpMoveWBVolatile: + case OpStoreWB, OpMoveWB, OpMoveWBVolatile, OpZeroWB: if IsStackAddr(v.Args[0]) { switch v.Op { case OpStoreWB: diff --git a/src/cmd/go/bug.go b/src/cmd/go/bug.go index cbd258b80b..658f6dabd9 100644 --- a/src/cmd/go/bug.go +++ b/src/cmd/go/bug.go @@ -20,11 +20,10 @@ import ( var cmdBug = &Command{ Run: runBug, UsageLine: "bug", - Short: "print information for bug reports", + Short: "start a bug report", Long: ` -Bug prints information that helps file effective bug reports. - -Bugs may be reported at https://golang.org/issue/new. +Bug opens the default browser and starts a new bug report. +The report includes useful system information. `, } diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index cdb167de75..6482f0fd32 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -894,9 +894,13 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, if buildContext.GOOS == "darwin" { if buildContext.GOARCH == "arm" || buildContext.GOARCH == "arm64" { - t.NeedCgo = true + t.IsIOS = true + t.NeedOS = true } } + if t.TestMain == nil { + t.NeedOS = true + } for _, cp := range pmain.imports { if len(cp.coverVars) > 0 { @@ -1343,7 +1347,8 @@ type testFuncs struct { NeedTest bool ImportXtest bool NeedXtest bool - NeedCgo bool + NeedOS bool + IsIOS bool Cover []coverInfo } @@ -1444,7 +1449,7 @@ var testmainTmpl = template.Must(template.New("main").Parse(` package main import ( -{{if not .TestMain}} +{{if .NeedOS}} "os" {{end}} "testing" @@ -1460,8 +1465,10 @@ import ( _cover{{$i}} {{$p.Package.ImportPath | printf "%q"}} {{end}} -{{if .NeedCgo}} +{{if .IsIOS}} + "os/signal" _ "runtime/cgo" + "syscall" {{end}} ) @@ -1523,6 +1530,32 @@ func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts {{end}} func main() { +{{if .IsIOS}} + // Send a SIGUSR2, which will be intercepted by LLDB to + // tell the test harness that installation was successful. + // See misc/ios/go_darwin_arm_exec.go. + signal.Notify(make(chan os.Signal), syscall.SIGUSR2) + syscall.Kill(0, syscall.SIGUSR2) + signal.Reset(syscall.SIGUSR2) + + // The first argument supplied to an iOS test is an offset + // suffix for the current working directory. + // Process it here, and remove it from os.Args. + const hdr = "cwdSuffix=" + if len(os.Args) < 2 || len(os.Args[1]) <= len(hdr) || os.Args[1][:len(hdr)] != hdr { + panic("iOS test not passed a working directory suffix") + } + suffix := os.Args[1][len(hdr):] + dir, err := os.Getwd() + if err != nil { + panic(err) + } + if err := os.Chdir(dir + "/" + suffix); err != nil { + panic(err) + } + os.Args = append([]string{os.Args[0]}, os.Args[2:]...) +{{end}} + {{if .CoverEnabled}} testing.RegisterCover(testing.Cover{ Mode: {{printf "%q" .CoverMode}}, diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go index 1ebd7de662..479425f211 100644 --- a/src/cmd/link/internal/ld/pcln.go +++ b/src/cmd/link/internal/ld/pcln.go @@ -168,7 +168,7 @@ func container(s *Symbol) int { if s == nil { return 0 } - if Buildmode == BuildmodePlugin && onlycsymbol(s) { + if Buildmode == BuildmodePlugin && Headtype == obj.Hdarwin && onlycsymbol(s) { return 1 } // We want to generate func table entries only for the "lowest level" symbols, diff --git a/src/compress/bzip2/bzip2_test.go b/src/compress/bzip2/bzip2_test.go index a6c3080db3..95fb189585 100644 --- a/src/compress/bzip2/bzip2_test.go +++ b/src/compress/bzip2/bzip2_test.go @@ -204,12 +204,6 @@ func TestMTF(t *testing.T) { } } -var ( - digits = mustLoadFile("testdata/e.txt.bz2") - twain = mustLoadFile("testdata/Mark.Twain-Tom.Sawyer.txt.bz2") - random = mustLoadFile("testdata/random.data.bz2") -) - func benchmarkDecode(b *testing.B, compressed []byte) { // Determine the uncompressed size of testfile. uncompressedSize, err := io.Copy(ioutil.Discard, NewReader(bytes.NewReader(compressed))) @@ -227,6 +221,18 @@ func benchmarkDecode(b *testing.B, compressed []byte) { } } -func BenchmarkDecodeDigits(b *testing.B) { benchmarkDecode(b, digits) } -func BenchmarkDecodeTwain(b *testing.B) { benchmarkDecode(b, twain) } -func BenchmarkDecodeRand(b *testing.B) { benchmarkDecode(b, random) } +func BenchmarkDecodeDigits(b *testing.B) { + digits := mustLoadFile("testdata/e.txt.bz2") + b.ResetTimer() + benchmarkDecode(b, digits) +} +func BenchmarkDecodeTwain(b *testing.B) { + twain := mustLoadFile("testdata/Mark.Twain-Tom.Sawyer.txt.bz2") + b.ResetTimer() + benchmarkDecode(b, twain) +} +func BenchmarkDecodeRand(b *testing.B) { + random := mustLoadFile("testdata/random.data.bz2") + b.ResetTimer() + benchmarkDecode(b, random) +} diff --git a/src/compress/flate/deflate.go b/src/compress/flate/deflate.go index 97265b3ca2..4d6a5357d8 100644 --- a/src/compress/flate/deflate.go +++ b/src/compress/flate/deflate.go @@ -136,14 +136,17 @@ func (d *compressor) fillDeflate(b []byte) int { delta := d.hashOffset - 1 d.hashOffset -= delta d.chainHead -= delta - for i, v := range d.hashPrev { + + // Iterate over slices instead of arrays to avoid copying + // the entire table onto the stack (Issue #18625). + for i, v := range d.hashPrev[:] { if int(v) > delta { d.hashPrev[i] = uint32(int(v) - delta) } else { d.hashPrev[i] = 0 } } - for i, v := range d.hashHead { + for i, v := range d.hashHead[:] { if int(v) > delta { d.hashHead[i] = uint32(int(v) - delta) } else { diff --git a/src/compress/flate/deflate_test.go b/src/compress/flate/deflate_test.go index 521a260365..fbea761721 100644 --- a/src/compress/flate/deflate_test.go +++ b/src/compress/flate/deflate_test.go @@ -12,6 +12,7 @@ import ( "io" "io/ioutil" "reflect" + "runtime/debug" "sync" "testing" ) @@ -864,3 +865,33 @@ func TestBestSpeedMaxMatchOffset(t *testing.T) { } } } + +func TestMaxStackSize(t *testing.T) { + // This test must not run in parallel with other tests as debug.SetMaxStack + // affects all goroutines. + n := debug.SetMaxStack(1 << 16) + defer debug.SetMaxStack(n) + + var wg sync.WaitGroup + defer wg.Wait() + + b := make([]byte, 1<<20) + for level := HuffmanOnly; level <= BestCompression; level++ { + // Run in separate goroutine to increase probability of stack regrowth. + wg.Add(1) + go func(level int) { + defer wg.Done() + zw, err := NewWriter(ioutil.Discard, level) + if err != nil { + t.Errorf("level %d, NewWriter() = %v, want nil", level, err) + } + if n, err := zw.Write(b); n != len(b) || err != nil { + t.Errorf("level %d, Write() = (%d, %v), want (%d, nil)", level, n, err, len(b)) + } + if err := zw.Close(); err != nil { + t.Errorf("level %d, Close() = %v, want nil", level, err) + } + zw.Reset(ioutil.Discard) + }(level) + } +} diff --git a/src/compress/flate/deflatefast.go b/src/compress/flate/deflatefast.go index a1636a37d6..08298b76bb 100644 --- a/src/compress/flate/deflatefast.go +++ b/src/compress/flate/deflatefast.go @@ -60,7 +60,7 @@ func newDeflateFast() *deflateFast { func (e *deflateFast) encode(dst []token, src []byte) []token { // Ensure that e.cur doesn't wrap. if e.cur > 1<<30 { - *e = deflateFast{cur: maxStoreBlockSize, prev: e.prev[:0]} + e.resetAll() } // This check isn't in the Snappy implementation, but there, the caller @@ -265,6 +265,21 @@ func (e *deflateFast) reset() { // Protect against e.cur wraparound. if e.cur > 1<<30 { - *e = deflateFast{cur: maxStoreBlockSize, prev: e.prev[:0]} + e.resetAll() + } +} + +// resetAll resets the deflateFast struct and is only called in rare +// situations to prevent integer overflow. It manually resets each field +// to avoid causing large stack growth. +// +// See https://golang.org/issue/18636. +func (e *deflateFast) resetAll() { + // This is equivalent to: + // *e = deflateFast{cur: maxStoreBlockSize, prev: e.prev[:0]} + e.cur = maxStoreBlockSize + e.prev = e.prev[:0] + for i := range e.table { + e.table[i] = tableEntry{} } } diff --git a/src/compress/gzip/issue14937_test.go b/src/compress/gzip/issue14937_test.go index e76d47cc4e..30c1390dfd 100644 --- a/src/compress/gzip/issue14937_test.go +++ b/src/compress/gzip/issue14937_test.go @@ -9,11 +9,17 @@ import ( "testing" ) -// Per golang.org/issue/14937, check that every .gz file -// in the tree has a zero mtime. +// TestGZIPFilesHaveZeroMTimes checks that every .gz file in the tree +// has a zero MTIME. This is a requirement for the Debian maintainers +// to be able to have deterministic packages. +// +// See https://golang.org/issue/14937. func TestGZIPFilesHaveZeroMTimes(t *testing.T) { - if testing.Short() && testenv.Builder() == "" { - t.Skip("skipping in short mode") + // To avoid spurious false positives due to untracked GZIP files that + // may be in the user's GOROOT (Issue 18604), we only run this test on + // the builders, which should have a clean checkout of the tree. + if testenv.Builder() == "" { + t.Skip("skipping test on non-builder") } goroot, err := filepath.EvalSymlinks(runtime.GOROOT()) if err != nil { diff --git a/src/crypto/dsa/dsa_test.go b/src/crypto/dsa/dsa_test.go index b89aeaebea..8600059f03 100644 --- a/src/crypto/dsa/dsa_test.go +++ b/src/crypto/dsa/dsa_test.go @@ -95,7 +95,7 @@ func TestSignAndVerify(t *testing.T) { func TestSigningWithDegenerateKeys(t *testing.T) { // Signing with degenerate private keys should not cause an infinite // loop. - badKeys := []struct{ + badKeys := []struct { p, q, g, y, x string }{ {"00", "01", "00", "00", "00"}, @@ -105,7 +105,7 @@ func TestSigningWithDegenerateKeys(t *testing.T) { for i, test := range badKeys { priv := PrivateKey{ PublicKey: PublicKey{ - Parameters: Parameters { + Parameters: Parameters{ P: fromHex(test.p), Q: fromHex(test.q), G: fromHex(test.g), diff --git a/src/crypto/tls/cipher_suites.go b/src/crypto/tls/cipher_suites.go index 05146743ac..beb0f1926d 100644 --- a/src/crypto/tls/cipher_suites.go +++ b/src/crypto/tls/cipher_suites.go @@ -84,15 +84,15 @@ var cipherSuites = []*cipherSuite{ {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM}, {TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, {TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, - {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteDefaultOff, cipherAES, macSHA256, nil}, {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil}, - {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteDefaultOff, cipherAES, macSHA256, nil}, {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil}, {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil}, {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil}, {TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM}, {TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, - {TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12 | suiteDefaultOff, cipherAES, macSHA256, nil}, {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil}, {TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil}, {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil}, diff --git a/src/crypto/tls/tls.go b/src/crypto/tls/tls.go index f2e5aea2bc..615d1e5576 100644 --- a/src/crypto/tls/tls.go +++ b/src/crypto/tls/tls.go @@ -6,8 +6,8 @@ package tls // BUG(agl): The crypto/tls package only implements some countermeasures -// against Lucky13 attacks on CBC-mode encryption. See -// http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and +// against Lucky13 attacks on CBC-mode encryption, and only on SHA1 +// variants. See http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and // https://www.imperialviolet.org/2013/02/04/luckythirteen.html. import ( diff --git a/src/crypto/x509/cert_pool.go b/src/crypto/x509/cert_pool.go index fea33df379..71ffbdf0e0 100644 --- a/src/crypto/x509/cert_pool.go +++ b/src/crypto/x509/cert_pool.go @@ -4,7 +4,11 @@ package x509 -import "encoding/pem" +import ( + "encoding/pem" + "errors" + "runtime" +) // CertPool is a set of certificates. type CertPool struct { @@ -26,6 +30,11 @@ func NewCertPool() *CertPool { // Any mutations to the returned pool are not written to disk and do // not affect any other pool. func SystemCertPool() (*CertPool, error) { + if runtime.GOOS == "windows" { + // Issue 16736, 18609: + return nil, errors.New("crypto/x509: system root pool is not available on Windows") + } + return loadSystemRoots() } diff --git a/src/crypto/x509/root_windows.go b/src/crypto/x509/root_windows.go index ca2fba5cb4..a936fec7d8 100644 --- a/src/crypto/x509/root_windows.go +++ b/src/crypto/x509/root_windows.go @@ -226,6 +226,11 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate } func loadSystemRoots() (*CertPool, error) { + // TODO: restore this functionality on Windows. We tried to do + // it in Go 1.8 but had to revert it. See Issue 18609. + // Returning (nil, nil) was the old behavior, prior to CL 30578. + return nil, nil + const CRYPT_E_NOT_FOUND = 0x80092004 store, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("ROOT")) diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go index aa30d85b7d..b085dad90f 100644 --- a/src/crypto/x509/x509_test.go +++ b/src/crypto/x509/x509_test.go @@ -24,6 +24,7 @@ import ( "net" "os/exec" "reflect" + "runtime" "strings" "testing" "time" @@ -1477,6 +1478,9 @@ func TestMultipleRDN(t *testing.T) { } func TestSystemCertPool(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("not implemented on Windows; Issue 16736, 18609") + } _, err := SystemCertPool() if err != nil { t.Fatal(err) diff --git a/src/go/ast/scope.go b/src/go/ast/scope.go index 1ce5e2e84b..a400c7152a 100644 --- a/src/go/ast/scope.go +++ b/src/go/ast/scope.go @@ -70,10 +70,8 @@ func (s *Scope) String() string { // The Data fields contains object-specific data: // // Kind Data type Data value -// Pkg *types.Package package scope +// Pkg *Scope package scope // Con int iota for the respective declaration -// Con != nil constant value -// Typ *Scope (used as method scope during type checking - transient) // type Object struct { Kind ObjKind diff --git a/src/go/doc/doc_test.go b/src/go/doc/doc_test.go index ad8ba5378f..82e63100d4 100644 --- a/src/go/doc/doc_test.go +++ b/src/go/doc/doc_test.go @@ -25,7 +25,7 @@ var files = flag.String("files", "", "consider only Go test files matching this const dataDir = "testdata" -var templateTxt = readTemplate("template.txt") +var templateTxt *template.Template func readTemplate(filename string) *template.Template { t := template.New(filename) @@ -96,6 +96,9 @@ func test(t *testing.T, mode Mode) { if err != nil { t.Fatal(err) } + if templateTxt == nil { + templateTxt = readTemplate("template.txt") + } // test packages for _, pkg := range pkgs { diff --git a/src/go/parser/performance_test.go b/src/go/parser/performance_test.go index f2732c0e2b..b2e1c11e9d 100644 --- a/src/go/parser/performance_test.go +++ b/src/go/parser/performance_test.go @@ -10,17 +10,12 @@ import ( "testing" ) -var src = readFile("parser.go") - -func readFile(filename string) []byte { - data, err := ioutil.ReadFile(filename) +func BenchmarkParse(b *testing.B) { + src, err := ioutil.ReadFile("parser.go") if err != nil { - panic(err) + b.Fatal(err) } - return data -} - -func BenchmarkParse(b *testing.B) { + b.ResetTimer() b.SetBytes(int64(len(src))) for i := 0; i < b.N; i++ { if _, err := ParseFile(token.NewFileSet(), "", src, ParseComments); err != nil { diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go index 072da2552b..681dff193a 100644 --- a/src/net/http/serve_test.go +++ b/src/net/http/serve_test.go @@ -5173,3 +5173,142 @@ func TestServerDuplicateBackgroundRead(t *testing.T) { } wg.Wait() } + +// Test that the bufio.Reader returned by Hijack includes any buffered +// byte (from the Server's backgroundRead) in its buffer. We want the +// Handler code to be able to tell that a byte is available via +// bufio.Reader.Buffered(), without resorting to Reading it +// (potentially blocking) to get at it. +func TestServerHijackGetsBackgroundByte(t *testing.T) { + if runtime.GOOS == "plan9" { + t.Skip("skipping test; see https://golang.org/issue/18657") + } + setParallel(t) + defer afterTest(t) + done := make(chan struct{}) + inHandler := make(chan bool, 1) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + defer close(done) + + // Tell the client to send more data after the GET request. + inHandler <- true + + // Wait until the HTTP server sees the extra data + // after the GET request. The HTTP server fires the + // close notifier here, assuming it's a pipelined + // request, as documented. + select { + case <-w.(CloseNotifier).CloseNotify(): + case <-time.After(5 * time.Second): + t.Error("timeout") + return + } + + conn, buf, err := w.(Hijacker).Hijack() + if err != nil { + t.Error(err) + return + } + defer conn.Close() + n := buf.Reader.Buffered() + if n != 1 { + t.Errorf("buffered data = %d; want 1", n) + } + peek, err := buf.Reader.Peek(3) + if string(peek) != "foo" || err != nil { + t.Errorf("Peek = %q, %v; want foo, nil", peek, err) + } + })) + defer ts.Close() + + cn, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer cn.Close() + if _, err := cn.Write([]byte("GET / HTTP/1.1\r\nHost: e.com\r\n\r\n")); err != nil { + t.Fatal(err) + } + <-inHandler + if _, err := cn.Write([]byte("foo")); err != nil { + t.Fatal(err) + } + + if err := cn.(*net.TCPConn).CloseWrite(); err != nil { + t.Fatal(err) + } + select { + case <-done: + case <-time.After(2 * time.Second): + t.Error("timeout") + } +} + +// Like TestServerHijackGetsBackgroundByte above but sending a +// immediate 1MB of data to the server to fill up the server's 4KB +// buffer. +func TestServerHijackGetsBackgroundByte_big(t *testing.T) { + if runtime.GOOS == "plan9" { + t.Skip("skipping test; see https://golang.org/issue/18657") + } + setParallel(t) + defer afterTest(t) + done := make(chan struct{}) + const size = 8 << 10 + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + defer close(done) + + // Wait until the HTTP server sees the extra data + // after the GET request. The HTTP server fires the + // close notifier here, assuming it's a pipelined + // request, as documented. + select { + case <-w.(CloseNotifier).CloseNotify(): + case <-time.After(5 * time.Second): + t.Error("timeout") + return + } + + conn, buf, err := w.(Hijacker).Hijack() + if err != nil { + t.Error(err) + return + } + defer conn.Close() + slurp, err := ioutil.ReadAll(buf.Reader) + if err != nil { + t.Error("Copy: %v", err) + } + allX := true + for _, v := range slurp { + if v != 'x' { + allX = false + } + } + if len(slurp) != size { + t.Errorf("read %d; want %d", len(slurp), size) + } else if !allX { + t.Errorf("read %q; want %d 'x'", slurp, size) + } + })) + defer ts.Close() + + cn, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer cn.Close() + if _, err := fmt.Fprintf(cn, "GET / HTTP/1.1\r\nHost: e.com\r\n\r\n%s", + strings.Repeat("x", size)); err != nil { + t.Fatal(err) + } + if err := cn.(*net.TCPConn).CloseWrite(); err != nil { + t.Fatal(err) + } + + select { + case <-done: + case <-time.After(2 * time.Second): + t.Error("timeout") + } +} diff --git a/src/net/http/server.go b/src/net/http/server.go index 96236489bd..df70a15193 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -164,7 +164,7 @@ type Flusher interface { // should always test for this ability at runtime. type Hijacker interface { // Hijack lets the caller take over the connection. - // After a call to Hijack(), the HTTP server library + // After a call to Hijack the HTTP server library // will not do anything else with the connection. // // It becomes the caller's responsibility to manage @@ -174,6 +174,9 @@ type Hijacker interface { // already set, depending on the configuration of the // Server. It is the caller's responsibility to set // or clear those deadlines as needed. + // + // The returned bufio.Reader may contain unprocessed buffered + // data from the client. Hijack() (net.Conn, *bufio.ReadWriter, error) } @@ -293,6 +296,11 @@ func (c *conn) hijackLocked() (rwc net.Conn, buf *bufio.ReadWriter, err error) { rwc.SetDeadline(time.Time{}) buf = bufio.NewReadWriter(c.bufr, bufio.NewWriter(rwc)) + if c.r.hasByte { + if _, err := c.bufr.Peek(c.bufr.Buffered() + 1); err != nil { + return nil, nil, fmt.Errorf("unexpected Peek failure reading buffered byte: %v", err) + } + } c.setState(rwc, StateHijacked) return } diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index d5ddf6a123..a58b1839cc 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -36,6 +36,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "testing" "time" ) @@ -2545,6 +2546,13 @@ type closerFunc func() error func (f closerFunc) Close() error { return f() } +type writerFuncConn struct { + net.Conn + write func(p []byte) (n int, err error) +} + +func (c writerFuncConn) Write(p []byte) (n int, err error) { return c.write(p) } + // Issue 4677. If we try to reuse a connection that the server is in the // process of closing, we may end up successfully writing out our request (or a // portion of our request) only to find a connection error when we try to read @@ -2557,66 +2565,78 @@ func (f closerFunc) Close() error { return f() } func TestRetryIdempotentRequestsOnError(t *testing.T) { defer afterTest(t) + var ( + mu sync.Mutex + logbuf bytes.Buffer + ) + logf := func(format string, args ...interface{}) { + mu.Lock() + defer mu.Unlock() + fmt.Fprintf(&logbuf, format, args...) + logbuf.WriteByte('\n') + } + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + logf("Handler") + w.Header().Set("X-Status", "ok") })) defer ts.Close() - tr := &Transport{} + var writeNumAtomic int32 + tr := &Transport{ + Dial: func(network, addr string) (net.Conn, error) { + logf("Dial") + c, err := net.Dial(network, ts.Listener.Addr().String()) + if err != nil { + logf("Dial error: %v", err) + return nil, err + } + return &writerFuncConn{ + Conn: c, + write: func(p []byte) (n int, err error) { + if atomic.AddInt32(&writeNumAtomic, 1) == 2 { + logf("intentional write failure") + return 0, errors.New("second write fails") + } + logf("Write(%q)", p) + return c.Write(p) + }, + }, nil + }, + } + defer tr.CloseIdleConnections() c := &Client{Transport: tr} - const N = 2 - retryc := make(chan struct{}, N) SetRoundTripRetried(func() { - retryc <- struct{}{} + logf("Retried.") }) defer SetRoundTripRetried(nil) - for n := 0; n < 100; n++ { - // open 2 conns - errc := make(chan error, N) - for i := 0; i < N; i++ { - // start goroutines, send on errc - go func() { - res, err := c.Get(ts.URL) - if err == nil { - res.Body.Close() - } - errc <- err - }() - } - for i := 0; i < N; i++ { - if err := <-errc; err != nil { - t.Fatal(err) - } - } - - ts.CloseClientConnections() - for i := 0; i < N; i++ { - go func() { - res, err := c.Get(ts.URL) - if err == nil { - res.Body.Close() - } - errc <- err - }() + for i := 0; i < 3; i++ { + res, err := c.Get("http://fake.golang/") + if err != nil { + t.Fatalf("i=%d: Get = %v", i, err) } + res.Body.Close() + } - for i := 0; i < N; i++ { - if err := <-errc; err != nil { - t.Fatal(err) - } - } - for i := 0; i < N; i++ { - select { - case <-retryc: - // we triggered a retry, test was successful - t.Logf("finished after %d runs\n", n) - return - default: - } - } + mu.Lock() + got := logbuf.String() + mu.Unlock() + const want = `Dial +Write("GET / HTTP/1.1\r\nHost: fake.golang\r\nUser-Agent: Go-http-client/1.1\r\nAccept-Encoding: gzip\r\n\r\n") +Handler +intentional write failure +Retried. +Dial +Write("GET / HTTP/1.1\r\nHost: fake.golang\r\nUser-Agent: Go-http-client/1.1\r\nAccept-Encoding: gzip\r\n\r\n") +Handler +Write("GET / HTTP/1.1\r\nHost: fake.golang\r\nUser-Agent: Go-http-client/1.1\r\nAccept-Encoding: gzip\r\n\r\n") +Handler +` + if got != want { + t.Errorf("Log of events differs. Got:\n%s\nWant:\n%s", got, want) } - t.Fatal("did not trigger any retries") } // Issue 6981 diff --git a/src/os/os_test.go b/src/os/os_test.go index b7300cd38c..7ad9aac70e 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -54,12 +54,15 @@ var sysdir = func() *sysDir { case "darwin": switch runtime.GOARCH { case "arm", "arm64": + /// At this point the test harness has not had a chance + // to move us into the ./src/os directory, so the + // current working directory is the root of the app. wd, err := syscall.Getwd() if err != nil { wd = err.Error() } return &sysDir{ - filepath.Join(wd, "..", ".."), + wd, []string{ "ResourceRules.plist", "Info.plist", diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 033a18171d..022350b322 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -26,6 +26,8 @@ import ( "unsafe" ) +var sink interface{} + func TestBool(t *testing.T) { v := ValueOf(true) if v.Bool() != true { @@ -5331,6 +5333,72 @@ func TestCallGC(t *testing.T) { f2("four", "five5", "six666", "seven77", "eight888") } +// Issue 18635 (function version). +func TestKeepFuncLive(t *testing.T) { + // Test that we keep makeFuncImpl live as long as it is + // referenced on the stack. + typ := TypeOf(func(i int) {}) + var f, g func(in []Value) []Value + f = func(in []Value) []Value { + clobber() + i := int(in[0].Int()) + if i > 0 { + // We can't use Value.Call here because + // runtime.call* will keep the makeFuncImpl + // alive. However, by converting it to an + // interface value and calling that, + // reflect.callReflect is the only thing that + // can keep the makeFuncImpl live. + // + // Alternate between f and g so that if we do + // reuse the memory prematurely it's more + // likely to get obviously corrupted. + MakeFunc(typ, g).Interface().(func(i int))(i - 1) + } + return nil + } + g = func(in []Value) []Value { + clobber() + i := int(in[0].Int()) + MakeFunc(typ, f).Interface().(func(i int))(i) + return nil + } + MakeFunc(typ, f).Call([]Value{ValueOf(10)}) +} + +// Issue 18635 (method version). +type KeepMethodLive struct{} + +func (k KeepMethodLive) Method1(i int) { + clobber() + if i > 0 { + ValueOf(k).MethodByName("Method2").Interface().(func(i int))(i - 1) + } +} + +func (k KeepMethodLive) Method2(i int) { + clobber() + ValueOf(k).MethodByName("Method1").Interface().(func(i int))(i) +} + +func TestKeepMethodLive(t *testing.T) { + // Test that we keep methodValue live as long as it is + // referenced on the stack. + KeepMethodLive{}.Method1(10) +} + +// clobber tries to clobber unreachable memory. +func clobber() { + runtime.GC() + for i := 1; i < 32; i++ { + for j := 0; j < 10; j++ { + obj := make([]*byte, i) + sink = obj + } + } + runtime.GC() +} + type funcLayoutTest struct { rcvr, t Type size, argsize, retOffset uintptr diff --git a/src/reflect/value.go b/src/reflect/value.go index 042414ffe7..699ba69408 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -538,6 +538,11 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) { off += typ.size } } + + // runtime.getArgInfo expects to be able to find ctxt on the + // stack when it finds our caller, makeFuncStub. Make sure it + // doesn't get garbage collected. + runtime.KeepAlive(ctxt) } // methodReceiver returns information about the receiver @@ -650,6 +655,9 @@ func callMethod(ctxt *methodValue, frame unsafe.Pointer) { // though it's a heap object. memclrNoHeapPointers(args, frametype.size) framePool.Put(args) + + // See the comment in callReflect. + runtime.KeepAlive(ctxt) } // funcName returns the name of f, for use in error messages. diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go index 64a2f3abef..0b996d8950 100644 --- a/src/runtime/mgc.go +++ b/src/runtime/mgc.go @@ -1129,8 +1129,6 @@ top: // sitting in the per-P work caches. // Flush and disable work caches. - gcMarkRootCheck() - // Disallow caching workbufs and indicate that we're in mark 2. gcBlackenPromptly = true @@ -1153,6 +1151,16 @@ top: }) }) + // Check that roots are marked. We should be able to + // do this before the forEachP, but based on issue + // #16083 there may be a (harmless) race where we can + // enter mark 2 while some workers are still scanning + // stacks. The forEachP ensures these scans are done. + // + // TODO(austin): Figure out the race and fix this + // properly. + gcMarkRootCheck() + // Now we can start up mark 2 workers. atomic.Xaddint64(&gcController.dedicatedMarkWorkersNeeded, 0xffffffff) atomic.Xaddint64(&gcController.fractionalMarkWorkersNeeded, 0xffffffff) diff --git a/src/runtime/plugin.go b/src/runtime/plugin.go index 80869e1b1c..8edb29c9fe 100644 --- a/src/runtime/plugin.go +++ b/src/runtime/plugin.go @@ -56,7 +56,9 @@ func plugin_lastmoduleinit() (path string, syms map[string]interface{}, mismatch lock(&ifaceLock) for _, i := range md.itablinks { - additab(i, true, false) + if i.inhash == 0 { + additab(i, true, false) + } } unlock(&ifaceLock) diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s index 839df16406..6ddcb30ae2 100644 --- a/src/runtime/sys_linux_amd64.s +++ b/src/runtime/sys_linux_amd64.s @@ -330,9 +330,9 @@ sigtrampnog: // Lock sigprofCallersUse. MOVL $0, AX MOVL $1, CX - MOVQ $runtime·sigprofCallersUse(SB), BX + MOVQ $runtime·sigprofCallersUse(SB), R11 LOCK - CMPXCHGL CX, 0(BX) + CMPXCHGL CX, 0(R11) JNZ sigtramp // Skip stack trace if already locked. // Jump to the traceback function in runtime/cgo. diff --git a/src/syscall/mkpost.go b/src/syscall/mkpost.go index 26aeec843f..e75ba1502a 100644 --- a/src/syscall/mkpost.go +++ b/src/syscall/mkpost.go @@ -18,6 +18,7 @@ import ( "log" "os" "regexp" + "strings" ) func main() { @@ -38,10 +39,16 @@ func main() { re = regexp.MustCompile("Pad_cgo[A-Za-z0-9_]*") s = re.ReplaceAllString(s, "_") + // We want to keep X__val in Fsid. Hide it and restore it later. + s = strings.Replace(s, "X__val", "MKPOSTFSIDVAL", 1) + // Replace other unwanted fields with blank identifiers. re = regexp.MustCompile("X_[A-Za-z0-9_]*") s = re.ReplaceAllString(s, "_") + // Restore X__val in Fsid. + s = strings.Replace(s, "MKPOSTFSIDVAL", "X__val", 1) + // Force the type of RawSockaddr.Data to [14]int8 to match // the existing gccgo API. re = regexp.MustCompile("(Data\\s+\\[14\\])uint8") diff --git a/src/syscall/ztypes_linux_s390x.go b/src/syscall/ztypes_linux_s390x.go index cdde47863f..63c4a83b19 100644 --- a/src/syscall/ztypes_linux_s390x.go +++ b/src/syscall/ztypes_linux_s390x.go @@ -140,7 +140,7 @@ type Dirent struct { } type Fsid struct { - _ [2]int32 + X__val [2]int32 } type Flock_t struct { diff --git a/src/testing/benchmark.go b/src/testing/benchmark.go index c033ce5fec..bcebb418c4 100644 --- a/src/testing/benchmark.go +++ b/src/testing/benchmark.go @@ -219,7 +219,7 @@ func (b *B) run1() bool { } // Only print the output if we know we are not going to proceed. // Otherwise it is printed in processBench. - if b.hasSub || b.finished { + if atomic.LoadInt32(&b.hasSub) != 0 || b.finished { tag := "BENCH" if b.skipped { tag = "SKIP" @@ -460,10 +460,13 @@ func (ctx *benchContext) processBench(b *B) { // // A subbenchmark is like any other benchmark. A benchmark that calls Run at // least once will not be measured itself and will be called once with N=1. +// +// Run may be called simultaneously from multiple goroutines, but all such +// calls must happen before the outer benchmark function for b returns. func (b *B) Run(name string, f func(b *B)) bool { // Since b has subbenchmarks, we will no longer run it as a benchmark itself. // Release the lock and acquire it on exit to ensure locks stay paired. - b.hasSub = true + atomic.StoreInt32(&b.hasSub, 1) benchmarkLock.Unlock() defer benchmarkLock.Lock() diff --git a/src/testing/sub_test.go b/src/testing/sub_test.go index 8d5d9206f0..bb7b3e0925 100644 --- a/src/testing/sub_test.go +++ b/src/testing/sub_test.go @@ -6,6 +6,7 @@ package testing import ( "bytes" + "fmt" "regexp" "strings" "sync/atomic" @@ -515,3 +516,19 @@ func TestBenchmarkOutput(t *T) { Benchmark(func(b *B) { b.Error("do not print this output") }) Benchmark(func(b *B) {}) } + +func TestParallelSub(t *T) { + c := make(chan int) + block := make(chan int) + for i := 0; i < 10; i++ { + go func(i int) { + <-block + t.Run(fmt.Sprint(i), func(t *T) {}) + c <- 1 + }(i) + } + close(block) + for i := 0; i < 10; i++ { + <-c + } +} diff --git a/src/testing/testing.go b/src/testing/testing.go index c972b2737f..ddbdc25bf1 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -216,6 +216,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "time" ) @@ -267,8 +268,8 @@ type common struct { skipped bool // Test of benchmark has been skipped. finished bool // Test function has completed. done bool // Test is finished and all subtests have completed. - hasSub bool - raceErrors int // number of races detected during test + hasSub int32 // written atomically + raceErrors int // number of races detected during test parent *common level int // Nesting depth of test or benchmark. @@ -645,7 +646,7 @@ func tRunner(t *T, fn func(t *T)) { // Do not lock t.done to allow race detector to detect race in case // the user does not appropriately synchronizes a goroutine. t.done = true - if t.parent != nil && !t.hasSub { + if t.parent != nil && atomic.LoadInt32(&t.hasSub) == 0 { t.setRan() } t.signal <- true @@ -659,8 +660,11 @@ func tRunner(t *T, fn func(t *T)) { // Run runs f as a subtest of t called name. It reports whether f succeeded. // Run will block until all its parallel subtests have completed. +// +// Run may be called simultaneously from multiple goroutines, but all such +// calls must happen before the outer test function for t returns. func (t *T) Run(name string, f func(t *T)) bool { - t.hasSub = true + atomic.StoreInt32(&t.hasSub, 1) testName, ok := t.context.match.fullName(&t.common, name) if !ok { return true diff --git a/src/vendor/golang_org/x/crypto/poly1305/sum_amd64.s b/src/vendor/golang_org/x/crypto/poly1305/sum_amd64.s index bc75c61afc..2edae63828 100644 --- a/src/vendor/golang_org/x/crypto/poly1305/sum_amd64.s +++ b/src/vendor/golang_org/x/crypto/poly1305/sum_amd64.s @@ -54,9 +54,9 @@ ADCQ t3, h1; \ ADCQ $0, h2 -DATA poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF -DATA poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC -GLOBL poly1305Mask<>(SB), RODATA, $16 +DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF +DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC +GLOBL ·poly1305Mask<>(SB), RODATA, $16 // func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]key) TEXT ·poly1305(SB), $0-32 @@ -67,8 +67,8 @@ TEXT ·poly1305(SB), $0-32 MOVQ 0(AX), R11 MOVQ 8(AX), R12 - ANDQ poly1305Mask<>(SB), R11 // r0 - ANDQ poly1305Mask<>+8(SB), R12 // r1 + ANDQ ·poly1305Mask<>(SB), R11 // r0 + ANDQ ·poly1305Mask<>+8(SB), R12 // r1 XORQ R8, R8 // h0 XORQ R9, R9 // h1 XORQ R10, R10 // h2 diff --git a/src/vendor/golang_org/x/crypto/poly1305/sum_arm.s b/src/vendor/golang_org/x/crypto/poly1305/sum_arm.s index 93167b2712..f70b4ac484 100644 --- a/src/vendor/golang_org/x/crypto/poly1305/sum_arm.s +++ b/src/vendor/golang_org/x/crypto/poly1305/sum_arm.s @@ -9,12 +9,12 @@ // This code was translated into a form compatible with 5a from the public // domain source by Andrew Moon: github.com/floodyberry/poly1305-opt/blob/master/app/extensions/poly1305. -DATA poly1305_init_constants_armv6<>+0x00(SB)/4, $0x3ffffff -DATA poly1305_init_constants_armv6<>+0x04(SB)/4, $0x3ffff03 -DATA poly1305_init_constants_armv6<>+0x08(SB)/4, $0x3ffc0ff -DATA poly1305_init_constants_armv6<>+0x0c(SB)/4, $0x3f03fff -DATA poly1305_init_constants_armv6<>+0x10(SB)/4, $0x00fffff -GLOBL poly1305_init_constants_armv6<>(SB), 8, $20 +DATA ·poly1305_init_constants_armv6<>+0x00(SB)/4, $0x3ffffff +DATA ·poly1305_init_constants_armv6<>+0x04(SB)/4, $0x3ffff03 +DATA ·poly1305_init_constants_armv6<>+0x08(SB)/4, $0x3ffc0ff +DATA ·poly1305_init_constants_armv6<>+0x0c(SB)/4, $0x3f03fff +DATA ·poly1305_init_constants_armv6<>+0x10(SB)/4, $0x00fffff +GLOBL ·poly1305_init_constants_armv6<>(SB), 8, $20 // Warning: the linker may use R11 to synthesize certain instructions. Please // take care and verify that no synthetic instructions use it. @@ -27,7 +27,7 @@ TEXT poly1305_init_ext_armv6<>(SB), NOSPLIT, $0 ADD $4, R13, R8 MOVM.IB [R4-R7], (R8) MOVM.IA.W (R1), [R2-R5] - MOVW $poly1305_init_constants_armv6<>(SB), R7 + MOVW $·poly1305_init_constants_armv6<>(SB), R7 MOVW R2, R8 MOVW R2>>26, R9 MOVW R3>>20, g diff --git a/test/fixedbugs/issue18661.go b/test/fixedbugs/issue18661.go new file mode 100644 index 0000000000..8c83775200 --- /dev/null +++ b/test/fixedbugs/issue18661.go @@ -0,0 +1,18 @@ +// compile + +// Copyright 2017 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 p + +var ( + e interface{} + s = struct{ a *int }{} + b = e == s +) + +func test(obj interface{}) { + if obj != struct{ a *string }{} { + } +} |