aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Dempsky <mdempsky@google.com>2021-06-30 18:19:26 -0700
committerMatthew Dempsky <mdempsky@google.com>2021-06-30 18:28:34 -0700
commitad7e5b219e3b4b2fe9775c946e427057154c1c33 (patch)
tree8eb732ac0b95c5827b5b2e71b278db1525460539
parent8767b87ab54acca33c487ee46e237049b663b1c4 (diff)
parent4711bf30e5fec4cf290964785871deba5955c29a (diff)
downloadgo-ad7e5b219e3b4b2fe9775c946e427057154c1c33.tar.gz
go-ad7e5b219e3b4b2fe9775c946e427057154c1c33.zip
[dev.typeparams] all: merge master (4711bf3) into dev.typeparams
Conflicts: - src/cmd/compile/internal/walk/builtin.go On dev.typeparams, CL 330194 changed OCHECKNIL to not require manual SetTypecheck(1) anymore; while on master, CL 331070 got rid of the OCHECKNIL altogether by moving the check into the runtime support functions. - src/internal/buildcfg/exp.go On master, CL 331109 refactored the logic for parsing the GOEXPERIMENT string, so that it could be more easily reused by cmd/go; while on dev.typeparams, several CLs tweaked the regabi experiment defaults. Merge List: + 2021-06-30 4711bf30e5 doc/go1.17: linkify "language changes" in the runtime section + 2021-06-30 ed56ea73e8 path/filepath: deflake TestEvalSymlinksAboveRoot on darwin + 2021-06-30 c080d0323b cmd/dist: pass -Wno-unknown-warning-option in swig_callback_lto + 2021-06-30 7d0e9e6e74 image/gif: fix typo in the comment (io.ReadByte -> io.ByteReader) + 2021-06-30 0fa3265fe1 os: change example to avoid deprecated function + 2021-06-30 d19a53338f image: add Uniform.RGBA64At and Rectangle.RGBA64At + 2021-06-30 c45e800e0c crypto/x509: don't fail on optional auth key id fields + 2021-06-29 f9d50953b9 net: fix failure of TestCVE202133195 + 2021-06-29 e294b8a49e doc/go1.17: fix typo "MacOS" -> "macOS" + 2021-06-29 3463852b76 math/big: fix typo of comment (`BytesScanner` to `ByteScanner`) + 2021-06-29 fd4b587da3 cmd/compile: suppress details error for invalid variadic argument type + 2021-06-29 e2e05af6e1 cmd/internal/obj/arm64: fix an encoding error of CMPW instruction + 2021-06-28 4bb0847b08 cmd/compile,runtime: change unsafe.Slice((*T)(nil), 0) to return []T(nil) + 2021-06-28 1519271a93 spec: change unsafe.Slice((*T)(nil), 0) to return []T(nil) + 2021-06-28 5385e2386b runtime/internal/atomic: drop Cas64 pointer indirection in comments + 2021-06-28 956c81bfe6 cmd/go: add GOEXPERIMENT to `go env` output + 2021-06-28 a1d27269d6 cmd/go: prep for 'go env' refactoring + 2021-06-28 901510ed4e cmd/link/internal/ld: skip the windows ASLR test when CGO_ENABLED=0 + 2021-06-28 361159c055 cmd/cgo: fix 'see gmp.go' to 'see doc.go' + 2021-06-27 c95464f0ea internal/buildcfg: refactor GOEXPERIMENT parsing code somewhat + 2021-06-25 ed01ceaf48 runtime/race: use race build tag on syso_test.go + 2021-06-25 d1916e5e84 go/types: in TestCheck/issues.src, import regexp/syntax instead of cmd/compile/internal/syntax + 2021-06-25 5160896c69 go/types: in TestStdlib, import from source instead of export data + 2021-06-25 d01bc571f7 runtime: make ncgocall a global counter Change-Id: I1ce4a3b3ff7c824c67ad66dd27d9d5f1d25c0023
-rw-r--r--api/go1.17.txt2
-rw-r--r--doc/go1.17.html8
-rw-r--r--doc/go_spec.html10
-rw-r--r--src/cmd/asm/internal/asm/testdata/arm64.s3
-rw-r--r--src/cmd/cgo/main.go2
-rw-r--r--src/cmd/compile/internal/typecheck/builtin.go5
-rw-r--r--src/cmd/compile/internal/typecheck/builtin/runtime.go5
-rw-r--r--src/cmd/compile/internal/typecheck/func.go7
-rw-r--r--src/cmd/compile/internal/typecheck/typecheck.go17
-rw-r--r--src/cmd/compile/internal/walk/builtin.go25
-rw-r--r--src/cmd/dist/test.go6
-rw-r--r--src/cmd/go/alldocs.go6
-rw-r--r--src/cmd/go/internal/cfg/cfg.go2
-rw-r--r--src/cmd/go/internal/envcmd/env.go218
-rw-r--r--src/cmd/go/internal/help/helpdoc.go6
-rw-r--r--src/cmd/go/main.go64
-rw-r--r--src/cmd/go/testdata/script/env_exp.txt17
-rw-r--r--src/cmd/go/testdata/script/env_unset.txt30
-rw-r--r--src/cmd/go/testdata/script/env_write.txt6
-rw-r--r--src/cmd/internal/obj/arm64/asm7.go4
-rw-r--r--src/cmd/link/internal/ld/ld_test.go2
-rw-r--r--src/crypto/x509/parser.go8
-rw-r--r--src/crypto/x509/x509_test.go42
-rw-r--r--src/go/types/stdlib_test.go8
-rw-r--r--src/go/types/testdata/check/issues.src8
-rw-r--r--src/image/geom.go8
-rw-r--r--src/image/gif/reader.go2
-rw-r--r--src/image/image_test.go12
-rw-r--r--src/image/names.go5
-rw-r--r--src/internal/buildcfg/exp.go60
-rw-r--r--src/math/big/int.go2
-rw-r--r--src/net/dnsclient_unix_test.go8
-rw-r--r--src/os/example_test.go5
-rw-r--r--src/path/filepath/path_test.go11
-rw-r--r--src/runtime/cgocall.go2
-rw-r--r--src/runtime/checkptr.go21
-rw-r--r--src/runtime/checkptr_test.go2
-rw-r--r--src/runtime/debug.go2
-rw-r--r--src/runtime/internal/atomic/atomic_amd64.s2
-rw-r--r--src/runtime/internal/atomic/atomic_arm64.s2
-rw-r--r--src/runtime/internal/atomic/atomic_mips64x.s2
-rw-r--r--src/runtime/internal/atomic/atomic_ppc64x.s2
-rw-r--r--src/runtime/internal/atomic/atomic_riscv64.s3
-rw-r--r--src/runtime/proc.go2
-rw-r--r--src/runtime/race/syso_test.go10
-rw-r--r--src/runtime/slice.go24
-rw-r--r--src/runtime/testdata/testprog/checkptr.go13
-rw-r--r--src/unsafe/unsafe.go5
-rw-r--r--test/fixedbugs/issue46957.go13
-rw-r--r--test/unsafebuiltins.go7
50 files changed, 508 insertions, 228 deletions
diff --git a/api/go1.17.txt b/api/go1.17.txt
index c5eb381708..8e4c0f5624 100644
--- a/api/go1.17.txt
+++ b/api/go1.17.txt
@@ -47,7 +47,9 @@ pkg image, method (*Paletted) RGBA64At(int, int) color.RGBA64
pkg image, method (*Paletted) SetRGBA64(int, int, color.RGBA64)
pkg image, method (*RGBA) RGBA64At(int, int) color.RGBA64
pkg image, method (*RGBA) SetRGBA64(int, int, color.RGBA64)
+pkg image, method (*Uniform) RGBA64At(int, int) color.RGBA64
pkg image, method (*YCbCr) RGBA64At(int, int) color.RGBA64
+pkg image, method (Rectangle) RGBA64At(int, int) color.RGBA64
pkg image, type RGBA64Image interface { At, Bounds, ColorModel, RGBA64At }
pkg image, type RGBA64Image interface, At(int, int) color.Color
pkg image, type RGBA64Image interface, Bounds() Rectangle
diff --git a/doc/go1.17.html b/doc/go1.17.html
index 22896c8c27..66b4f48b61 100644
--- a/doc/go1.17.html
+++ b/doc/go1.17.html
@@ -401,7 +401,7 @@ func Foo() bool {
<p><!-- golang.org/issue/40724 -->
Go 1.17 implements a new way of passing function arguments and results using
- registers instead of the stack. This work is enabled for Linux, MacOS, and
+ registers instead of the stack. This work is enabled for Linux, macOS, and
Windows on the 64-bit x86 architecture (the <code>linux/amd64</code>,
<code>darwin/amd64</code>, <code>windows/amd64</code> ports). For a
representative set of Go packages and programs, benchmarking has shown
@@ -811,8 +811,8 @@ func Foo() bool {
<p><!-- CL 311129 -->
The concrete image types (<code>RGBA</code>, <code>Gray16</code> and so on)
now implement a new <a href="/pkg/image/#RGBA64Image"><code>RGBA64Image</code></a>
- interface. Those concrete types, other than the chroma-subsampling
- related <code>YCbCr</code> and <code>NYCbCrA</code>, also now implement
+ interface. The concrete types that previously implemented
+ <a href="/pkg/image/draw/#Image"><code>draw.Image</code></a> now also implement
<a href="/pkg/image/draw/#RGBA64Image"><code>draw.RGBA64Image</code></a>, a
new interface in the <code>image/draw</code> package.
</p>
@@ -993,7 +993,7 @@ func Foo() bool {
is no longer sufficient to guarantee that a call to
<a href="/pkg/reflect/#Value.Convert"><code>Value.Convert</code></a> will not panic.
It may panic when converting `[]T` to `*[N]T` if the slice's length is less than N.
- See the language changes section above.
+ See the <a href="#language">language changes</a> section above.
</p>
</dd>
</dl><!-- reflect -->
diff --git a/doc/go_spec.html b/doc/go_spec.html
index b59b37fd55..e0602418e8 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
<!--{
"Title": "The Go Programming Language Specification",
- "Subtitle": "Version of Jun 22, 2021",
+ "Subtitle": "Version of Jun 28, 2021",
"Path": "/ref/spec"
}-->
@@ -6790,10 +6790,16 @@ and whose length and capacity are <code>len</code>:
</pre>
<p>
+As a special case, if <code>ptr</code> is <code>nil</code> and <code>len</code> is zero,
+<code>Slice</code> returns <code>nil</code>.
+</p>
+
+<p>
The <code>len</code> argument must be of integer type or an untyped <a href="#Constants">constant</a>.
A constant <code>len</code> argument must be non-negative and <a href="#Representability">representable</a> by a value of type <code>int</code>;
if it is an untyped constant it is given type <code>int</code>.
-If <code>ptr</code> is <code>nil</code> or <code>len</code> is negative at run time,
+At run time, if <code>len</code> is negative,
+or if <code>ptr</code> is <code>nil</code> and <code>len</code> is not zero,
a <a href="#Run_time_panics">run-time panic</a> occurs.
</p>
diff --git a/src/cmd/asm/internal/asm/testdata/arm64.s b/src/cmd/asm/internal/asm/testdata/arm64.s
index 5f1e68545b..d8a20edfc1 100644
--- a/src/cmd/asm/internal/asm/testdata/arm64.s
+++ b/src/cmd/asm/internal/asm/testdata/arm64.s
@@ -89,7 +89,7 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
CMP R1<<33, R2
CMP R22.SXTX, RSP // ffe336eb
CMP $0x22220000, RSP // CMP $572653568, RSP // 5b44a4d2ff633beb
- CMPW $0x22220000, RSP // CMPW $572653568, RSP // 5b44a452ff633b6b
+ CMPW $0x22220000, RSP // CMPW $572653568, RSP // 5b44a452ff433b6b
CCMN MI, ZR, R1, $4 // e44341ba
// MADD Rn,Rm,Ra,Rd
MADD R1, R2, R3, R4 // 6408019b
@@ -377,6 +377,7 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
MOVD $0x1000100010001000, RSP // MOVD $1152939097061330944, RSP // ff8304b2
MOVW $0x10001000, RSP // MOVW $268439552, RSP // ff830432
ADDW $0x10001000, R1 // ADDW $268439552, R1 // fb83043221001b0b
+ ADDW $0x22220000, RSP, R3 // ADDW $572653568, RSP, R3 // 5b44a452e3433b0b
// move a large constant to a Vd.
VMOVS $0x80402010, V11 // VMOVS $2151686160, V11
diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go
index 03a662e689..c6a0c525e6 100644
--- a/src/cmd/cgo/main.go
+++ b/src/cmd/cgo/main.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Cgo; see gmp.go for an overview.
+// Cgo; see doc.go for an overview.
// TODO(rsc):
// Emit correct line number annotations.
diff --git a/src/cmd/compile/internal/typecheck/builtin.go b/src/cmd/compile/internal/typecheck/builtin.go
index 67a894c7ed..833b17b414 100644
--- a/src/cmd/compile/internal/typecheck/builtin.go
+++ b/src/cmd/compile/internal/typecheck/builtin.go
@@ -138,6 +138,7 @@ var runtimeDecls = [...]struct {
{"growslice", funcTag, 116},
{"unsafeslice", funcTag, 117},
{"unsafeslice64", funcTag, 118},
+ {"unsafeslicecheckptr", funcTag, 118},
{"memmove", funcTag, 119},
{"memclrNoHeapPointers", funcTag, 120},
{"memclrHasPointers", funcTag, 120},
@@ -341,8 +342,8 @@ func runtimeTypes() []*types.Type {
typs[114] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7]))
typs[115] = types.NewSlice(typs[2])
typs[116] = newSig(params(typs[1], typs[115], typs[15]), params(typs[115]))
- typs[117] = newSig(params(typs[1], typs[15]), nil)
- typs[118] = newSig(params(typs[1], typs[22]), nil)
+ typs[117] = newSig(params(typs[1], typs[7], typs[15]), nil)
+ typs[118] = newSig(params(typs[1], typs[7], typs[22]), nil)
typs[119] = newSig(params(typs[3], typs[3], typs[5]), nil)
typs[120] = newSig(params(typs[7], typs[5]), nil)
typs[121] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6]))
diff --git a/src/cmd/compile/internal/typecheck/builtin/runtime.go b/src/cmd/compile/internal/typecheck/builtin/runtime.go
index ebeaeae79e..2b29ea3c08 100644
--- a/src/cmd/compile/internal/typecheck/builtin/runtime.go
+++ b/src/cmd/compile/internal/typecheck/builtin/runtime.go
@@ -183,8 +183,9 @@ func makeslice(typ *byte, len int, cap int) unsafe.Pointer
func makeslice64(typ *byte, len int64, cap int64) unsafe.Pointer
func makeslicecopy(typ *byte, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer
func growslice(typ *byte, old []any, cap int) (ary []any)
-func unsafeslice(typ *byte, len int)
-func unsafeslice64(typ *byte, len int64)
+func unsafeslice(typ *byte, ptr unsafe.Pointer, len int)
+func unsafeslice64(typ *byte, ptr unsafe.Pointer, len int64)
+func unsafeslicecheckptr(typ *byte, ptr unsafe.Pointer, len int64)
func memmove(to *any, frm *any, length uintptr)
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
diff --git a/src/cmd/compile/internal/typecheck/func.go b/src/cmd/compile/internal/typecheck/func.go
index 5f9f8c6ebd..d0aad5ac07 100644
--- a/src/cmd/compile/internal/typecheck/func.go
+++ b/src/cmd/compile/internal/typecheck/func.go
@@ -961,7 +961,14 @@ func tcUnsafeSlice(n *ir.BinaryExpr) *ir.BinaryExpr {
t := n.X.Type()
if !t.IsPtr() {
base.Errorf("first argument to unsafe.Slice must be pointer; have %L", t)
+ } else if t.Elem().NotInHeap() {
+ // TODO(mdempsky): This can be relaxed, but should only affect the
+ // Go runtime itself. End users should only see //go:notinheap
+ // types due to incomplete C structs in cgo, and those types don't
+ // have a meaningful size anyway.
+ base.Errorf("unsafe.Slice of incomplete (or unallocatable) type not allowed")
}
+
if !checkunsafeslice(&n.Y) {
n.SetType(nil)
return n
diff --git a/src/cmd/compile/internal/typecheck/typecheck.go b/src/cmd/compile/internal/typecheck/typecheck.go
index 232c0e66ef..21d3100f66 100644
--- a/src/cmd/compile/internal/typecheck/typecheck.go
+++ b/src/cmd/compile/internal/typecheck/typecheck.go
@@ -1471,15 +1471,22 @@ toomany:
}
func errorDetails(nl ir.Nodes, tstruct *types.Type, isddd bool) string {
- // If we don't know any type at a call site, let's suppress any return
- // message signatures. See Issue https://golang.org/issues/19012.
+ // Suppress any return message signatures if:
+ //
+ // (1) We don't know any type at a call site (see #19012).
+ // (2) Any node has an unknown type.
+ // (3) Invalid type for variadic parameter (see #46957).
if tstruct == nil {
- return ""
+ return "" // case 1
}
- // If any node has an unknown type, suppress it as well
+
+ if isddd && !nl[len(nl)-1].Type().IsSlice() {
+ return "" // case 3
+ }
+
for _, n := range nl {
if n.Type() == nil {
- return ""
+ return "" // case 2
}
}
return fmt.Sprintf("\n\thave %s\n\twant %v", fmtSignature(nl, isddd), tstruct)
diff --git a/src/cmd/compile/internal/walk/builtin.go b/src/cmd/compile/internal/walk/builtin.go
index 135eaee6bc..e1a2319e14 100644
--- a/src/cmd/compile/internal/walk/builtin.go
+++ b/src/cmd/compile/internal/walk/builtin.go
@@ -647,35 +647,28 @@ func walkRecoverFP(nn *ir.CallExpr, init *ir.Nodes) ir.Node {
}
func walkUnsafeSlice(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
+ ptr := safeExpr(n.X, init)
len := safeExpr(n.Y, init)
fnname := "unsafeslice64"
- argtype := types.Types[types.TINT64]
+ lenType := types.Types[types.TINT64]
// Type checking guarantees that TIDEAL len/cap are positive and fit in an int.
// The case of len or cap overflow when converting TUINT or TUINTPTR to TINT
// will be handled by the negative range checks in unsafeslice during runtime.
- if len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size() {
+ if ir.ShouldCheckPtr(ir.CurFunc, 1) {
+ fnname = "unsafeslicecheckptr"
+ // for simplicity, unsafeslicecheckptr always uses int64
+ } else if len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size() {
fnname = "unsafeslice"
- argtype = types.Types[types.TINT]
+ lenType = types.Types[types.TINT]
}
t := n.Type()
- // Call runtime.unsafeslice[64] to check that the length argument is
- // non-negative and smaller than the max length allowed for the
- // element type.
+ // Call runtime.unsafeslice{,64,checkptr} to check ptr and len.
fn := typecheck.LookupRuntime(fnname)
- init.Append(mkcall1(fn, nil, init, reflectdata.TypePtr(t.Elem()), typecheck.Conv(len, argtype)))
-
- ptr := walkExpr(n.X, init)
-
- check := ir.NewUnaryExpr(n.Pos(), ir.OCHECKNIL, ptr)
- init.Append(typecheck.Stmt(check))
-
- // TODO(mdempsky): checkptr instrumentation. Maybe merge into length
- // check above, along with nil check? Need to be careful about
- // notinheap pointers though: can't pass them as unsafe.Pointer.
+ init.Append(mkcall1(fn, nil, init, reflectdata.TypePtr(t.Elem()), typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]), typecheck.Conv(len, lenType)))
h := ir.NewSliceHeaderExpr(n.Pos(), t,
typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]),
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index f2c4cf0b43..4acd357974 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -737,9 +737,9 @@ func (t *tester) registerTests() {
fn: func(dt *distTest) error {
cmd := t.addCmd(dt, "misc/swig/callback", t.goTest())
cmd.Env = append(os.Environ(),
- "CGO_CFLAGS=-flto -Wno-lto-type-mismatch",
- "CGO_CXXFLAGS=-flto -Wno-lto-type-mismatch",
- "CGO_LDFLAGS=-flto -Wno-lto-type-mismatch",
+ "CGO_CFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option",
+ "CGO_CXXFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option",
+ "CGO_LDFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option",
)
return nil
},
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index fd95da23eb..90eb3e2a00 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -1903,6 +1903,12 @@
// GCCGOTOOLDIR
// If set, where to find gccgo tools, such as cgo.
// The default is based on how gccgo was configured.
+// GOEXPERIMENT
+// Comma-separated list of toolchain experiments to enable or disable.
+// The list of available experiments may change arbitrarily over time.
+// See src/internal/goexperiment/flags.go for currently valid values.
+// Warning: This variable is provided for the development and testing
+// of the Go toolchain itself. Use beyond that purpose is unsupported.
// GOROOT_FINAL
// The root of the installed Go tree, when it is
// installed in a location other than where it is built.
diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go
index fc6989097e..57a3c1ff6f 100644
--- a/src/cmd/go/internal/cfg/cfg.go
+++ b/src/cmd/go/internal/cfg/cfg.go
@@ -79,7 +79,7 @@ func defaultContext() build.Context {
// The experiments flags are based on GOARCH, so they may
// need to change. TODO: This should be cleaned up.
- buildcfg.UpdateExperiments(ctxt.GOARCH)
+ buildcfg.UpdateExperiments(ctxt.GOOS, ctxt.GOARCH, envOr("GOEXPERIMENT", buildcfg.DefaultGOEXPERIMENT))
ctxt.ToolTags = nil
for _, exp := range buildcfg.EnabledExperiments() {
ctxt.ToolTags = append(ctxt.ToolTags, "goexperiment."+exp)
diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go
index b30c37ab27..1553d26391 100644
--- a/src/cmd/go/internal/envcmd/env.go
+++ b/src/cmd/go/internal/envcmd/env.go
@@ -10,6 +10,7 @@ import (
"encoding/json"
"fmt"
"go/build"
+ "internal/buildcfg"
"io"
"os"
"path/filepath"
@@ -72,6 +73,7 @@ func MkEnv() []cfg.EnvVar {
{Name: "GOCACHE", Value: cache.DefaultDir()},
{Name: "GOENV", Value: envFile},
{Name: "GOEXE", Value: cfg.ExeSuffix},
+ {Name: "GOEXPERIMENT", Value: buildcfg.GOEXPERIMENT()},
{Name: "GOFLAGS", Value: cfg.Getenv("GOFLAGS")},
{Name: "GOHOSTARCH", Value: runtime.GOARCH},
{Name: "GOHOSTOS", Value: runtime.GOOS},
@@ -197,6 +199,21 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
if *envU && *envW {
base.Fatalf("go env: cannot use -u with -w")
}
+
+ // Handle 'go env -w' and 'go env -u' before calling buildcfg.Check,
+ // so they can be used to recover from an invalid configuration.
+ if *envW {
+ runEnvW(args)
+ return
+ }
+
+ if *envU {
+ runEnvU(args)
+ return
+ }
+
+ buildcfg.Check()
+
env := cfg.CmdEnv
env = append(env, ExtraEnvVars()...)
@@ -206,14 +223,7 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
// Do we need to call ExtraEnvVarsCostly, which is a bit expensive?
needCostly := false
- if *envU || *envW {
- // We're overwriting or removing default settings,
- // so it doesn't really matter what the existing settings are.
- //
- // Moreover, we haven't validated the new settings yet, so it is
- // important that we NOT perform any actions based on them,
- // such as initializing the builder to compute other variables.
- } else if len(args) == 0 {
+ if len(args) == 0 {
// We're listing all environment variables ("go env"),
// including the expensive ones.
needCostly = true
@@ -238,95 +248,6 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
env = append(env, ExtraEnvVarsCostly()...)
}
- if *envW {
- // Process and sanity-check command line.
- if len(args) == 0 {
- base.Fatalf("go env -w: no KEY=VALUE arguments given")
- }
- osEnv := make(map[string]string)
- for _, e := range cfg.OrigEnv {
- if i := strings.Index(e, "="); i >= 0 {
- osEnv[e[:i]] = e[i+1:]
- }
- }
- add := make(map[string]string)
- for _, arg := range args {
- i := strings.Index(arg, "=")
- if i < 0 {
- base.Fatalf("go env -w: arguments must be KEY=VALUE: invalid argument: %s", arg)
- }
- key, val := arg[:i], arg[i+1:]
- if err := checkEnvWrite(key, val); err != nil {
- base.Fatalf("go env -w: %v", err)
- }
- if _, ok := add[key]; ok {
- base.Fatalf("go env -w: multiple values for key: %s", key)
- }
- add[key] = val
- if osVal := osEnv[key]; osVal != "" && osVal != val {
- fmt.Fprintf(os.Stderr, "warning: go env -w %s=... does not override conflicting OS environment variable\n", key)
- }
- }
-
- goos, okGOOS := add["GOOS"]
- goarch, okGOARCH := add["GOARCH"]
- if okGOOS || okGOARCH {
- if !okGOOS {
- goos = cfg.Goos
- }
- if !okGOARCH {
- goarch = cfg.Goarch
- }
- if err := work.CheckGOOSARCHPair(goos, goarch); err != nil {
- base.Fatalf("go env -w: %v", err)
- }
- }
-
- gotmp, okGOTMP := add["GOTMPDIR"]
- if okGOTMP {
- if !filepath.IsAbs(gotmp) && gotmp != "" {
- base.Fatalf("go env -w: GOTMPDIR must be an absolute path")
- }
- }
-
- updateEnvFile(add, nil)
- return
- }
-
- if *envU {
- // Process and sanity-check command line.
- if len(args) == 0 {
- base.Fatalf("go env -u: no arguments given")
- }
- del := make(map[string]bool)
- for _, arg := range args {
- if err := checkEnvWrite(arg, ""); err != nil {
- base.Fatalf("go env -u: %v", err)
- }
- del[arg] = true
- }
- if del["GOOS"] || del["GOARCH"] {
- goos, goarch := cfg.Goos, cfg.Goarch
- if del["GOOS"] {
- goos = getOrigEnv("GOOS")
- if goos == "" {
- goos = build.Default.GOOS
- }
- }
- if del["GOARCH"] {
- goarch = getOrigEnv("GOARCH")
- if goarch == "" {
- goarch = build.Default.GOARCH
- }
- }
- if err := work.CheckGOOSARCHPair(goos, goarch); err != nil {
- base.Fatalf("go env -u: %v", err)
- }
- }
- updateEnvFile(nil, del)
- return
- }
-
if len(args) > 0 {
if *envJson {
var es []cfg.EnvVar
@@ -351,6 +272,109 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
PrintEnv(os.Stdout, env)
}
+func runEnvW(args []string) {
+ // Process and sanity-check command line.
+ if len(args) == 0 {
+ base.Fatalf("go env -w: no KEY=VALUE arguments given")
+ }
+ osEnv := make(map[string]string)
+ for _, e := range cfg.OrigEnv {
+ if i := strings.Index(e, "="); i >= 0 {
+ osEnv[e[:i]] = e[i+1:]
+ }
+ }
+ add := make(map[string]string)
+ for _, arg := range args {
+ i := strings.Index(arg, "=")
+ if i < 0 {
+ base.Fatalf("go env -w: arguments must be KEY=VALUE: invalid argument: %s", arg)
+ }
+ key, val := arg[:i], arg[i+1:]
+ if err := checkEnvWrite(key, val); err != nil {
+ base.Fatalf("go env -w: %v", err)
+ }
+ if _, ok := add[key]; ok {
+ base.Fatalf("go env -w: multiple values for key: %s", key)
+ }
+ add[key] = val
+ if osVal := osEnv[key]; osVal != "" && osVal != val {
+ fmt.Fprintf(os.Stderr, "warning: go env -w %s=... does not override conflicting OS environment variable\n", key)
+ }
+ }
+
+ if err := checkBuildConfig(add, nil); err != nil {
+ base.Fatalf("go env -w: %v", err)
+ }
+
+ gotmp, okGOTMP := add["GOTMPDIR"]
+ if okGOTMP {
+ if !filepath.IsAbs(gotmp) && gotmp != "" {
+ base.Fatalf("go env -w: GOTMPDIR must be an absolute path")
+ }
+ }
+
+ updateEnvFile(add, nil)
+}
+
+func runEnvU(args []string) {
+ // Process and sanity-check command line.
+ if len(args) == 0 {
+ base.Fatalf("go env -u: no arguments given")
+ }
+ del := make(map[string]bool)
+ for _, arg := range args {
+ if err := checkEnvWrite(arg, ""); err != nil {
+ base.Fatalf("go env -u: %v", err)
+ }
+ del[arg] = true
+ }
+
+ if err := checkBuildConfig(nil, del); err != nil {
+ base.Fatalf("go env -u: %v", err)
+ }
+
+ updateEnvFile(nil, del)
+}
+
+// checkBuildConfig checks whether the build configuration is valid
+// after the specified configuration environment changes are applied.
+func checkBuildConfig(add map[string]string, del map[string]bool) error {
+ // get returns the value for key after applying add and del and
+ // reports whether it changed. cur should be the current value
+ // (i.e., before applying changes) and def should be the default
+ // value (i.e., when no environment variables are provided at all).
+ get := func(key, cur, def string) (string, bool) {
+ if val, ok := add[key]; ok {
+ return val, true
+ }
+ if del[key] {
+ val := getOrigEnv(key)
+ if val == "" {
+ val = def
+ }
+ return val, true
+ }
+ return cur, false
+ }
+
+ goos, okGOOS := get("GOOS", cfg.Goos, build.Default.GOOS)
+ goarch, okGOARCH := get("GOARCH", cfg.Goarch, build.Default.GOARCH)
+ if okGOOS || okGOARCH {
+ if err := work.CheckGOOSARCHPair(goos, goarch); err != nil {
+ return err
+ }
+ }
+
+ goexperiment, okGOEXPERIMENT := get("GOEXPERIMENT", buildcfg.GOEXPERIMENT(), "")
+ if okGOEXPERIMENT {
+ if _, _, err := buildcfg.ParseGOEXPERIMENT(goos, goarch, goexperiment); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
// PrintEnv prints the environment variables to w.
func PrintEnv(w io.Writer, env []cfg.EnvVar) {
for _, e := range env {
diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go
index b552777e3e..490ff1fb7c 100644
--- a/src/cmd/go/internal/help/helpdoc.go
+++ b/src/cmd/go/internal/help/helpdoc.go
@@ -610,6 +610,12 @@ Special-purpose environment variables:
GCCGOTOOLDIR
If set, where to find gccgo tools, such as cgo.
The default is based on how gccgo was configured.
+ GOEXPERIMENT
+ Comma-separated list of toolchain experiments to enable or disable.
+ The list of available experiments may change arbitrarily over time.
+ See src/internal/goexperiment/flags.go for currently valid values.
+ Warning: This variable is provided for the development and testing
+ of the Go toolchain itself. Use beyond that purpose is unsupported.
GOROOT_FINAL
The root of the installed Go tree, when it is
installed in a location other than where it is built.
diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go
index 02174a56ff..16361e02ca 100644
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -145,24 +145,6 @@ func main() {
os.Exit(2)
}
- if err := buildcfg.Error; err != nil {
- fmt.Fprintf(os.Stderr, "go: %v\n", buildcfg.Error)
- os.Exit(2)
- }
-
- // Set environment (GOOS, GOARCH, etc) explicitly.
- // In theory all the commands we invoke should have
- // the same default computation of these as we do,
- // but in practice there might be skew
- // This makes sure we all agree.
- cfg.OrigEnv = os.Environ()
- cfg.CmdEnv = envcmd.MkEnv()
- for _, env := range cfg.CmdEnv {
- if os.Getenv(env.Name) != env.Value {
- os.Setenv(env.Name, env.Value)
- }
- }
-
BigCmdLoop:
for bigCmd := base.Go; ; {
for _, cmd := range bigCmd.Commands {
@@ -188,18 +170,7 @@ BigCmdLoop:
if !cmd.Runnable() {
continue
}
- cmd.Flag.Usage = func() { cmd.Usage() }
- if cmd.CustomFlags {
- args = args[1:]
- } else {
- base.SetFromGOFLAGS(&cmd.Flag)
- cmd.Flag.Parse(args[1:])
- args = cmd.Flag.Args()
- }
- ctx := maybeStartTrace(context.Background())
- ctx, span := trace.StartSpan(ctx, fmt.Sprint("Running ", cmd.Name(), " command"))
- cmd.Run(ctx, cmd, args)
- span.Done()
+ invoke(cmd, args)
base.Exit()
return
}
@@ -213,6 +184,39 @@ BigCmdLoop:
}
}
+func invoke(cmd *base.Command, args []string) {
+ // 'go env' handles checking the build config
+ if cmd != envcmd.CmdEnv {
+ buildcfg.Check()
+ }
+
+ // Set environment (GOOS, GOARCH, etc) explicitly.
+ // In theory all the commands we invoke should have
+ // the same default computation of these as we do,
+ // but in practice there might be skew
+ // This makes sure we all agree.
+ cfg.OrigEnv = os.Environ()
+ cfg.CmdEnv = envcmd.MkEnv()
+ for _, env := range cfg.CmdEnv {
+ if os.Getenv(env.Name) != env.Value {
+ os.Setenv(env.Name, env.Value)
+ }
+ }
+
+ cmd.Flag.Usage = func() { cmd.Usage() }
+ if cmd.CustomFlags {
+ args = args[1:]
+ } else {
+ base.SetFromGOFLAGS(&cmd.Flag)
+ cmd.Flag.Parse(args[1:])
+ args = cmd.Flag.Args()
+ }
+ ctx := maybeStartTrace(context.Background())
+ ctx, span := trace.StartSpan(ctx, fmt.Sprint("Running ", cmd.Name(), " command"))
+ cmd.Run(ctx, cmd, args)
+ span.Done()
+}
+
func init() {
base.Usage = mainUsage
}
diff --git a/src/cmd/go/testdata/script/env_exp.txt b/src/cmd/go/testdata/script/env_exp.txt
new file mode 100644
index 0000000000..681512d87c
--- /dev/null
+++ b/src/cmd/go/testdata/script/env_exp.txt
@@ -0,0 +1,17 @@
+# Test GOEXPERIMENT variable
+
+# go env shows default empty GOEXPERIMENT
+go env
+stdout GOEXPERIMENT=
+
+# go env shows valid experiments
+env GOEXPERIMENT=fieldtrack,staticlockranking
+go env GOEXPERIMENT
+stdout '.*fieldtrack.*staticlockranking.*'
+go env
+stdout 'GOEXPERIMENT=.*fieldtrack.*staticlockranking.*'
+
+# go env rejects unknown experiments
+env GOEXPERIMENT=bad
+! go env GOEXPERIMENT
+stderr 'unknown GOEXPERIMENT bad'
diff --git a/src/cmd/go/testdata/script/env_unset.txt b/src/cmd/go/testdata/script/env_unset.txt
new file mode 100644
index 0000000000..4e0f249509
--- /dev/null
+++ b/src/cmd/go/testdata/script/env_unset.txt
@@ -0,0 +1,30 @@
+# Test that we can unset variables, even if initially invalid,
+# as long as resulting config is valid.
+
+env GOENV=badenv
+env GOOS=
+env GOARCH=
+env GOEXPERIMENT=
+
+! go env
+stderr '^go(\.exe)?: unknown GOEXPERIMENT badexp$'
+
+go env -u GOEXPERIMENT
+
+! go env
+stderr '^cmd/go: unsupported GOOS/GOARCH pair bados/badarch$'
+
+! go env -u GOOS
+stderr '^go env -u: unsupported GOOS/GOARCH pair \w+/badarch$'
+
+! go env -u GOARCH
+stderr '^go env -u: unsupported GOOS/GOARCH pair bados/\w+$'
+
+go env -u GOOS GOARCH
+
+go env
+
+-- badenv --
+GOOS=bados
+GOARCH=badarch
+GOEXPERIMENT=badexp
diff --git a/src/cmd/go/testdata/script/env_write.txt b/src/cmd/go/testdata/script/env_write.txt
index 4fa39df104..b5e9739167 100644
--- a/src/cmd/go/testdata/script/env_write.txt
+++ b/src/cmd/go/testdata/script/env_write.txt
@@ -179,3 +179,9 @@ stderr 'unsupported GOOS/GOARCH.*windows/mips$'
stderr 'go env -w: GOMODCACHE entry is relative; must be absolute path: "~/test"'
! go env -w GOMODCACHE=./test
stderr 'go env -w: GOMODCACHE entry is relative; must be absolute path: "./test"'
+
+# go env -w checks validity of GOEXPERIMENT
+env GOEXPERIMENT=
+! go env -w GOEXPERIMENT=badexp
+stderr 'unknown GOEXPERIMENT badexp'
+go env -w GOEXPERIMENT=fieldtrack
diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go
index b8c3cd97c7..d99afa3d27 100644
--- a/src/cmd/internal/obj/arm64/asm7.go
+++ b/src/cmd/internal/obj/arm64/asm7.go
@@ -4333,8 +4333,10 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
if p.To.Reg == REG_RSP && isADDSop(p.As) {
c.ctxt.Diag("illegal destination register: %v\n", p)
}
+ lsl0 := LSL0_64
if isADDWop(p.As) || isANDWop(p.As) {
o1 = c.omovconst(AMOVW, p, &p.From, REGTMP)
+ lsl0 = LSL0_32
} else {
o1 = c.omovconst(AMOVD, p, &p.From, REGTMP)
}
@@ -4350,7 +4352,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
if p.To.Reg == REGSP || r == REGSP {
o2 = c.opxrrr(p, p.As, false)
o2 |= REGTMP & 31 << 16
- o2 |= LSL0_64
+ o2 |= uint32(lsl0)
} else {
o2 = c.oprrr(p, p.As)
o2 |= REGTMP & 31 << 16 /* shift is 0 */
diff --git a/src/cmd/link/internal/ld/ld_test.go b/src/cmd/link/internal/ld/ld_test.go
index ca764632c3..3702a4d08f 100644
--- a/src/cmd/link/internal/ld/ld_test.go
+++ b/src/cmd/link/internal/ld/ld_test.go
@@ -174,6 +174,8 @@ func TestWindowsBuildmodeCSharedASLR(t *testing.T) {
t.Skip("skipping windows amd64/386 only test")
}
+ testenv.MustHaveCGO(t)
+
t.Run("aslr", func(t *testing.T) {
testWindowsBuildmodeCSharedASLR(t, true)
})
diff --git a/src/crypto/x509/parser.go b/src/crypto/x509/parser.go
index 3d51ddd7f5..f085162a4e 100644
--- a/src/crypto/x509/parser.go
+++ b/src/crypto/x509/parser.go
@@ -734,10 +734,12 @@ func processExtensions(out *Certificate) error {
if !val.ReadASN1(&akid, cryptobyte_asn1.SEQUENCE) {
return errors.New("x509: invalid authority key identifier")
}
- if !akid.ReadASN1(&akid, cryptobyte_asn1.Tag(0).ContextSpecific()) {
- return errors.New("x509: invalid authority key identifier")
+ if akid.PeekASN1Tag(cryptobyte_asn1.Tag(0).ContextSpecific()) {
+ if !akid.ReadASN1(&akid, cryptobyte_asn1.Tag(0).ContextSpecific()) {
+ return errors.New("x509: invalid authority key identifier")
+ }
+ out.AuthorityKeyId = akid
}
- out.AuthorityKeyId = akid
case 37:
out.ExtKeyUsage, out.UnknownExtKeyUsage, err = parseExtKeyUsageExtension(e.Value)
if err != nil {
diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go
index 802bce0f9a..a4053abf41 100644
--- a/src/crypto/x509/x509_test.go
+++ b/src/crypto/x509/x509_test.go
@@ -3174,3 +3174,45 @@ func TestSigAlgMismatch(t *testing.T) {
}
}
}
+
+const optionalAuthKeyIDPEM = `-----BEGIN CERTIFICATE-----
+MIIFEjCCBHugAwIBAgICAQwwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1Zh
+bGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu
+Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24g
+QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAe
+BgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTA0MDYyOTE3MzkxNloX
+DTI0MDYyOTE3MzkxNlowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVs
+ZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAy
+IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0A
+MIIBCAKCAQEAtzLI/ulxpgSFrQwRZN/OTe/IAxiHP6Gr+zymn/DDodrU2G4rU5D7
+JKQ+hPCe6F/s5SdE9SimP3ve4CrwyK9TL57KBQGTHo9mHDmnTfpatnMEJWbrd3/n
+WcZKmSUUVOsmx/N/GdUwcI+vsEYq/63rKe3Xn6oEh6PU+YmlNF/bQ5GCNtlmPLG4
+uYL9nDo+EMg77wZlZnqbGRg9/3FRPDAuX749d3OyXQZswyNWmiuFJpIcpwKz5D8N
+rwh5grg2Peqc0zWzvGnK9cyd6P1kjReAM25eSl2ZyR6HtJ0awNVuEzUjXt+bXz3v
+1vd2wuo+u3gNHEJnawTY+Nbab4vyRKABqwIBA6OCAfMwggHvMB0GA1UdDgQWBBS/
+X7fRzt0fhvRbVazc1xDCDqmI5zCB0gYDVR0jBIHKMIHHoYHBpIG+MIG7MSQwIgYD
+VQQHExtWYWxpQ2VydCBWYWxpZGF0aW9uIE5ldHdvcmsxFzAVBgNVBAoTDlZhbGlD
+ZXJ0LCBJbmMuMTUwMwYDVQQLEyxWYWxpQ2VydCBDbGFzcyAyIFBvbGljeSBWYWxp
+ZGF0aW9uIEF1dGhvcml0eTEhMB8GA1UEAxMYaHR0cDovL3d3dy52YWxpY2VydC5j
+b20vMSAwHgYJKoZIhvcNAQkBFhFpbmZvQHZhbGljZXJ0LmNvbYIBATAPBgNVHRMB
+Af8EBTADAQH/MDkGCCsGAQUFBwEBBC0wKzApBggrBgEFBQcwAYYdaHR0cDovL29j
+c3Auc3RhcmZpZWxkdGVjaC5jb20wSgYDVR0fBEMwQTA/oD2gO4Y5aHR0cDovL2Nl
+cnRpZmljYXRlcy5zdGFyZmllbGR0ZWNoLmNvbS9yZXBvc2l0b3J5L3Jvb3QuY3Js
+MFEGA1UdIARKMEgwRgYEVR0gADA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY2VydGlm
+aWNhdGVzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkwDgYDVR0PAQH/BAQD
+AgEGMA0GCSqGSIb3DQEBBQUAA4GBAKVi8afCXSWlcD284ipxs33kDTcdVWptobCr
+mADkhWBKIMuh8D1195TaQ39oXCUIuNJ9MxB73HZn8bjhU3zhxoNbKXuNSm8uf0So
+GkVrMgfHeMpkksK0hAzc3S1fTbvdiuo43NlmouxBulVtWmQ9twPMHOKRUJ7jCUSV
+FxdzPcwl
+-----END CERTIFICATE-----`
+
+func TestAuthKeyIdOptional(t *testing.T) {
+ b, _ := pem.Decode([]byte(optionalAuthKeyIDPEM))
+ if b == nil {
+ t.Fatalf("couldn't decode test certificate")
+ }
+ _, err := ParseCertificate(b.Bytes)
+ if err != nil {
+ t.Fatalf("ParseCertificate to failed to parse certificate with optional authority key identifier fields: %s", err)
+ }
+}
diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go
index 503d0a6f44..d86a77a110 100644
--- a/src/go/types/stdlib_test.go
+++ b/src/go/types/stdlib_test.go
@@ -26,9 +26,15 @@ import (
. "go/types"
)
+// The cmd/*/internal packages may have been deleted as part of a binary
+// release. Import from source instead.
+//
+// (See https://golang.org/issue/43232 and
+// https://github.com/golang/build/blob/df58bbac082bc87c4a3cdfe336d1ffe60bbaa916/cmd/release/release.go#L533-L545.)
+//
// Use the same importer for all std lib tests to
// avoid repeated importing of the same packages.
-var stdLibImporter = importer.Default()
+var stdLibImporter = importer.ForCompiler(token.NewFileSet(), "source", nil)
func TestStdlib(t *testing.T) {
testenv.MustHaveGoBuild(t)
diff --git a/src/go/types/testdata/check/issues.src b/src/go/types/testdata/check/issues.src
index b0231548a7..ef1737331d 100644
--- a/src/go/types/testdata/check/issues.src
+++ b/src/go/types/testdata/check/issues.src
@@ -6,7 +6,7 @@ package go1_17 // don't permit non-interface elements in interfaces
import (
"fmt"
- syn "cmd/compile/internal/syntax"
+ syn "regexp/syntax"
t1 "text/template"
t2 "html/template"
)
@@ -329,10 +329,10 @@ func (... /* ERROR can only use ... with final parameter */ TT) f()
func issue28281g() (... /* ERROR can only use ... with final parameter */ TT)
// Issue #26234: Make various field/method lookup errors easier to read by matching cmd/compile's output
-func issue26234a(f *syn.File) {
+func issue26234a(f *syn.Prog) {
// The error message below should refer to the actual package name (syntax)
// not the local package name (syn).
- f.foo /* ERROR f\.foo undefined \(type \*syntax\.File has no field or method foo\) */
+ f.foo /* ERROR f\.foo undefined \(type \*syntax\.Prog has no field or method foo\) */
}
type T struct {
@@ -357,7 +357,7 @@ func issue35895() {
var _ T = 0 // ERROR cannot use 0 \(untyped int constant\) as T
// There is only one package with name syntax imported, only use the (global) package name in error messages.
- var _ *syn.File = 0 // ERROR cannot use 0 \(untyped int constant\) as \*syntax.File
+ var _ *syn.Prog = 0 // ERROR cannot use 0 \(untyped int constant\) as \*syntax.Prog
// Because both t1 and t2 have the same global package name (template),
// qualify packages with full path name in this case.
diff --git a/src/image/geom.go b/src/image/geom.go
index 78e9e49d4f..e71aa61187 100644
--- a/src/image/geom.go
+++ b/src/image/geom.go
@@ -246,6 +246,14 @@ func (r Rectangle) At(x, y int) color.Color {
return color.Transparent
}
+// RGBA64At implements the RGBA64Image interface.
+func (r Rectangle) RGBA64At(x, y int) color.RGBA64 {
+ if (Point{x, y}).In(r) {
+ return color.RGBA64{0xffff, 0xffff, 0xffff, 0xffff}
+ }
+ return color.RGBA64{}
+}
+
// Bounds implements the Image interface.
func (r Rectangle) Bounds() Rectangle {
return r
diff --git a/src/image/gif/reader.go b/src/image/gif/reader.go
index e580ab049e..9e8268c86f 100644
--- a/src/image/gif/reader.go
+++ b/src/image/gif/reader.go
@@ -116,7 +116,7 @@ type decoder struct {
// consumed when checking that the blockReader is exhausted.
//
// To avoid the allocation of a bufio.Reader for the lzw Reader, blockReader
-// implements io.ReadByte and buffers blocks into the decoder's "tmp" buffer.
+// implements io.ByteReader and buffers blocks into the decoder's "tmp" buffer.
type blockReader struct {
d *decoder
i, j uint8 // d.tmp[i:j] contains the buffered bytes
diff --git a/src/image/image_test.go b/src/image/image_test.go
index c64b6107b7..7f41bcb6c7 100644
--- a/src/image/image_test.go
+++ b/src/image/image_test.go
@@ -213,7 +213,9 @@ func TestRGBA64Image(t *testing.T) {
NewPaletted(r, palette.Plan9),
NewRGBA(r),
NewRGBA64(r),
+ NewUniform(color.RGBA64{}),
NewYCbCr(r, YCbCrSubsampleRatio444),
+ r,
}
for _, tc := range testCases {
switch tc := tc.(type) {
@@ -226,6 +228,9 @@ func TestRGBA64Image(t *testing.T) {
// means that setting one pixel can modify neighboring pixels. They
// don't have Set or SetRGBA64 methods because that side effect could
// be surprising. Here, we just memset the channel buffers instead.
+ //
+ // The Uniform and Rectangle types are also special-cased, as they
+ // don't have a Set or SetRGBA64 method.
case interface {
SetRGBA64(x, y int, c color.RGBA64)
}:
@@ -237,11 +242,18 @@ func TestRGBA64Image(t *testing.T) {
memset(tc.YCbCr.Cr, 0x99)
memset(tc.A, 0xAA)
+ case *Uniform:
+ tc.C = color.RGBA64{0x7FFF, 0x3FFF, 0x0000, 0x7FFF}
+
case *YCbCr:
memset(tc.Y, 0x77)
memset(tc.Cb, 0x88)
memset(tc.Cr, 0x99)
+ case Rectangle:
+ // No-op. Rectangle pixels' colors are immutable. They're always
+ // color.Opaque.
+
default:
t.Errorf("could not initialize pixels for %T", tc)
continue
diff --git a/src/image/names.go b/src/image/names.go
index 8595a35014..17b06588ac 100644
--- a/src/image/names.go
+++ b/src/image/names.go
@@ -41,6 +41,11 @@ func (c *Uniform) Bounds() Rectangle { return Rectangle{Point{-1e9, -1e9}, Point
func (c *Uniform) At(x, y int) color.Color { return c.C }
+func (c *Uniform) RGBA64At(x, y int) color.RGBA64 {
+ r, g, b, a := c.C.RGBA()
+ return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}
+}
+
// Opaque scans the entire image and reports whether it is fully opaque.
func (c *Uniform) Opaque() bool {
_, _, _, a := c.C.RGBA()
diff --git a/src/internal/buildcfg/exp.go b/src/internal/buildcfg/exp.go
index e87b6221f1..6e8bf30743 100644
--- a/src/internal/buildcfg/exp.go
+++ b/src/internal/buildcfg/exp.go
@@ -6,7 +6,6 @@ package buildcfg
import (
"fmt"
- "os"
"reflect"
"strings"
@@ -18,19 +17,19 @@ import (
//
// (This is not necessarily the set of experiments the compiler itself
// was built with.)
-var Experiment goexperiment.Flags = parseExperiments(GOARCH)
-
-var regabiSupported = GOARCH == "amd64" || GOARCH == "arm64"
-var regabiDeveloping = false
-
+//
// experimentBaseline specifies the experiment flags that are enabled by
// default in the current toolchain. This is, in effect, the "control"
// configuration and any variation from this is an experiment.
-var experimentBaseline = goexperiment.Flags{
- RegabiWrappers: regabiSupported,
- RegabiReflect: regabiSupported,
- RegabiArgs: regabiSupported,
-}
+var Experiment, experimentBaseline = func() (goexperiment.Flags, goexperiment.Flags) {
+ flags, baseline, err := ParseGOEXPERIMENT(GOOS, GOARCH, envOr("GOEXPERIMENT", defaultGOEXPERIMENT))
+ if err != nil {
+ Error = err
+ }
+ return flags, baseline
+}()
+
+const DefaultGOEXPERIMENT = defaultGOEXPERIMENT
// FramePointerEnabled enables the use of platform conventions for
// saving frame pointers.
@@ -41,16 +40,27 @@ var experimentBaseline = goexperiment.Flags{
// Note: must agree with runtime.framepointer_enabled.
var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64"
-func parseExperiments(goarch string) goexperiment.Flags {
+// ParseGOEXPERIMENT parses a (GOOS, GOARCH, GOEXPERIMENT)
+// configuration tuple and returns the enabled and baseline experiment
+// flag sets.
+//
+// TODO(mdempsky): Move to internal/goexperiment.
+func ParseGOEXPERIMENT(goos, goarch, goexp string) (flags, baseline goexperiment.Flags, err error) {
+ regabiSupported := goarch == "amd64" || goarch == "arm64"
+
+ baseline = goexperiment.Flags{
+ RegabiWrappers: regabiSupported,
+ RegabiReflect: regabiSupported,
+ RegabiArgs: regabiSupported,
+ }
+
// Start with the statically enabled set of experiments.
- flags := experimentBaseline
+ flags = baseline
// Pick up any changes to the baseline configuration from the
// GOEXPERIMENT environment. This can be set at make.bash time
// and overridden at build time.
- env := envOr("GOEXPERIMENT", defaultGOEXPERIMENT)
-
- if env != "" {
+ if goexp != "" {
// Create a map of known experiment names.
names := make(map[string]func(bool))
rv := reflect.ValueOf(&flags).Elem()
@@ -71,7 +81,7 @@ func parseExperiments(goarch string) goexperiment.Flags {
}
// Parse names.
- for _, f := range strings.Split(env, ",") {
+ for _, f := range strings.Split(goexp, ",") {
if f == "" {
continue
}
@@ -88,8 +98,8 @@ func parseExperiments(goarch string) goexperiment.Flags {
}
set, ok := names[f]
if !ok {
- fmt.Printf("unknown experiment %s\n", f)
- os.Exit(2)
+ err = fmt.Errorf("unknown GOEXPERIMENT %s", f)
+ return
}
set(val)
}
@@ -107,9 +117,9 @@ func parseExperiments(goarch string) goexperiment.Flags {
}
// Check regabi dependencies.
if flags.RegabiArgs && !(flags.RegabiWrappers && flags.RegabiReflect) {
- Error = fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers,regabireflect")
+ err = fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers,regabireflect")
}
- return flags
+ return
}
// expList returns the list of lower-cased experiment names for
@@ -165,6 +175,10 @@ func AllExperiments() []string {
// UpdateExperiments updates the Experiment global based on a new GOARCH value.
// This is only required for cmd/go, which can change GOARCH after
// program startup due to use of "go env -w".
-func UpdateExperiments(goarch string) {
- Experiment = parseExperiments(goarch)
+func UpdateExperiments(goos, goarch, goexperiment string) {
+ var err error
+ Experiment, experimentBaseline, err = ParseGOEXPERIMENT(goos, goarch, goexperiment)
+ if err != nil {
+ Error = err
+ }
}
diff --git a/src/math/big/int.go b/src/math/big/int.go
index 65f32487b5..7647346486 100644
--- a/src/math/big/int.go
+++ b/src/math/big/int.go
@@ -425,7 +425,7 @@ func (z *Int) SetString(s string, base int) (*Int, bool) {
return z.setFromScanner(strings.NewReader(s), base)
}
-// setFromScanner implements SetString given an io.BytesScanner.
+// setFromScanner implements SetString given an io.ByteScanner.
// For documentation see comments of SetString.
func (z *Int) setFromScanner(r io.ByteScanner, base int) (*Int, bool) {
if _, _, err := z.scan(r, base); err != nil {
diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go
index a59be7fea0..d69107a2f2 100644
--- a/src/net/dnsclient_unix_test.go
+++ b/src/net/dnsclient_unix_test.go
@@ -1921,12 +1921,12 @@ func TestCVE202133195(t *testing.T) {
t.Errorf("LookupSRV returned unexpected error, got %q, want %q", err, expected)
}
- _, _, err = r.LookupSRV(context.Background(), "hdr", "tcp", "golang.org")
- if expected := "lookup golang.org: SRV header name is invalid"; err == nil || err.Error() != expected {
+ _, _, err = r.LookupSRV(context.Background(), "hdr", "tcp", "golang.org.")
+ if expected := "lookup golang.org.: SRV header name is invalid"; err == nil || err.Error() != expected {
t.Errorf("Resolver.LookupSRV returned unexpected error, got %q, want %q", err, expected)
}
- _, _, err = LookupSRV("hdr", "tcp", "golang.org")
- if expected := "lookup golang.org: SRV header name is invalid"; err == nil || err.Error() != expected {
+ _, _, err = LookupSRV("hdr", "tcp", "golang.org.")
+ if expected := "lookup golang.org.: SRV header name is invalid"; err == nil || err.Error() != expected {
t.Errorf("LookupSRV returned unexpected error, got %q, want %q", err, expected)
}
diff --git a/src/os/example_test.go b/src/os/example_test.go
index 3adce51784..e8554b0b12 100644
--- a/src/os/example_test.go
+++ b/src/os/example_test.go
@@ -5,6 +5,7 @@
package os_test
import (
+ "errors"
"fmt"
"io/fs"
"log"
@@ -71,9 +72,9 @@ func ExampleFileMode() {
}
}
-func ExampleIsNotExist() {
+func ExampleErrNotExist() {
filename := "a-nonexistent-file"
- if _, err := os.Stat(filename); os.IsNotExist(err) {
+ if _, err := os.Stat(filename); errors.Is(err, fs.ErrNotExist) {
fmt.Println("file does not exist")
}
// Output:
diff --git a/src/path/filepath/path_test.go b/src/path/filepath/path_test.go
index cd107b6c85..bc5509b49c 100644
--- a/src/path/filepath/path_test.go
+++ b/src/path/filepath/path_test.go
@@ -1469,11 +1469,16 @@ func TestEvalSymlinksAboveRoot(t *testing.T) {
// 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 {
+ resolved, err := filepath.EvalSymlinks(check)
+ switch {
+ case runtime.GOOS == "darwin" && errors.Is(err, fs.ErrNotExist):
+ // On darwin, the temp dir is sometimes cleaned up mid-test (issue 37910).
+ testenv.SkipFlaky(t, 37910)
+ case err != nil:
t.Errorf("EvalSymlinks(%q) failed: %v", check, err)
- } else if !strings.HasSuffix(resolved, wantSuffix) {
+ case !strings.HasSuffix(resolved, wantSuffix):
t.Errorf("EvalSymlinks(%q) = %q does not end with %q", check, resolved, wantSuffix)
- } else {
+ default:
t.Logf("EvalSymlinks(%q) = %q", check, resolved)
}
}
diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go
index 34eec02d07..479878e6d2 100644
--- a/src/runtime/cgocall.go
+++ b/src/runtime/cgocall.go
@@ -111,6 +111,8 @@ func syscall_cgocaller(fn unsafe.Pointer, args ...uintptr) uintptr {
return as.retval
}
+var ncgocall uint64 // number of cgo calls in total for dead m
+
// Call from Go to C.
//
// This must be nosplit because it's used for syscalls on some
diff --git a/src/runtime/checkptr.go b/src/runtime/checkptr.go
index 59891a06a5..d42950844b 100644
--- a/src/runtime/checkptr.go
+++ b/src/runtime/checkptr.go
@@ -16,11 +16,30 @@ func checkptrAlignment(p unsafe.Pointer, elem *_type, n uintptr) {
}
// Check that (*[n]elem)(p) doesn't straddle multiple heap objects.
- if size := n * elem.size; size > 1 && checkptrBase(p) != checkptrBase(add(p, size-1)) {
+ // TODO(mdempsky): Fix #46938 so we don't need to worry about overflow here.
+ if checkptrStraddles(p, n*elem.size) {
throw("checkptr: converted pointer straddles multiple allocations")
}
}
+// checkptrStraddles reports whether the first size-bytes of memory
+// addressed by ptr is known to straddle more than one Go allocation.
+func checkptrStraddles(ptr unsafe.Pointer, size uintptr) bool {
+ if size <= 1 {
+ return false
+ }
+
+ end := add(ptr, size-1)
+ if uintptr(end) < uintptr(ptr) {
+ return true
+ }
+
+ // TODO(mdempsky): Detect when [ptr, end] contains Go allocations,
+ // but neither ptr nor end point into one themselves.
+
+ return checkptrBase(ptr) != checkptrBase(end)
+}
+
func checkptrArithmetic(p unsafe.Pointer, originals []unsafe.Pointer) {
if 0 < uintptr(p) && uintptr(p) < minLegalPointer {
throw("checkptr: pointer arithmetic computed bad pointer value")
diff --git a/src/runtime/checkptr_test.go b/src/runtime/checkptr_test.go
index 194cc1243a..2a5c364e97 100644
--- a/src/runtime/checkptr_test.go
+++ b/src/runtime/checkptr_test.go
@@ -30,6 +30,8 @@ func TestCheckPtr(t *testing.T) {
{"CheckPtrArithmetic2", "fatal error: checkptr: pointer arithmetic result points to invalid allocation\n"},
{"CheckPtrSize", "fatal error: checkptr: converted pointer straddles multiple allocations\n"},
{"CheckPtrSmall", "fatal error: checkptr: pointer arithmetic computed bad pointer value\n"},
+ {"CheckPtrSliceOK", ""},
+ {"CheckPtrSliceFail", "fatal error: checkptr: unsafe.Slice result straddles multiple allocations\n"},
}
for _, tc := range testCases {
diff --git a/src/runtime/debug.go b/src/runtime/debug.go
index f411b22676..82deefa200 100644
--- a/src/runtime/debug.go
+++ b/src/runtime/debug.go
@@ -45,7 +45,7 @@ func NumCPU() int {
// NumCgoCall returns the number of cgo calls made by the current process.
func NumCgoCall() int64 {
- var n int64
+ var n = int64(atomic.Load64(&ncgocall))
for mp := (*m)(atomic.Loadp(unsafe.Pointer(&allm))); mp != nil; mp = mp.alllink {
n += int64(mp.ncgocall)
}
diff --git a/src/runtime/internal/atomic/atomic_amd64.s b/src/runtime/internal/atomic/atomic_amd64.s
index 57cd59dd8c..d21514b36b 100644
--- a/src/runtime/internal/atomic/atomic_amd64.s
+++ b/src/runtime/internal/atomic/atomic_amd64.s
@@ -37,7 +37,7 @@ TEXT ·Cas(SB),NOSPLIT,$0-17
// bool ·Cas64(uint64 *val, uint64 old, uint64 new)
// Atomically:
-// if(*val == *old){
+// if(*val == old){
// *val = new;
// return 1;
// } else {
diff --git a/src/runtime/internal/atomic/atomic_arm64.s b/src/runtime/internal/atomic/atomic_arm64.s
index e9467afecd..5f77d92deb 100644
--- a/src/runtime/internal/atomic/atomic_arm64.s
+++ b/src/runtime/internal/atomic/atomic_arm64.s
@@ -192,7 +192,7 @@ ok:
// bool ·Cas64(uint64 *ptr, uint64 old, uint64 new)
// Atomically:
-// if(*val == *old){
+// if(*val == old){
// *val = new;
// return 1;
// } else {
diff --git a/src/runtime/internal/atomic/atomic_mips64x.s b/src/runtime/internal/atomic/atomic_mips64x.s
index fba668f94a..fedfc4a175 100644
--- a/src/runtime/internal/atomic/atomic_mips64x.s
+++ b/src/runtime/internal/atomic/atomic_mips64x.s
@@ -37,7 +37,7 @@ cas_fail:
// bool cas64(uint64 *ptr, uint64 old, uint64 new)
// Atomically:
-// if(*val == *old){
+// if(*val == old){
// *val = new;
// return 1;
// } else {
diff --git a/src/runtime/internal/atomic/atomic_ppc64x.s b/src/runtime/internal/atomic/atomic_ppc64x.s
index dca26cb334..226b3b6216 100644
--- a/src/runtime/internal/atomic/atomic_ppc64x.s
+++ b/src/runtime/internal/atomic/atomic_ppc64x.s
@@ -107,7 +107,7 @@ cas_fail:
// bool ·Cas64(uint64 *ptr, uint64 old, uint64 new)
// Atomically:
-// if(*val == *old){
+// if(*val == old){
// *val = new;
// return 1;
// } else {
diff --git a/src/runtime/internal/atomic/atomic_riscv64.s b/src/runtime/internal/atomic/atomic_riscv64.s
index ec05302a78..21d5adcdbc 100644
--- a/src/runtime/internal/atomic/atomic_riscv64.s
+++ b/src/runtime/internal/atomic/atomic_riscv64.s
@@ -30,8 +30,9 @@
#include "textflag.h"
+// func Cas(ptr *uint64, old, new uint64) bool
// Atomically:
-// if(*val == *old){
+// if(*val == old){
// *val = new;
// return 1;
// } else {
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index f13f7d20a3..4dc1811fc6 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -1510,6 +1510,8 @@ found:
}
unlock(&sched.lock)
+ atomic.Xadd64(&ncgocall, int64(m.ncgocall))
+
// Release the P.
handoffp(releasep())
// After this point we must not have write barriers.
diff --git a/src/runtime/race/syso_test.go b/src/runtime/race/syso_test.go
index cbce5a8f18..f5095737a4 100644
--- a/src/runtime/race/syso_test.go
+++ b/src/runtime/race/syso_test.go
@@ -2,14 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build !android && !js && !ppc64le
-// +build !android,!js,!ppc64le
-
-// Note: we don't run on Android or ppc64 because if there is any non-race test
-// file in this package, the OS tries to link the .syso file into the
-// test (even when we're not in race mode), which fails. I'm not sure
-// why, but easiest to just punt - as long as a single builder runs
-// this test, we're good.
+//go:build race
+// +build race
package race
diff --git a/src/runtime/slice.go b/src/runtime/slice.go
index 66e3aff1a4..e8267be885 100644
--- a/src/runtime/slice.go
+++ b/src/runtime/slice.go
@@ -114,19 +114,37 @@ func makeslice64(et *_type, len64, cap64 int64) unsafe.Pointer {
return makeslice(et, len, cap)
}
-func unsafeslice(et *_type, len int) {
+func unsafeslice(et *_type, ptr unsafe.Pointer, len int) {
+ if len == 0 {
+ return
+ }
+
+ if ptr == nil {
+ panic(errorString("unsafe.Slice: ptr is nil and len is not zero"))
+ }
+
mem, overflow := math.MulUintptr(et.size, uintptr(len))
if overflow || mem > maxAlloc || len < 0 {
panicunsafeslicelen()
}
}
-func unsafeslice64(et *_type, len64 int64) {
+func unsafeslice64(et *_type, ptr unsafe.Pointer, len64 int64) {
len := int(len64)
if int64(len) != len64 {
panicunsafeslicelen()
}
- unsafeslice(et, len)
+ unsafeslice(et, ptr, len)
+}
+
+func unsafeslicecheckptr(et *_type, ptr unsafe.Pointer, len64 int64) {
+ unsafeslice64(et, ptr, len64)
+
+ // Check that underlying array doesn't straddle multiple heap objects.
+ // unsafeslice64 has already checked for overflow.
+ if checkptrStraddles(ptr, uintptr(len64)*et.size) {
+ throw("checkptr: unsafe.Slice result straddles multiple allocations")
+ }
}
func panicunsafeslicelen() {
diff --git a/src/runtime/testdata/testprog/checkptr.go b/src/runtime/testdata/testprog/checkptr.go
index e0a2794f4c..f76b64ad96 100644
--- a/src/runtime/testdata/testprog/checkptr.go
+++ b/src/runtime/testdata/testprog/checkptr.go
@@ -13,6 +13,8 @@ func init() {
register("CheckPtrArithmetic2", CheckPtrArithmetic2)
register("CheckPtrSize", CheckPtrSize)
register("CheckPtrSmall", CheckPtrSmall)
+ register("CheckPtrSliceOK", CheckPtrSliceOK)
+ register("CheckPtrSliceFail", CheckPtrSliceFail)
}
func CheckPtrAlignmentNoPtr() {
@@ -49,3 +51,14 @@ func CheckPtrSize() {
func CheckPtrSmall() {
sink2 = unsafe.Pointer(uintptr(1))
}
+
+func CheckPtrSliceOK() {
+ p := new([4]int64)
+ sink2 = unsafe.Slice(&p[1], 3)
+}
+
+func CheckPtrSliceFail() {
+ p := new(int64)
+ sink2 = p
+ sink2 = unsafe.Slice(p, 100)
+}
diff --git a/src/unsafe/unsafe.go b/src/unsafe/unsafe.go
index ecbd28c523..eaf72c9618 100644
--- a/src/unsafe/unsafe.go
+++ b/src/unsafe/unsafe.go
@@ -221,8 +221,11 @@ func Add(ptr Pointer, len IntegerType) Pointer
//
// (*[len]ArbitraryType)(unsafe.Pointer(ptr))[:]
//
+// As a special case, if ptr is nil and len is zero, Slice returns nil.
+//
// The len argument must be of integer type or an untyped constant.
// A constant len argument must be non-negative and representable by a value of type int;
// if it is an untyped constant it is given type int.
-// If ptr is nil or len is negative at run time, a run-time panic occurs.
+// At run time, if len is negative, or if ptr is nil and len is not zero,
+// a run-time panic occurs.
func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType
diff --git a/test/fixedbugs/issue46957.go b/test/fixedbugs/issue46957.go
new file mode 100644
index 0000000000..f3ed3c3def
--- /dev/null
+++ b/test/fixedbugs/issue46957.go
@@ -0,0 +1,13 @@
+// errorcheck
+
+// Copyright 2021 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
+
+func f(a int, b ...int) {}
+
+func main() {
+ f(nil...) // ERROR "not enough arguments in call to f$"
+}
diff --git a/test/unsafebuiltins.go b/test/unsafebuiltins.go
index c10f8084a7..4c940aa855 100644
--- a/test/unsafebuiltins.go
+++ b/test/unsafebuiltins.go
@@ -30,8 +30,11 @@ func main() {
assert(len(s) == len(p))
assert(cap(s) == len(p))
- // nil pointer
- mustPanic(func() { _ = unsafe.Slice((*int)(nil), 0) })
+ // nil pointer with zero length returns nil
+ assert(unsafe.Slice((*int)(nil), 0) == nil)
+
+ // nil pointer with positive length panics
+ mustPanic(func() { _ = unsafe.Slice((*int)(nil), 1) })
// negative length
var neg int = -1