Merge "[dev.typeparams] all: merge master (8212707) into dev.typeparams" into dev.typeparams

This commit is contained in:
Gerrit Code Review 2021-06-07 19:48:09 +00:00
commit f0c97219a3
33 changed files with 525 additions and 169 deletions

View file

@ -224,12 +224,6 @@ Do not send CLs removing the interior tags from such phrases.
TODO: complete the Vet section
</p>
<h2 id="runtime">Runtime</h2>
<p>
TODO: complete the Runtime section
</p>
<h2 id="compiler">Compiler</h2>
<p><!-- golang.org/issue/40724 -->
@ -434,7 +428,7 @@ Do not send CLs removing the interior tags from such phrases.
<dl id="flag"><dt><a href="/pkg/flag/">flag</a></dt>
<dd>
<p><!-- CL 271788 -->
TODO: <a href="https://golang.org/cl/271788">https://golang.org/cl/271788</a>: panic if flag name begins with - or contains =
Flag declarations now panic if an invalid name is specified.
</p>
</dd>
</dl><!-- flag -->
@ -651,15 +645,22 @@ Do not send CLs removing the interior tags from such phrases.
DragonFly and all OpenBSD systems (it was already defined on
some OpenBSD systems and all FreeBSD, NetBSD, and Linux systems).
</p>
<p><!-- CL 315281 -->
The constants <code>SYS_WAIT6</code> and <code>WEXITED</code>
are now defined on NetBSD systems (<code>SYS_WAIT6</code> was
already defined on DragonFly and FreeBSD systems;
<code>WEXITED</code> was already defined on Darwin, DragonFly,
FreeBSD, Linux, and Solaris systems).
</p>
</dd>
</dl><!-- syscall -->
<dl id="testing"><dt><a href="/pkg/testing/">testing</a></dt>
<dd>
<p><!-- CL 310033 -->
TODO: <a href="https://golang.org/cl/310033">https://golang.org/cl/310033</a>: add -shuffle=off|on|N to alter the execution order of tests and benchmarks
Added a new <a href="/cmd/go/#hdr-Testing_flags">testing flag</a> <code>-shuffle</code> which controls the execution order of tests and benchmarks.
</p>
<p><!-- CL 260577 -->
The new
<a href="/pkg/testing/#T.Setenv"><code>T.Setenv</code></a>
@ -689,15 +690,26 @@ Do not send CLs removing the interior tags from such phrases.
</p>
<p><!-- CL 264077 -->
TODO: <a href="https://golang.org/cl/264077">https://golang.org/cl/264077</a>: add Time.IsDST() to check if its Location is in Daylight Savings Time
The new <a href="/pkg/time/#Time.IsDST"><code>Time.IsDST</code></a> method can be used to check whether the time
is in Daylight Savings Time in its configured location.
</p>
<p><!-- CL 293349 -->
TODO: <a href="https://golang.org/cl/293349">https://golang.org/cl/293349</a>: add Time.Unix{Milli,Micro} and to-Time helpers UnixMicro, UnixMilli
The new <a href="/pkg/time/#Time.UnixMilli"><code>Time.UnixMilli</code></a> and
<a href="/pkg/time/#Time.UnixMicro"><code>Time.UnixMicro</code></a> methods return the number of milliseconds and
microseconds elapsed since January 1, 1970 UTC respectively.<br>
The new <code>UnixMilli</code> and <code>UnixMicro</code> functions return local Time corresponding to given
Unix time.
</p>
<p><!-- CL 300996 -->
TODO: <a href="https://golang.org/cl/300996">https://golang.org/cl/300996</a>: support &#34;,&#34; as separator for fractional seconds
The package now accepts comma "," as a separator for fractional seconds when parsing and formatting time.
The following time formats are now accepted:
<ul>
<li>2006-01-02 14:06:03,999999999 -0700 MST</li>
<li>Mon Jan _2 14:06:03,120007 2006</li>
<li>Mon Jan 2 14:06:03,120007 2006</li>
</ul>
</p>
<p><!-- CL 320252 -->

View file

@ -1,6 +1,6 @@
<!--{
"Title": "The Go Programming Language Specification",
"Subtitle": "Version of Apr 28, 2021",
"Subtitle": "Version of Jun 2, 2021",
"Path": "/ref/spec"
}-->
@ -4909,7 +4909,7 @@ if x := f(); x &lt; y {
<p>
"Switch" statements provide multi-way execution.
An expression or type specifier is compared to the "cases"
An expression or type is compared to the "cases"
inside the "switch" to determine which branch
to execute.
</p>
@ -5020,7 +5020,7 @@ floating point, or string constants in case expressions.
A type switch compares types rather than values. It is otherwise similar
to an expression switch. It is marked by a special switch expression that
has the form of a <a href="#Type_assertions">type assertion</a>
using the reserved word <code>type</code> rather than an actual type:
using the keyword <code>type</code> rather than an actual type:
</p>
<pre>

View file

@ -40,7 +40,8 @@ func check(t *testing.T, file string) {
if len(frags) == 1 {
continue
}
re, err := regexp.Compile(string(frags[1]))
frag := fmt.Sprintf(":%d:.*%s", i+1, frags[1])
re, err := regexp.Compile(frag)
if err != nil {
t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frags[1])
continue

View file

@ -40,15 +40,15 @@ func main() {
C.foop = x // ERROR HERE
// issue 13129: used to output error about C.unsignedshort with CC=clang
var x C.ushort
x = int(0) // ERROR HERE: C\.ushort
var x1 C.ushort
x1 = int(0) // ERROR HERE: C\.ushort
// issue 13423
_ = C.fopen() // ERROR HERE
// issue 13467
var x rune = '✈'
var _ rune = C.transform(x) // ERROR HERE: C\.int
var x2 rune = '✈'
var _ rune = C.transform(x2) // ERROR HERE: C\.int
// issue 13635: used to output error about C.unsignedchar.
// This test tests all such types.
@ -91,10 +91,10 @@ func main() {
// issue 26745
_ = func(i int) int {
return C.i + 1 // ERROR HERE: :13
return C.i + 1 // ERROR HERE: 14
}
_ = func(i int) {
C.fi(i) // ERROR HERE: :6
C.fi(i) // ERROR HERE: 7
}
C.fi = C.fi // ERROR HERE

View file

@ -1638,6 +1638,8 @@ func (p *Package) gccCmd() []string {
c = append(c, "-maix64")
c = append(c, "-mcmodel=large")
}
// disable LTO so we get an object whose symbols we can read
c = append(c, "-fno-lto")
c = append(c, "-") //read input from standard input
return c
}

View file

@ -168,8 +168,18 @@ func (p *Package) writeDefs() {
if *gccgo {
fmt.Fprintf(fc, "extern byte *%s;\n", n.C)
} else {
fmt.Fprintf(fm, "extern char %s[];\n", n.C)
fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C)
// Force a reference to all symbols so that
// the external linker will add DT_NEEDED
// entries as needed on ELF systems.
// Treat function variables differently
// to avoid type confict errors from LTO
// (Link Time Optimization).
if n.Kind == "fpvar" {
fmt.Fprintf(fm, "extern void %s();\n", n.C)
} else {
fmt.Fprintf(fm, "extern char %s[];\n", n.C)
fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C)
}
fmt.Fprintf(fgo2, "//go:linkname __cgo_%s %s\n", n.C, n.C)
fmt.Fprintf(fgo2, "//go:cgo_import_static %s\n", n.C)
fmt.Fprintf(fgo2, "var __cgo_%s byte\n", n.C)
@ -1042,7 +1052,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
// This unpacks the argument struct above and calls the Go function.
fmt.Fprintf(fgo2, "func _cgoexp%s_%s(a *%s) {\n", cPrefix, exp.ExpName, gotype)
fmt.Fprintf(fm, "int _cgoexp%s_%s;\n", cPrefix, exp.ExpName)
fmt.Fprintf(fm, "void _cgoexp%s_%s(void* p){}\n", cPrefix, exp.ExpName)
if gccResult != "void" {
// Write results back to frame.

View file

@ -449,7 +449,7 @@ func (config *ABIConfig) ABIAnalyze(t *types.Type, setNname bool) *ABIParamResul
// parameterUpdateMu protects the Offset field of function/method parameters (a subset of structure Fields)
var parameterUpdateMu sync.Mutex
// FieldOffsetOf returns a concurency-safe version of f.Offset
// FieldOffsetOf returns a concurrency-safe version of f.Offset
func FieldOffsetOf(f *types.Field) int64 {
parameterUpdateMu.Lock()
defer parameterUpdateMu.Unlock()

View file

@ -886,9 +886,6 @@ func (p *noder) typeExpr(typ syntax.Expr) ir.Ntype {
if n == nil {
return nil
}
if _, ok := n.(ir.Ntype); !ok {
ir.Dump("NOT NTYPE", n)
}
return n.(ir.Ntype)
}

View file

@ -999,6 +999,12 @@ func tcRecover(n *ir.CallExpr) ir.Node {
// tcUnsafeAdd typechecks an OUNSAFEADD node.
func tcUnsafeAdd(n *ir.BinaryExpr) *ir.BinaryExpr {
if !types.AllowsGoVersion(curpkg(), 1, 17) {
base.ErrorfVers("go1.17", "unsafe.Add")
n.SetType(nil)
return n
}
n.X = AssignConv(Expr(n.X), types.Types[types.TUNSAFEPTR], "argument to unsafe.Add")
n.Y = DefaultLit(Expr(n.Y), types.Types[types.TINT])
if n.X.Type() == nil || n.Y.Type() == nil {
@ -1015,6 +1021,12 @@ func tcUnsafeAdd(n *ir.BinaryExpr) *ir.BinaryExpr {
// tcUnsafeSlice typechecks an OUNSAFESLICE node.
func tcUnsafeSlice(n *ir.BinaryExpr) *ir.BinaryExpr {
if !types.AllowsGoVersion(curpkg(), 1, 17) {
base.ErrorfVers("go1.17", "unsafe.Slice")
n.SetType(nil)
return n
}
n.X = Expr(n.X)
n.Y = Expr(n.Y)
if n.X.Type() == nil || n.Y.Type() == nil {

View file

@ -579,6 +579,11 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
case _Add:
// unsafe.Add(ptr unsafe.Pointer, len IntegerType) unsafe.Pointer
if !check.allowVersion(check.pkg, 1, 17) {
check.error(call.Fun, "unsafe.Add requires go1.17 or later")
return
}
check.assignment(x, Typ[UnsafePointer], "argument to unsafe.Add")
if x.mode == invalid {
return
@ -675,6 +680,11 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
case _Slice:
// unsafe.Slice(ptr *T, len IntegerType) []T
if !check.allowVersion(check.pkg, 1, 17) {
check.error(call.Fun, "unsafe.Slice requires go1.17 or later")
return
}
typ := asPointer(x.typ)
if typ == nil {
check.errorf(x, invalidArg+"%s is not a pointer", x)

29
src/cmd/dist/test.go vendored
View file

@ -722,14 +722,29 @@ func (t *tester) registerTests() {
},
})
if t.hasCxx() {
t.tests = append(t.tests, distTest{
name: "swig_callback",
heading: "../misc/swig/callback",
fn: func(dt *distTest) error {
t.addCmd(dt, "misc/swig/callback", t.goTest())
return nil
t.tests = append(t.tests,
distTest{
name: "swig_callback",
heading: "../misc/swig/callback",
fn: func(dt *distTest) error {
t.addCmd(dt, "misc/swig/callback", t.goTest())
return nil
},
},
})
distTest{
name: "swig_callback_lto",
heading: "../misc/swig/callback",
fn: func(dt *distTest) error {
cmd := t.addCmd(dt, "misc/swig/callback", t.goTest())
cmd.Env = append(os.Environ(),
"CGO_CFLAGS=-flto",
"CGO_CXXFLAGS=-flto",
"CGO_LDFLAGS=-flto",
)
return nil
},
},
)
}
}
}

141
src/cmd/link/cgo_test.go Normal file
View file

@ -0,0 +1,141 @@
// 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
import (
"bytes"
"fmt"
"internal/testenv"
"os"
"os/exec"
"path/filepath"
"testing"
)
// Issues 43830, 46295
func TestCGOLTO(t *testing.T) {
testenv.MustHaveCGO(t)
testenv.MustHaveGoBuild(t)
t.Parallel()
for _, cc := range []string{"gcc", "clang"} {
for test := 0; test < 2; test++ {
t.Run(fmt.Sprintf("%s-%d", cc, test), func(t *testing.T) {
testCGOLTO(t, cc, test)
})
}
}
}
const test1_main = `
package main
/*
extern int myadd(int, int);
int c_add(int a, int b) {
return myadd(a, b);
}
*/
import "C"
func main() {
println(C.c_add(1, 2))
}
`
const test1_add = `
package main
import "C"
/* test */
//export myadd
func myadd(a C.int, b C.int) C.int {
return a + b
}
`
const test2_main = `
package main
import "fmt"
/*
#include <stdio.h>
void hello(void) {
printf("hello\n");
}
*/
import "C"
func main() {
hello := C.hello
fmt.Printf("%v\n", hello)
}
`
func testCGOLTO(t *testing.T, cc string, test int) {
t.Parallel()
if _, err := exec.LookPath(cc); err != nil {
t.Skipf("no %s compiler", cc)
}
dir := t.TempDir()
writeTempFile := func(name, contents string) {
if err := os.WriteFile(filepath.Join(dir, name), []byte(contents), 0644); err != nil {
t.Fatal(err)
}
}
writeTempFile("go.mod", "module cgolto\n")
switch test {
case 0:
writeTempFile("main.go", test1_main)
writeTempFile("add.go", test1_add)
case 1:
writeTempFile("main.go", test2_main)
default:
t.Fatalf("bad case %d", test)
}
cmd := exec.Command(testenv.GoToolPath(t), "build")
cmd.Dir = dir
cmd.Env = append(os.Environ(),
"CC="+cc,
"CGO_CFLAGS=-flto",
)
t.Log("go build")
out, err := cmd.CombinedOutput()
t.Logf("%s", out)
if err != nil {
t.Logf("go build failed: %v", err)
// Error messages we've seen indicating that LTO is not supported.
// These errors come from GCC or clang, not Go.
var noLTO = []string{
`unrecognized command line option "-flto"`,
"unable to pass LLVM bit-code files to linker",
"file not recognized: File format not recognized",
"LTO support has not been enabled",
"linker command failed with exit code",
"gcc: can't load library",
}
for _, msg := range noLTO {
if bytes.Contains(out, []byte(msg)) {
t.Skipf("C compiler %v does not support LTO", cc)
}
}
t.Error("failed")
}
}

View file

@ -124,6 +124,10 @@ func hostArchive(ctxt *Link, name string) {
libgcc := sym.Library{Pkg: "libgcc"}
h := ldobj(ctxt, f, &libgcc, l, pname, name)
if h.ld == nil {
Errorf(nil, "%s unrecognized object file at offset %d", name, off)
continue
}
f.MustSeek(h.off, 0)
h.ld(ctxt, f, h.pkg, h.length, h.pn)
}

View file

@ -241,6 +241,10 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) {
return true, "dynamically linking with a shared library"
}
if unknownObjFormat {
return true, "some input objects have an unrecognized file format"
}
return false, ""
}
@ -248,7 +252,7 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) {
//
// It is called after flags are processed and inputs are processed,
// so the ctxt.LinkMode variable has an initial value from the -linkmode
// flag and the iscgo externalobj variables are set.
// flag and the iscgo, externalobj, and unknownObjFormat variables are set.
func determineLinkMode(ctxt *Link) {
extNeeded, extReason := mustLinkExternal(ctxt)
via := ""

View file

@ -343,10 +343,16 @@ var (
const pkgdef = "__.PKGDEF"
var (
// Set if we see an object compiled by the host compiler that is not
// from a package that is known to support internal linking mode.
// externalobj is set to true if we see an object compiled by
// the host compiler that is not from a package that is known
// to support internal linking mode.
externalobj = false
theline string
// unknownObjFormat is set to true if we see an object whose
// format we don't recognize.
unknownObjFormat = false
theline string
)
func Lflag(ctxt *Link, arg string) {
@ -1065,6 +1071,10 @@ func hostobjs(ctxt *Link) {
}
f.MustSeek(h.off, 0)
if h.ld == nil {
Errorf(nil, "%s: unrecognized object file format", h.pn)
continue
}
h.ld(ctxt, f, h.pkg, h.length, h.pn)
f.Close()
}
@ -1855,6 +1865,14 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
}
if c1 != 'g' || c2 != 'o' || c3 != ' ' || c4 != 'o' {
// An unrecognized object is just passed to the external linker.
// If we try to read symbols from this object, we will
// report an error at that time.
unknownObjFormat = true
return ldhostobj(nil, ctxt.HeadType, f, pkg, length, pn, file)
}
/* check the header */
line, err := f.ReadString('\n')
if err != nil {
@ -1874,7 +1892,7 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
return nil
}
Errorf(nil, "%s: not an object file: @%d %02x%02x%02x%02x", pn, start, c1, c2, c3, c4)
Errorf(nil, "%s: not an object file: @%d %q", pn, start, line)
return nil
}

View file

@ -475,7 +475,7 @@ func (f *peFile) addDWARFSection(name string, size int) *peSection {
off := f.stringTable.add(name)
h := f.addSection(name, size, size)
h.shortName = fmt.Sprintf("/%d", off)
h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_CNT_INITIALIZED_DATA
h.characteristics = IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_CNT_INITIALIZED_DATA
return h
}

View file

@ -455,7 +455,7 @@ func initP384() {
// Multiple invocations of this function will return the same value, so it can
// be used for equality checks and switch statements.
//
// The cryptographic operations are implemented using constant-time algorithms.
// ScalarMult and ScalarBaseMult are implemented using constant-time algorithms.
func P256() Curve {
initonce.Do(initAll)
return p256
@ -479,7 +479,7 @@ func P384() Curve {
// Multiple invocations of this function will return the same value, so it can
// be used for equality checks and switch statements.
//
// The cryptographic operations do not use constant-time algorithms.
// The cryptographic operations are implemented using constant-time algorithms.
func P521() Curve {
initonce.Do(initAll)
return p521

View file

@ -619,7 +619,7 @@ type Config struct {
// protocol will be one from this list, and the connection will fail
// if there is no mutually supported protocol. If NextProtos is empty
// or the peer doesn't support ALPN, the connection will succeed and
// ConnectionState.NegotiatedProtocol will be empty."
// ConnectionState.NegotiatedProtocol will be empty.
NextProtos []string
// ServerName is used to verify the hostname on the returned

View file

@ -189,7 +189,7 @@
When printing a struct, fmt cannot and therefore does not invoke
formatting methods such as Error or String on unexported fields.
Explicit argument indexes:
Explicit argument indexes
In Printf, Sprintf, and Fprintf, the default behavior is for each
formatting verb to format successive arguments passed in the call.
@ -211,7 +211,7 @@
fmt.Sprintf("%d %d %#[1]x %#x", 16, 17)
will yield "16 17 0x10 0x11".
Format errors:
Format errors
If an invalid argument is given for a verb, such as providing
a string to %d, the generated string will contain a

View file

@ -145,17 +145,14 @@ func Import(fset *token.FileSet, packages map[string]*types.Package, path, srcDi
err = fmt.Errorf("import %q: old textual export format no longer supported (recompile library)", path)
case "$$B\n":
var data []byte
data, err = io.ReadAll(buf)
if err != nil {
break
}
var exportFormat byte
exportFormat, err = buf.ReadByte()
// The indexed export format starts with an 'i'; the older
// binary export format starts with a 'c', 'd', or 'v'
// (from "version"). Select appropriate importer.
if len(data) > 0 && data[0] == 'i' {
_, pkg, err = iImportData(fset, packages, data[1:], id)
if err == nil && exportFormat == 'i' {
pkg, err = iImportData(fset, packages, buf, id)
} else {
err = fmt.Errorf("import %q: old binary export format no longer supported (recompile library)", path)
}

View file

@ -8,6 +8,7 @@
package gcimporter
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
@ -20,7 +21,7 @@ import (
)
type intReader struct {
*bytes.Reader
*bufio.Reader
path string
}
@ -73,7 +74,7 @@ const (
// and returns the number of bytes consumed and a reference to the package.
// If the export data version is not recognized or the format is otherwise
// compromised, an error is returned.
func iImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
func iImportData(fset *token.FileSet, imports map[string]*types.Package, dataReader *bufio.Reader, path string) (pkg *types.Package, err error) {
const currentVersion = iexportVersionCurrent
version := int64(-1)
defer func() {
@ -86,7 +87,7 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, data []
}
}()
r := &intReader{bytes.NewReader(data), path}
r := &intReader{dataReader, path}
version = int64(r.uint64())
switch version {
@ -102,10 +103,12 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, data []
sLen := int64(r.uint64())
dLen := int64(r.uint64())
whence, _ := r.Seek(0, io.SeekCurrent)
stringData := data[whence : whence+sLen]
declData := data[whence+sLen : whence+sLen+dLen]
r.Seek(sLen+dLen, io.SeekCurrent)
data := make([]byte, sLen+dLen)
if _, err := io.ReadFull(r, data); err != nil {
errorf("cannot read %d bytes of stringData and declData: %s", len(data), err)
}
stringData := data[:sLen]
declData := data[sLen:]
p := iimporter{
exportVersion: version,
@ -182,9 +185,7 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, data []
// package was imported completely and without errors
localpkg.MarkComplete()
consumed, _ := r.Seek(0, io.SeekCurrent)
return int(consumed), localpkg, nil
return localpkg, nil
}
type iimporter struct {

View file

@ -588,6 +588,11 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
case _Add:
// unsafe.Add(ptr unsafe.Pointer, len IntegerType) unsafe.Pointer
if !check.allowVersion(check.pkg, 1, 17) {
check.errorf(call.Fun, _InvalidUnsafeAdd, "unsafe.Add requires go1.17 or later")
return
}
check.assignment(x, Typ[UnsafePointer], "argument to unsafe.Add")
if x.mode == invalid {
return
@ -684,6 +689,11 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
case _Slice:
// unsafe.Slice(ptr *T, len IntegerType) []T
if !check.allowVersion(check.pkg, 1, 17) {
check.errorf(call.Fun, _InvalidUnsafeSlice, "unsafe.Slice requires go1.17 or later")
return
}
typ := asPointer(x.typ)
if typ == nil {
check.invalidArg(x, _InvalidUnsafeSlice, "%s is not a pointer", x)

View file

@ -19,10 +19,10 @@ type SubFS interface {
// Sub returns an FS corresponding to the subtree rooted at fsys's dir.
//
// If fs implements SubFS, Sub calls returns fsys.Sub(dir).
// Otherwise, if dir is ".", Sub returns fsys unchanged.
// If dir is ".", Sub returns fsys unchanged.
// Otherwise, if fs implements SubFS, Sub returns fsys.Sub(dir).
// Otherwise, Sub returns a new FS implementation sub that,
// in effect, implements sub.Open(dir) as fsys.Open(path.Join(dir, name)).
// in effect, implements sub.Open(name) as fsys.Open(path.Join(dir, name)).
// The implementation also translates calls to ReadDir, ReadFile, and Glob appropriately.
//
// Note that Sub(os.DirFS("/"), "prefix") is equivalent to os.DirFS("/prefix")

View file

@ -1898,61 +1898,62 @@ func TestCVE202133195(t *testing.T) {
// Change the default resolver to match our manipulated resolver
originalDefault := DefaultResolver
DefaultResolver = &r
defer func() {
DefaultResolver = originalDefault
}()
defer func() { DefaultResolver = originalDefault }()
// Redirect host file lookups.
defer func(orig string) { testHookHostsPath = orig }(testHookHostsPath)
testHookHostsPath = "testdata/hosts"
_, err := r.LookupCNAME(context.Background(), "golang.org")
if expected := "lookup golang.org: CNAME target is invalid"; err == nil || err.Error() != expected {
t.Errorf("Resolver.LookupCNAME returned unexpected error, got %q, want %q", err.Error(), expected)
t.Errorf("Resolver.LookupCNAME returned unexpected error, got %q, want %q", err, expected)
}
_, err = LookupCNAME("golang.org")
if expected := "lookup golang.org: CNAME target is invalid"; err == nil || err.Error() != expected {
t.Errorf("LookupCNAME returned unexpected error, got %q, want %q", err.Error(), expected)
t.Errorf("LookupCNAME returned unexpected error, got %q, want %q", err, expected)
}
_, _, err = r.LookupSRV(context.Background(), "target", "tcp", "golang.org")
if expected := "lookup golang.org: SRV target is invalid"; err == nil || err.Error() != expected {
t.Errorf("Resolver.LookupSRV returned unexpected error, got %q, want %q", err.Error(), expected)
t.Errorf("Resolver.LookupSRV returned unexpected error, got %q, want %q", err, expected)
}
_, _, err = LookupSRV("target", "tcp", "golang.org")
if expected := "lookup golang.org: SRV target is invalid"; err == nil || err.Error() != expected {
t.Errorf("LookupSRV returned unexpected error, got %q, want %q", err.Error(), expected)
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 {
t.Errorf("Resolver.LookupSRV returned unexpected error, got %q, want %q", 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 {
t.Errorf("LookupSRV returned unexpected error, got %q, want %q", err.Error(), expected)
t.Errorf("LookupSRV returned unexpected error, got %q, want %q", err, expected)
}
_, err = r.LookupMX(context.Background(), "golang.org")
if expected := "lookup golang.org: MX target is invalid"; err == nil || err.Error() != expected {
t.Errorf("Resolver.LookupMX returned unexpected error, got %q, want %q", err.Error(), expected)
t.Errorf("Resolver.LookupMX returned unexpected error, got %q, want %q", err, expected)
}
_, err = LookupMX("golang.org")
if expected := "lookup golang.org: MX target is invalid"; err == nil || err.Error() != expected {
t.Errorf("LookupMX returned unexpected error, got %q, want %q", err.Error(), expected)
t.Errorf("LookupMX returned unexpected error, got %q, want %q", err, expected)
}
_, err = r.LookupNS(context.Background(), "golang.org")
if expected := "lookup golang.org: NS target is invalid"; err == nil || err.Error() != expected {
t.Errorf("Resolver.LookupNS returned unexpected error, got %q, want %q", err.Error(), expected)
t.Errorf("Resolver.LookupNS returned unexpected error, got %q, want %q", err, expected)
}
_, err = LookupNS("golang.org")
if expected := "lookup golang.org: NS target is invalid"; err == nil || err.Error() != expected {
t.Errorf("LookupNS returned unexpected error, got %q, want %q", err.Error(), expected)
t.Errorf("LookupNS returned unexpected error, got %q, want %q", err, expected)
}
_, err = r.LookupAddr(context.Background(), "1.2.3.4")
if expected := "lookup 1.2.3.4: PTR target is invalid"; err == nil || err.Error() != expected {
t.Errorf("Resolver.LookupAddr returned unexpected error, got %q, want %q", err.Error(), expected)
_, err = r.LookupAddr(context.Background(), "192.0.2.42")
if expected := "lookup 192.0.2.42: PTR target is invalid"; err == nil || err.Error() != expected {
t.Errorf("Resolver.LookupAddr returned unexpected error, got %q, want %q", err, expected)
}
_, err = LookupAddr("1.2.3.4")
if expected := "lookup 1.2.3.4: PTR target is invalid"; err == nil || err.Error() != expected {
t.Errorf("LookupAddr returned unexpected error, got %q, want %q", err.Error(), expected)
_, err = LookupAddr("192.0.2.42")
if expected := "lookup 192.0.2.42: PTR target is invalid"; err == nil || err.Error() != expected {
t.Errorf("LookupAddr returned unexpected error, got %q, want %q", err, expected)
}
}

View file

@ -577,37 +577,17 @@ func (w *response) ReadFrom(src io.Reader) (n int64, err error) {
return io.CopyBuffer(writerOnly{w}, src, buf)
}
// sendfile path:
// Do not start actually writing response until src is readable.
// If body length is <= sniffLen, sendfile/splice path will do
// little anyway. This small read also satisfies sniffing the
// body in case Content-Type is missing.
nr, er := src.Read(buf[:sniffLen])
atEOF := errors.Is(er, io.EOF)
n += int64(nr)
if nr > 0 {
// Write the small amount read normally.
nw, ew := w.Write(buf[:nr])
if ew != nil {
err = ew
} else if nr != nw {
err = io.ErrShortWrite
// Copy the first sniffLen bytes before switching to ReadFrom.
// This ensures we don't start writing the response before the
// source is available (see golang.org/issue/5660) and provides
// enough bytes to perform Content-Type sniffing when required.
if !w.cw.wroteHeader {
n0, err := io.CopyBuffer(writerOnly{w}, io.LimitReader(src, sniffLen), buf)
n += n0
if err != nil || n0 < sniffLen {
return n, err
}
}
if err == nil && er != nil && !atEOF {
err = er
}
// Do not send StatusOK in the error case where nothing has been written.
if err == nil && !w.wroteHeader {
w.WriteHeader(StatusOK) // nr == 0, no error (or EOF)
}
if err != nil || atEOF {
return n, err
}
w.w.Flush() // get rid of any previous writes
w.cw.flush() // make sure Header is written; flush data to rwc
@ -620,7 +600,7 @@ func (w *response) ReadFrom(src io.Reader) (n int64, err error) {
return n, err
}
n0, err := io.Copy(writerOnly{w}, src)
n0, err := io.CopyBuffer(writerOnly{w}, src, buf)
n += n0
return n, err
}

View file

@ -157,9 +157,25 @@ func testServerIssue5953(t *testing.T, h2 bool) {
resp.Body.Close()
}
func TestContentTypeWithCopy_h1(t *testing.T) { testContentTypeWithCopy(t, h1Mode) }
func TestContentTypeWithCopy_h2(t *testing.T) { testContentTypeWithCopy(t, h2Mode) }
func testContentTypeWithCopy(t *testing.T, h2 bool) {
type byteAtATimeReader struct {
buf []byte
}
func (b *byteAtATimeReader) Read(p []byte) (n int, err error) {
if len(p) < 1 {
return 0, nil
}
if len(b.buf) == 0 {
return 0, io.EOF
}
p[0] = b.buf[0]
b.buf = b.buf[1:]
return 1, nil
}
func TestContentTypeWithVariousSources_h1(t *testing.T) { testContentTypeWithVariousSources(t, h1Mode) }
func TestContentTypeWithVariousSources_h2(t *testing.T) { testContentTypeWithVariousSources(t, h2Mode) }
func testContentTypeWithVariousSources(t *testing.T, h2 bool) {
defer afterTest(t)
const (
@ -167,30 +183,86 @@ func testContentTypeWithCopy(t *testing.T, h2 bool) {
expected = "text/html; charset=utf-8"
)
cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
// Use io.Copy from a bytes.Buffer to trigger ReadFrom.
buf := bytes.NewBuffer([]byte(input))
n, err := io.Copy(w, buf)
if int(n) != len(input) || err != nil {
t.Errorf("io.Copy(w, %q) = %v, %v want %d, nil", input, n, err, len(input))
}
}))
defer cst.close()
for _, test := range []struct {
name string
handler func(ResponseWriter, *Request)
}{{
name: "write",
handler: func(w ResponseWriter, r *Request) {
// Write the whole input at once.
n, err := w.Write([]byte(input))
if int(n) != len(input) || err != nil {
t.Errorf("w.Write(%q) = %v, %v want %d, nil", input, n, err, len(input))
}
},
}, {
name: "write one byte at a time",
handler: func(w ResponseWriter, r *Request) {
// Write the input one byte at a time.
buf := []byte(input)
for i := range buf {
n, err := w.Write(buf[i : i+1])
if n != 1 || err != nil {
t.Errorf("w.Write(%q) = %v, %v want 1, nil", input, n, err)
}
}
},
}, {
name: "copy from Reader",
handler: func(w ResponseWriter, r *Request) {
// Use io.Copy from a plain Reader.
type readerOnly struct{ io.Reader }
buf := bytes.NewBuffer([]byte(input))
n, err := io.Copy(w, readerOnly{buf})
if int(n) != len(input) || err != nil {
t.Errorf("io.Copy(w, %q) = %v, %v want %d, nil", input, n, err, len(input))
}
},
}, {
name: "copy from bytes.Buffer",
handler: func(w ResponseWriter, r *Request) {
// Use io.Copy from a bytes.Buffer to trigger ReadFrom.
buf := bytes.NewBuffer([]byte(input))
n, err := io.Copy(w, buf)
if int(n) != len(input) || err != nil {
t.Errorf("io.Copy(w, %q) = %v, %v want %d, nil", input, n, err, len(input))
}
},
}, {
name: "copy one byte at a time",
handler: func(w ResponseWriter, r *Request) {
// Use io.Copy from a Reader that returns one byte at a time.
n, err := io.Copy(w, &byteAtATimeReader{[]byte(input)})
if int(n) != len(input) || err != nil {
t.Errorf("io.Copy(w, %q) = %v, %v want %d, nil", input, n, err, len(input))
}
},
}} {
t.Run(test.name, func(t *testing.T) {
cst := newClientServerTest(t, h2, HandlerFunc(test.handler))
defer cst.close()
resp, err := cst.c.Get(cst.ts.URL)
if err != nil {
t.Fatalf("Get: %v", err)
}
if ct := resp.Header.Get("Content-Type"); ct != expected {
t.Errorf("Content-Type = %q, want %q", ct, expected)
}
if want, got := resp.Header.Get("Content-Length"), fmt.Sprint(len(input)); want != got {
t.Errorf("Content-Length = %q, want %q", want, got)
}
data, err := io.ReadAll(resp.Body)
if err != nil {
t.Errorf("reading body: %v", err)
} else if !bytes.Equal(data, []byte(input)) {
t.Errorf("data is %q, want %q", data, input)
}
resp.Body.Close()
})
resp, err := cst.c.Get(cst.ts.URL)
if err != nil {
t.Fatalf("Get: %v", err)
}
if ct := resp.Header.Get("Content-Type"); ct != expected {
t.Errorf("Content-Type = %q, want %q", ct, expected)
}
data, err := io.ReadAll(resp.Body)
if err != nil {
t.Errorf("reading body: %v", err)
} else if !bytes.Equal(data, []byte(input)) {
t.Errorf("data is %q, want %q", data, input)
}
resp.Body.Close()
}
func TestSniffWriteSize_h1(t *testing.T) { testSniffWriteSize(t, h1Mode) }

View file

@ -45,16 +45,6 @@ func (p *Process) wait() (ps *ProcessState, err error) {
return &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil
}
func terminateProcess(pid, exitcode int) error {
h, e := syscall.OpenProcess(syscall.PROCESS_TERMINATE, false, uint32(pid))
if e != nil {
return NewSyscallError("OpenProcess", e)
}
defer syscall.CloseHandle(h)
e = syscall.TerminateProcess(h, uint32(exitcode))
return NewSyscallError("TerminateProcess", e)
}
func (p *Process) signal(sig Signal) error {
handle := atomic.LoadUintptr(&p.handle)
if handle == uintptr(syscall.InvalidHandle) {
@ -64,16 +54,22 @@ func (p *Process) signal(sig Signal) error {
return ErrProcessDone
}
if sig == Kill {
err := terminateProcess(p.Pid, 1)
var terminationHandle syscall.Handle
e := syscall.DuplicateHandle(^syscall.Handle(0), syscall.Handle(handle), ^syscall.Handle(0), &terminationHandle, syscall.PROCESS_TERMINATE, false, 0)
if e != nil {
return NewSyscallError("DuplicateHandle", e)
}
runtime.KeepAlive(p)
return err
defer syscall.CloseHandle(terminationHandle)
e = syscall.TerminateProcess(syscall.Handle(terminationHandle), 1)
return NewSyscallError("TerminateProcess", e)
}
// TODO(rsc): Handle Interrupt too?
return syscall.Errno(syscall.EWINDOWS)
}
func (p *Process) release() error {
handle := atomic.LoadUintptr(&p.handle)
handle := atomic.SwapUintptr(&p.handle, uintptr(syscall.InvalidHandle))
if handle == uintptr(syscall.InvalidHandle) {
return syscall.EINVAL
}
@ -81,7 +77,6 @@ func (p *Process) release() error {
if e != nil {
return NewSyscallError("CloseHandle", e)
}
atomic.StoreUintptr(&p.handle, uintptr(syscall.InvalidHandle))
// no need for a finalizer anymore
runtime.SetFinalizer(p, nil)
return nil

View file

@ -48,18 +48,21 @@ func main() {
if target == "nacl" {
continue
}
var buf bytes.Buffer
fmt.Fprintf(&buf, "// Code generated by gengoos.go using 'go generate'. DO NOT EDIT.\n\n")
var tags []string
if target == "linux" {
fmt.Fprintf(&buf, "// +build !android\n") // must explicitly exclude android for linux
tags = append(tags, "!android") // must explicitly exclude android for linux
}
if target == "solaris" {
fmt.Fprintf(&buf, "// +build !illumos\n") // must explicitly exclude illumos for solaris
tags = append(tags, "!illumos") // must explicitly exclude illumos for solaris
}
if target == "darwin" {
fmt.Fprintf(&buf, "// +build !ios\n") // must explicitly exclude ios for darwin
tags = append(tags, "!ios") // must explicitly exclude ios for darwin
}
fmt.Fprintf(&buf, "// +build %s\n\n", target) // must explicitly include target for bootstrapping purposes
tags = append(tags, target) // must explicitly include target for bootstrapping purposes
var buf bytes.Buffer
fmt.Fprintf(&buf, "// Code generated by gengoos.go using 'go generate'. DO NOT EDIT.\n\n")
fmt.Fprintf(&buf, "//go:build %s\n", strings.Join(tags, " && "))
fmt.Fprintf(&buf, "// +build %s\n\n", strings.Join(tags, ","))
fmt.Fprintf(&buf, "package sys\n\n")
fmt.Fprintf(&buf, "const GOOS = `%s`\n\n", target)
for _, goos := range gooses {
@ -81,6 +84,7 @@ func main() {
}
var buf bytes.Buffer
fmt.Fprintf(&buf, "// Code generated by gengoos.go using 'go generate'. DO NOT EDIT.\n\n")
fmt.Fprintf(&buf, "//go:build %s\n", target)
fmt.Fprintf(&buf, "// +build %s\n\n", target) // must explicitly include target for bootstrapping purposes
fmt.Fprintf(&buf, "package sys\n\n")
fmt.Fprintf(&buf, "const GOARCH = `%s`\n\n", target)

View file

@ -4071,8 +4071,16 @@ func exitsyscall0(gp *g) {
if schedEnabled(gp) {
_p_ = pidleget()
}
var locked bool
if _p_ == nil {
globrunqput(gp)
// Below, we stoplockedm if gp is locked. globrunqput releases
// ownership of gp, so we must check if gp is locked prior to
// committing the release by unlocking sched.lock, otherwise we
// could race with another M transitioning gp from unlocked to
// locked.
locked = gp.lockedm != 0
} else if atomic.Load(&sched.sysmonwait) != 0 {
atomic.Store(&sched.sysmonwait, 0)
notewakeup(&sched.sysmonnote)
@ -4082,7 +4090,7 @@ func exitsyscall0(gp *g) {
acquirep(_p_)
execute(gp, false) // Never returns.
}
if gp.lockedm != 0 {
if locked {
// Wait until another thread schedules gp and so m again.
//
// N.B. lockedm must be this M, as this g was running on this M

View file

@ -313,6 +313,17 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
}
}
var maj, min, build uint32
rtlGetNtVersionNumbers(&maj, &min, &build)
isWin7 := maj < 6 || (maj == 6 && min <= 1)
// NT kernel handles are divisible by 4, with the bottom 3 bits left as
// a tag. The fully set tag correlates with the types of handles we're
// concerned about here. Except, the kernel will interpret some
// special handle values, like -1, -2, and so forth, so kernelbase.dll
// checks to see that those bottom three bits are checked, but that top
// bit is not checked.
isLegacyWin7ConsoleHandle := func(handle Handle) bool { return isWin7 && handle&0x10000003 == 3 }
p, _ := GetCurrentProcess()
parentProcess := p
if sys.ParentProcess != 0 {
@ -321,7 +332,15 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
fd := make([]Handle, len(attr.Files))
for i := range attr.Files {
if attr.Files[i] > 0 {
err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
destinationProcessHandle := parentProcess
// On Windows 7, console handles aren't real handles, and can only be duplicated
// into the current process, not a parent one, which amounts to the same thing.
if parentProcess != p && isLegacyWin7ConsoleHandle(Handle(attr.Files[i])) {
destinationProcessHandle = p
}
err := DuplicateHandle(p, Handle(attr.Files[i]), destinationProcessHandle, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
if err != nil {
return 0, 0, err
}
@ -351,19 +370,40 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
si.StdErr = fd[2]
fd = append(fd, sys.AdditionalInheritedHandles...)
// On Windows 7, console handles aren't real handles, so don't pass them
// through to PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
for i := range fd {
if isLegacyWin7ConsoleHandle(fd[i]) {
fd[i] = 0
}
}
// The presence of a NULL handle in the list is enough to cause PROC_THREAD_ATTRIBUTE_HANDLE_LIST
// to treat the entire list as empty, so remove NULL handles.
j := 0
for i := range fd {
if fd[i] != 0 {
fd[j] = fd[i]
j++
}
}
fd = fd[:j]
// Do not accidentally inherit more than these handles.
err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&fd[0]), uintptr(len(fd))*unsafe.Sizeof(fd[0]), nil, nil)
if err != nil {
return 0, 0, err
if len(fd) > 0 {
err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&fd[0]), uintptr(len(fd))*unsafe.Sizeof(fd[0]), nil, nil)
if err != nil {
return 0, 0, err
}
}
pi := new(ProcessInformation)
flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT | _EXTENDED_STARTUPINFO_PRESENT
if sys.Token != 0 {
err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi)
err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, len(fd) > 0 && !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi)
} else {
err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi)
err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, len(fd) > 0 && !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi)
}
if err != nil {
return 0, 0, err

View file

@ -198,6 +198,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
//sys FreeLibrary(handle Handle) (err error)
//sys GetProcAddress(module Handle, procname string) (proc uintptr, err error)
//sys GetVersion() (ver uint32, err error)
//sys rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) = ntdll.RtlGetNtVersionNumbers
//sys formatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) = FormatMessageW
//sys ExitProcess(exitcode uint32)
//sys CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle Handle, err error) [failretval==InvalidHandle] = CreateFileW

View file

@ -43,6 +43,7 @@ var (
modkernel32 = NewLazyDLL(sysdll.Add("kernel32.dll"))
modmswsock = NewLazyDLL(sysdll.Add("mswsock.dll"))
modnetapi32 = NewLazyDLL(sysdll.Add("netapi32.dll"))
modntdll = NewLazyDLL(sysdll.Add("ntdll.dll"))
modsecur32 = NewLazyDLL(sysdll.Add("secur32.dll"))
modshell32 = NewLazyDLL(sysdll.Add("shell32.dll"))
moduserenv = NewLazyDLL(sysdll.Add("userenv.dll"))
@ -167,6 +168,7 @@ var (
procNetApiBufferFree = modnetapi32.NewProc("NetApiBufferFree")
procNetGetJoinInformation = modnetapi32.NewProc("NetGetJoinInformation")
procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo")
procRtlGetNtVersionNumbers = modntdll.NewProc("RtlGetNtVersionNumbers")
procGetUserNameExW = modsecur32.NewProc("GetUserNameExW")
procTranslateNameW = modsecur32.NewProc("TranslateNameW")
procCommandLineToArgvW = modshell32.NewProc("CommandLineToArgvW")
@ -1213,6 +1215,11 @@ func NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **by
return
}
func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) {
Syscall(procRtlGetNtVersionNumbers.Addr(), 3, uintptr(unsafe.Pointer(majorVersion)), uintptr(unsafe.Pointer(minorVersion)), uintptr(unsafe.Pointer(buildNumber)))
return
}
func GetUserNameEx(nameFormat uint32, nameBuffre *uint16, nSize *uint32) (err error) {
r1, _, e1 := Syscall(procGetUserNameExW.Addr(), 3, uintptr(nameFormat), uintptr(unsafe.Pointer(nameBuffre)), uintptr(unsafe.Pointer(nSize)))
if r1&0xff == 0 {

View file

@ -0,0 +1,14 @@
// errorcheck -lang=go1.16
// 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 p
import "unsafe"
func main() {
_ = unsafe.Add(unsafe.Pointer(nil), 0) // ERROR "unsafe.Add requires go1.17 or later"
_ = unsafe.Slice(new(byte), 1) // ERROR "unsafe.Slice requires go1.17 or later"
}