diff --git a/doc/asm.html b/doc/asm.html index cce2fe2b8d..392af174c2 100644 --- a/doc/asm.html +++ b/doc/asm.html @@ -621,6 +621,15 @@ These modes accept only 1, 2, 4, and 8 as scale factors. +

+When using the compiler and assembler's +-dynlink or -shared modes, +any load or store of a fixed memory location such as a global variable +must be assumed to overwrite CX. +Therefore, to be safe for use with these modes, +assembly sources should typically avoid CX except between memory references. +

+

64-bit Intel 386 (a.k.a. amd64)

diff --git a/doc/contribute.html b/doc/contribute.html index a321a8646f..4619c81124 100644 --- a/doc/contribute.html +++ b/doc/contribute.html @@ -198,9 +198,13 @@ prints help text, not an error.

-Note to Git aficionados: The git-codereview command is not required to +Note to Git aficionados: +The git-codereview command is not required to upload and manage Gerrit code reviews. For those who prefer plain Git, the text -below gives the Git equivalent of each git-codereview command. If you do use plain +below gives the Git equivalent of each git-codereview command. +

+ +

If you do use plain Git, note that you still need the commit hooks that the git-codereview command configures; those hooks add a Gerrit Change-Id line to the commit message and check that all Go source files have been formatted with gofmt. Even @@ -208,6 +212,12 @@ if you intend to use plain Git for daily work, install the hooks in a new Git checkout by running git-codereview hooks.

+

+The workflow described below assumes a single change per branch. +It is also possible to prepare a sequence of (usually related) changes in a single branch. +See the git-codereview documentation for details. +

+

Set up git aliases

diff --git a/doc/devel/release.html b/doc/devel/release.html index ad060a8ce6..e0c2ac0219 100644 --- a/doc/devel/release.html +++ b/doc/devel/release.html @@ -30,6 +30,13 @@ to fix critical security problems in both Go 1.4 and Go 1.5 as they arise. See the security policy for more details.

+

go1.6 (released 2016/02/17)

+ +

+Go 1.6 is a major release of Go. +Read the Go 1.6 Release Notes for more information. +

+

go1.5 (released 2015/08/19)

diff --git a/doc/go1.6.html b/doc/go1.6.html index 6e3710cbad..17c3536aeb 100644 --- a/doc/go1.6.html +++ b/doc/go1.6.html @@ -1,5 +1,5 @@ @@ -13,13 +13,6 @@ Edit .,s;^([a-z][A-Za-z0-9_/]+)\.([A-Z][A-Za-z0-9_]+\.)?([A-Z][A-Za-z0-9_]+)([ . ul li { margin: 0.5em 0; } -

-NOTE: This is a DRAFT of the Go 1.6 release notes, prepared for the Go 1.6 beta. -Go 1.6 has NOT yet been released. -By our regular schedule, it is expected some time in February 2016. - -

-

Introduction to Go 1.6

@@ -70,9 +63,12 @@ On NaCl, Go 1.5 required SDK version pepper-41. Go 1.6 adds support for later SDK versions.

-
-TODO: CX no longer available on 386 assembly? (https://golang.org/cl/16386)
-
+

+On 32-bit x86 systems using the -dynlink or -shared compilation modes, +the register CX is now overwritten by certain memory references and should +be avoided in hand-written assembly. +See the assembly documentation for details. +

Tools

@@ -248,7 +244,7 @@ Some programs may run faster, some slower. On average the programs in the Go 1 benchmark suite run a few percent faster in Go 1.6 than they did in Go 1.5. The garbage collector's pauses are even lower than in Go 1.5, -although the effect is likely only noticeable for programs using +especially for programs using a large amount of memory.

@@ -569,7 +565,7 @@ The debug/elf package adds support for general compressed ELF sections. User code needs no updating: the sections are decompressed automatically when read. However, compressed -Section's do not support random access: +Sections do not support random access: they have a nil ReaderAt field. @@ -632,7 +628,6 @@ In previous releases, the argument to * was required to have type < Also in the fmt package, Scanf can now scan hexadecimal strings using %X, as an alias for %x. Both formats accept any mix of upper- and lower-case hexadecimal. -TODO: Keep?
  • @@ -717,9 +712,6 @@ Second, DNS lookup functions such as LookupAddr now return rooted domain names (with a trailing dot) on Plan 9 and Windows, to match the behavior of Go on Unix systems. -TODO: Third, lookups satisfied from /etc/hosts now add a trailing dot as well, -so that looking up 127.0.0.1 typically now returns “localhost.” not “localhost”. -This is arguably a mistake but is not yet fixed. See https://golang.org/issue/13564.
  • diff --git a/doc/go1.7.txt b/doc/go1.7.txt new file mode 100644 index 0000000000..15efa287ce --- /dev/null +++ b/doc/go1.7.txt @@ -0,0 +1,14 @@ +Tools: + +cmd/go: GO15VENDOREXPERIMENT gone, assumed on (CL 19615) +cmd/link: "-X name value" form gone (CL 19614) + +Ports: + +SOMETHING WILL HAPPEN + +API additions and behavior changes: + +SOMETHING WILL HAPPEN + + diff --git a/doc/go_spec.html b/doc/go_spec.html index 6b6e75c7c9..437fc066bb 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,6 +1,6 @@ @@ -2443,9 +2443,8 @@ PrimaryExpr = Selector = "." identifier . Index = "[" Expression "]" . -Slice = "[" ( [ Expression ] ":" [ Expression ] ) | - ( [ Expression ] ":" Expression ":" Expression ) - "]" . +Slice = "[" [ Expression ] ":" [ Expression ] "]" | + "[" [ Expression ] ":" Expression ":" Expression "]" . TypeAssertion = "." "(" Type ")" . Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" . diff --git a/misc/cgo/test/issue13930.go b/misc/cgo/test/issue13930.go new file mode 100644 index 0000000000..3a22459e68 --- /dev/null +++ b/misc/cgo/test/issue13930.go @@ -0,0 +1,13 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 13930. Test that cgo's multiple-value special form for +// C function calls works in variable declaration statements. + +package cgotest + +// #include +import "C" + +var _, _ = C.abs(0) diff --git a/misc/nacl/testzip.proto b/misc/nacl/testzip.proto index b51e8da5c7..dcb156350a 100644 --- a/misc/nacl/testzip.proto +++ b/misc/nacl/testzip.proto @@ -27,6 +27,18 @@ go src=.. internal objfile objfile.go + unvendor + golang.org + x + arch + arm + armasm + testdata + + + x86 + x86asm + testdata + + gofmt gofmt.go gofmt_test.go @@ -35,18 +47,6 @@ go src=.. newlink testdata + - vendor - golang.org - x - arch - arm - armasm - testdata - + - x86 - x86asm - testdata - + archive tar testdata diff --git a/src/archive/zip/writer.go b/src/archive/zip/writer.go index 5ce66e6be5..3a9292e380 100644 --- a/src/archive/zip/writer.go +++ b/src/archive/zip/writer.go @@ -52,7 +52,7 @@ func (w *Writer) Flush() error { } // Close finishes writing the zip file by writing the central directory. -// It does not (and can not) close the underlying writer. +// It does not (and cannot) close the underlying writer. func (w *Writer) Close() error { if w.last != nil && !w.last.closed { if err := w.last.close(); err != nil { diff --git a/src/bytes/buffer.go b/src/bytes/buffer.go index ddaba3bff3..f135b46959 100644 --- a/src/bytes/buffer.go +++ b/src/bytes/buffer.go @@ -17,7 +17,7 @@ import ( type Buffer struct { buf []byte // contents are the bytes buf[off : len(buf)] off int // read at &buf[off], write at &buf[len(buf)] - runeBytes [utf8.UTFMax]byte // avoid allocation of slice on each WriteByte or Rune + runeBytes [utf8.UTFMax]byte // avoid allocation of slice on each call to WriteRune bootstrap [64]byte // memory to hold first slice; helps small buffers (Printf) avoid allocation. lastRead readOp // last read operation, so that Unread* can work correctly. } diff --git a/src/cmd/api/goapi_test.go b/src/cmd/api/goapi_test.go index 1d2cc9ac82..a3fe0efedc 100644 --- a/src/cmd/api/goapi_test.go +++ b/src/cmd/api/goapi_test.go @@ -178,7 +178,7 @@ func BenchmarkAll(b *testing.B) { for _, context := range contexts { w := NewWalker(context, filepath.Join(build.Default.GOROOT, "src")) for _, name := range pkgNames { - if name != "unsafe" && !strings.HasPrefix(name, "cmd/") { + if name != "unsafe" && !strings.HasPrefix(name, "cmd/") && !internalPkg.MatchString(name) { pkg, _ := w.Import(name) w.export(pkg) } diff --git a/src/cmd/asm/internal/arch/arch.go b/src/cmd/asm/internal/arch/arch.go index c14a13cdb1..f9436cb7f2 100644 --- a/src/cmd/asm/internal/arch/arch.go +++ b/src/cmd/asm/internal/arch/arch.go @@ -162,8 +162,6 @@ func archX86(linkArch *obj.LinkArch) *Arch { instructions["MOVDQ2Q"] = x86.AMOVQ instructions["MOVNTDQ"] = x86.AMOVNTO instructions["MOVOA"] = x86.AMOVO - instructions["PF2ID"] = x86.APF2IL - instructions["PI2FD"] = x86.API2FL instructions["PSLLDQ"] = x86.APSLLO instructions["PSRLDQ"] = x86.APSRLO instructions["PADDD"] = x86.APADDL diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go index c3a24c2b76..4c5dc9a23d 100644 --- a/src/cmd/cgo/ast.go +++ b/src/cmd/cgo/ast.go @@ -447,7 +447,11 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{} case *ast.ImportSpec: case *ast.ValueSpec: f.walk(&n.Type, "type", visit) - f.walk(n.Values, "expr", visit) + if len(n.Names) == 2 && len(n.Values) == 1 { + f.walk(&n.Values[0], "as2", visit) + } else { + f.walk(n.Values, "expr", visit) + } case *ast.TypeSpec: f.walk(&n.Type, "type", visit) diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index bd38a5c153..90c2584c7f 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -133,7 +133,7 @@ C's union types are represented as a Go byte array with the same length. Go structs cannot embed fields with C types. -Go code can not refer to zero-sized fields that occur at the end of +Go code cannot refer to zero-sized fields that occur at the end of non-empty C structs. To get the address of such a field (which is the only operation you can do with a zero-sized field) you must take the address of the struct and add the size of the struct. @@ -148,8 +148,9 @@ assignment context to retrieve both the return value (if any) and the C errno variable as an error (use _ to skip the result value if the function returns void). For example: - n, err := C.sqrt(-1) + n, err = C.sqrt(-1) _, err := C.voidFunc() + var n, err = C.sqrt(1) Calling C function pointers is currently not supported, however you can declare Go variables which hold C function pointers and pass them diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index fb5049c1a1..5bfdef785c 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -432,7 +432,7 @@ func (p *Package) loadDWARF(f *File, names []*Name) { fmt.Fprintf(&b, "\t0,\n") } } - // for the last entry, we can not use 0, otherwise + // for the last entry, we cannot use 0, otherwise // in case all __cgodebug_data is zero initialized, // LLVM-based gcc will place the it in the __DATA.__common // zero-filled section (our debug/macho doesn't support @@ -2025,7 +2025,7 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct // We can't permit that, because then the size of the Go // struct will not be the same as the size of the C struct. // Our only option in such a case is to remove the field, - // which means that it can not be referenced from Go. + // which means that it cannot be referenced from Go. for off > 0 && sizes[len(sizes)-1] == 0 { n := len(sizes) fld = fld[0 : n-1] diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go index 3adb8e8783..52ca160ad9 100644 --- a/src/cmd/cgo/util.go +++ b/src/cmd/cgo/util.go @@ -8,6 +8,7 @@ import ( "bytes" "fmt" "go/token" + "io/ioutil" "os" "os/exec" ) @@ -16,6 +17,43 @@ import ( // It returns the output to standard output and standard error. // ok indicates whether the command exited successfully. func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { + if i := find(argv, "-xc"); i >= 0 && argv[len(argv)-1] == "-" { + // Some compilers have trouble with standard input. + // Others have trouble with -xc. + // Avoid both problems by writing a file with a .c extension. + f, err := ioutil.TempFile("", "cgo-gcc-input-") + if err != nil { + fatalf("%s", err) + } + name := f.Name() + f.Close() + if err := ioutil.WriteFile(name+".c", stdin, 0666); err != nil { + os.Remove(name) + fatalf("%s", err) + } + defer os.Remove(name) + defer os.Remove(name + ".c") + + // Build new argument list without -xc and trailing -. + new := append(argv[:i:i], argv[i+1:len(argv)-1]...) + + // Since we are going to write the file to a temporary directory, + // we will need to add -I . explicitly to the command line: + // any #include "foo" before would have looked in the current + // directory as the directory "holding" standard input, but now + // the temporary directory holds the input. + // We've also run into compilers that reject "-I." but allow "-I", ".", + // so be sure to use two arguments. + // This matters mainly for people invoking cgo -godefs by hand. + new = append(new, "-I", ".") + + // Finish argument list with path to C file. + new = append(new, name+".c") + + argv = new + stdin = nil + } + p := exec.Command(argv[0], argv[1:]...) p.Stdin = bytes.NewReader(stdin) var bout, berr bytes.Buffer @@ -30,6 +68,15 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { return } +func find(argv []string, target string) int { + for i, arg := range argv { + if arg == target { + return i + } + } + return -1 +} + func lineno(pos token.Pos) string { return fset.Position(pos).String() } diff --git a/src/cmd/compile/internal/arm/prog.go b/src/cmd/compile/internal/arm/prog.go index 81be77a5b0..49a329b535 100644 --- a/src/cmd/compile/internal/arm/prog.go +++ b/src/cmd/compile/internal/arm/prog.go @@ -98,11 +98,11 @@ var progtable = [arm.ALAST]obj.ProgInfo{ arm.AMOVH: {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move}, arm.AMOVW: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move}, - // In addtion, duffzero reads R0,R1 and writes R1. This fact is + // In addition, duffzero reads R0,R1 and writes R1. This fact is // encoded in peep.c obj.ADUFFZERO: {Flags: gc.Call}, - // In addtion, duffcopy reads R1,R2 and writes R0,R1,R2. This fact is + // In addition, duffcopy reads R1,R2 and writes R0,R1,R2. This fact is // encoded in peep.c obj.ADUFFCOPY: {Flags: gc.Call}, diff --git a/src/cmd/compile/internal/big/example_rat_test.go b/src/cmd/compile/internal/big/example_rat_test.go new file mode 100644 index 0000000000..ef0649785a --- /dev/null +++ b/src/cmd/compile/internal/big/example_rat_test.go @@ -0,0 +1,65 @@ +// Copyright 2015 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 big_test + +import ( + "cmd/compile/internal/big" + "fmt" +) + +// Use the classic continued fraction for e +// e = [1; 0, 1, 1, 2, 1, 1, ... 2n, 1, 1, ...] +// i.e., for the nth term, use +// 1 if n mod 3 != 1 +// (n-1)/3 * 2 if n mod 3 == 1 +func recur(n, lim int64) *big.Rat { + term := new(big.Rat) + if n%3 != 1 { + term.SetInt64(1) + } else { + term.SetInt64((n - 1) / 3 * 2) + } + + if n > lim { + return term + } + + // Directly initialize frac as the fractional + // inverse of the result of recur. + frac := new(big.Rat).Inv(recur(n+1, lim)) + + return term.Add(term, frac) +} + +// This example demonstrates how to use big.Rat to compute the +// first 15 terms in the sequence of rational convergents for +// the constant e (base of natural logarithm). +func Example_eConvergents() { + for i := 1; i <= 15; i++ { + r := recur(0, int64(i)) + + // Print r both as a fraction and as a floating-point number. + // Since big.Rat implements fmt.Formatter, we can use %-13s to + // get a left-aligned string representation of the fraction. + fmt.Printf("%-13s = %s\n", r, r.FloatString(8)) + } + + // Output: + // 2/1 = 2.00000000 + // 3/1 = 3.00000000 + // 8/3 = 2.66666667 + // 11/4 = 2.75000000 + // 19/7 = 2.71428571 + // 87/32 = 2.71875000 + // 106/39 = 2.71794872 + // 193/71 = 2.71830986 + // 1264/465 = 2.71827957 + // 1457/536 = 2.71828358 + // 2721/1001 = 2.71828172 + // 23225/8544 = 2.71828184 + // 25946/9545 = 2.71828182 + // 49171/18089 = 2.71828183 + // 517656/190435 = 2.71828183 +} diff --git a/src/cmd/compile/internal/big/floatmarsh.go b/src/cmd/compile/internal/big/floatmarsh.go new file mode 100644 index 0000000000..44987ee03a --- /dev/null +++ b/src/cmd/compile/internal/big/floatmarsh.go @@ -0,0 +1,33 @@ +// Copyright 2015 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. + +// This file implements encoding/decoding of Floats. + +package big + +import "fmt" + +// MarshalText implements the encoding.TextMarshaler interface. +// Only the Float value is marshaled (in full precision), other +// attributes such as precision or accuracy are ignored. +func (x *Float) MarshalText() (text []byte, err error) { + if x == nil { + return []byte(""), nil + } + var buf []byte + return x.Append(buf, 'g', -1), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// The result is rounded per the precision and rounding mode of z. +// If z's precision is 0, it is changed to 64 before rounding takes +// effect. +func (z *Float) UnmarshalText(text []byte) error { + // TODO(gri): get rid of the []byte/string conversion + _, _, err := z.Parse(string(text), 0) + if err != nil { + err = fmt.Errorf("math/big: cannot unmarshal %q into a *big.Float (%v)", text, err) + } + return err +} diff --git a/src/cmd/compile/internal/big/floatmarsh_test.go b/src/cmd/compile/internal/big/floatmarsh_test.go new file mode 100644 index 0000000000..d7ef2fca68 --- /dev/null +++ b/src/cmd/compile/internal/big/floatmarsh_test.go @@ -0,0 +1,54 @@ +// Copyright 2015 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 big + +import ( + "encoding/json" + "testing" +) + +var floatVals = []string{ + "0", + "1", + "0.1", + "2.71828", + "1234567890", + "3.14e1234", + "3.14e-1234", + "0.738957395793475734757349579759957975985497e100", + "0.73895739579347546656564656573475734957975995797598589749859834759476745986795497e100", + "inf", + "Inf", +} + +func TestFloatJSONEncoding(t *testing.T) { + for _, test := range floatVals { + for _, sign := range []string{"", "+", "-"} { + for _, prec := range []uint{0, 1, 2, 10, 53, 64, 100, 1000} { + x := sign + test + var tx Float + _, _, err := tx.SetPrec(prec).Parse(x, 0) + if err != nil { + t.Errorf("parsing of %s (prec = %d) failed (invalid test case): %v", x, prec, err) + continue + } + b, err := json.Marshal(&tx) + if err != nil { + t.Errorf("marshaling of %v (prec = %d) failed: %v", &tx, prec, err) + continue + } + var rx Float + rx.SetPrec(prec) + if err := json.Unmarshal(b, &rx); err != nil { + t.Errorf("unmarshaling of %v (prec = %d) failed: %v", &tx, prec, err) + continue + } + if rx.Cmp(&tx) != 0 { + t.Errorf("JSON encoding of %v (prec = %d) failed: got %v want %v", &tx, prec, &rx, &tx) + } + } + } + } +} diff --git a/src/cmd/compile/internal/big/intmarsh.go b/src/cmd/compile/internal/big/intmarsh.go new file mode 100644 index 0000000000..4ff57b6464 --- /dev/null +++ b/src/cmd/compile/internal/big/intmarsh.go @@ -0,0 +1,74 @@ +// Copyright 2015 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. + +// This file implements encoding/decoding of Ints. + +package big + +import "fmt" + +// Gob codec version. Permits backward-compatible changes to the encoding. +const intGobVersion byte = 1 + +// GobEncode implements the gob.GobEncoder interface. +func (x *Int) GobEncode() ([]byte, error) { + if x == nil { + return nil, nil + } + buf := make([]byte, 1+len(x.abs)*_S) // extra byte for version and sign bit + i := x.abs.bytes(buf) - 1 // i >= 0 + b := intGobVersion << 1 // make space for sign bit + if x.neg { + b |= 1 + } + buf[i] = b + return buf[i:], nil +} + +// GobDecode implements the gob.GobDecoder interface. +func (z *Int) GobDecode(buf []byte) error { + if len(buf) == 0 { + // Other side sent a nil or default value. + *z = Int{} + return nil + } + b := buf[0] + if b>>1 != intGobVersion { + return fmt.Errorf("Int.GobDecode: encoding version %d not supported", b>>1) + } + z.neg = b&1 != 0 + z.abs = z.abs.setBytes(buf[1:]) + return nil +} + +// MarshalText implements the encoding.TextMarshaler interface. +func (x *Int) MarshalText() (text []byte, err error) { + if x == nil { + return []byte(""), nil + } + return x.abs.itoa(x.neg, 10), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (z *Int) UnmarshalText(text []byte) error { + // TODO(gri): get rid of the []byte/string conversion + if _, ok := z.SetString(string(text), 0); !ok { + return fmt.Errorf("math/big: cannot unmarshal %q into a *big.Int", text) + } + return nil +} + +// The JSON marshallers are only here for API backward compatibility +// (programs that explicitly look for these two methods). JSON works +// fine with the TextMarshaler only. + +// MarshalJSON implements the json.Marshaler interface. +func (x *Int) MarshalJSON() ([]byte, error) { + return x.MarshalText() +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (z *Int) UnmarshalJSON(text []byte) error { + return z.UnmarshalText(text) +} diff --git a/src/cmd/compile/internal/big/intmarsh_test.go b/src/cmd/compile/internal/big/intmarsh_test.go new file mode 100644 index 0000000000..f82956ceaf --- /dev/null +++ b/src/cmd/compile/internal/big/intmarsh_test.go @@ -0,0 +1,121 @@ +// Copyright 2015 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 big + +import ( + "bytes" + "encoding/gob" + "encoding/json" + "encoding/xml" + "testing" +) + +var encodingTests = []string{ + "0", + "1", + "2", + "10", + "1000", + "1234567890", + "298472983472983471903246121093472394872319615612417471234712061", +} + +func TestIntGobEncoding(t *testing.T) { + var medium bytes.Buffer + enc := gob.NewEncoder(&medium) + dec := gob.NewDecoder(&medium) + for _, test := range encodingTests { + for _, sign := range []string{"", "+", "-"} { + x := sign + test + medium.Reset() // empty buffer for each test case (in case of failures) + var tx Int + tx.SetString(x, 10) + if err := enc.Encode(&tx); err != nil { + t.Errorf("encoding of %s failed: %s", &tx, err) + continue + } + var rx Int + if err := dec.Decode(&rx); err != nil { + t.Errorf("decoding of %s failed: %s", &tx, err) + continue + } + if rx.Cmp(&tx) != 0 { + t.Errorf("transmission of %s failed: got %s want %s", &tx, &rx, &tx) + } + } + } +} + +// Sending a nil Int pointer (inside a slice) on a round trip through gob should yield a zero. +// TODO: top-level nils. +func TestGobEncodingNilIntInSlice(t *testing.T) { + buf := new(bytes.Buffer) + enc := gob.NewEncoder(buf) + dec := gob.NewDecoder(buf) + + var in = make([]*Int, 1) + err := enc.Encode(&in) + if err != nil { + t.Errorf("gob encode failed: %q", err) + } + var out []*Int + err = dec.Decode(&out) + if err != nil { + t.Fatalf("gob decode failed: %q", err) + } + if len(out) != 1 { + t.Fatalf("wrong len; want 1 got %d", len(out)) + } + var zero Int + if out[0].Cmp(&zero) != 0 { + t.Fatalf("transmission of (*Int)(nil) failed: got %s want 0", out) + } +} + +func TestIntJSONEncoding(t *testing.T) { + for _, test := range encodingTests { + for _, sign := range []string{"", "+", "-"} { + x := sign + test + var tx Int + tx.SetString(x, 10) + b, err := json.Marshal(&tx) + if err != nil { + t.Errorf("marshaling of %s failed: %s", &tx, err) + continue + } + var rx Int + if err := json.Unmarshal(b, &rx); err != nil { + t.Errorf("unmarshaling of %s failed: %s", &tx, err) + continue + } + if rx.Cmp(&tx) != 0 { + t.Errorf("JSON encoding of %s failed: got %s want %s", &tx, &rx, &tx) + } + } + } +} + +func TestIntXMLEncoding(t *testing.T) { + for _, test := range encodingTests { + for _, sign := range []string{"", "+", "-"} { + x := sign + test + var tx Int + tx.SetString(x, 0) + b, err := xml.Marshal(&tx) + if err != nil { + t.Errorf("marshaling of %s failed: %s", &tx, err) + continue + } + var rx Int + if err := xml.Unmarshal(b, &rx); err != nil { + t.Errorf("unmarshaling of %s failed: %s", &tx, err) + continue + } + if rx.Cmp(&tx) != 0 { + t.Errorf("XML encoding of %s failed: got %s want %s", &tx, &rx, &tx) + } + } + } +} diff --git a/src/cmd/compile/internal/big/ratconv.go b/src/cmd/compile/internal/big/ratconv.go index 4566ff4e39..57df124e88 100644 --- a/src/cmd/compile/internal/big/ratconv.go +++ b/src/cmd/compile/internal/big/ratconv.go @@ -15,7 +15,7 @@ import ( ) func ratTok(ch rune) bool { - return strings.IndexRune("+-/0123456789.eE", ch) >= 0 + return strings.ContainsRune("+-/0123456789.eE", ch) } // Scan is a support routine for fmt.Scanner. It accepts the formats @@ -25,7 +25,7 @@ func (z *Rat) Scan(s fmt.ScanState, ch rune) error { if err != nil { return err } - if strings.IndexRune("efgEFGv", ch) < 0 { + if !strings.ContainsRune("efgEFGv", ch) { return errors.New("Rat.Scan: invalid verb") } if _, ok := z.SetString(string(tok)); !ok { diff --git a/src/cmd/compile/internal/big/ratmarsh.go b/src/cmd/compile/internal/big/ratmarsh.go new file mode 100644 index 0000000000..b82e8d4ae8 --- /dev/null +++ b/src/cmd/compile/internal/big/ratmarsh.go @@ -0,0 +1,73 @@ +// Copyright 2015 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. + +// This file implements encoding/decoding of Rats. + +package big + +import ( + "encoding/binary" + "errors" + "fmt" +) + +// Gob codec version. Permits backward-compatible changes to the encoding. +const ratGobVersion byte = 1 + +// GobEncode implements the gob.GobEncoder interface. +func (x *Rat) GobEncode() ([]byte, error) { + if x == nil { + return nil, nil + } + buf := make([]byte, 1+4+(len(x.a.abs)+len(x.b.abs))*_S) // extra bytes for version and sign bit (1), and numerator length (4) + i := x.b.abs.bytes(buf) + j := x.a.abs.bytes(buf[:i]) + n := i - j + if int(uint32(n)) != n { + // this should never happen + return nil, errors.New("Rat.GobEncode: numerator too large") + } + binary.BigEndian.PutUint32(buf[j-4:j], uint32(n)) + j -= 1 + 4 + b := ratGobVersion << 1 // make space for sign bit + if x.a.neg { + b |= 1 + } + buf[j] = b + return buf[j:], nil +} + +// GobDecode implements the gob.GobDecoder interface. +func (z *Rat) GobDecode(buf []byte) error { + if len(buf) == 0 { + // Other side sent a nil or default value. + *z = Rat{} + return nil + } + b := buf[0] + if b>>1 != ratGobVersion { + return fmt.Errorf("Rat.GobDecode: encoding version %d not supported", b>>1) + } + const j = 1 + 4 + i := j + binary.BigEndian.Uint32(buf[j-4:j]) + z.a.neg = b&1 != 0 + z.a.abs = z.a.abs.setBytes(buf[j:i]) + z.b.abs = z.b.abs.setBytes(buf[i:]) + return nil +} + +// MarshalText implements the encoding.TextMarshaler interface. +func (x *Rat) MarshalText() (text []byte, err error) { + // TODO(gri): get rid of the []byte/string conversion + return []byte(x.RatString()), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (z *Rat) UnmarshalText(text []byte) error { + // TODO(gri): get rid of the []byte/string conversion + if _, ok := z.SetString(string(text)); !ok { + return fmt.Errorf("math/big: cannot unmarshal %q into a *big.Rat", text) + } + return nil +} diff --git a/src/cmd/compile/internal/big/ratmarsh_test.go b/src/cmd/compile/internal/big/ratmarsh_test.go new file mode 100644 index 0000000000..351d109f8d --- /dev/null +++ b/src/cmd/compile/internal/big/ratmarsh_test.go @@ -0,0 +1,125 @@ +// Copyright 2015 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 big + +import ( + "bytes" + "encoding/gob" + "encoding/json" + "encoding/xml" + "testing" +) + +func TestRatGobEncoding(t *testing.T) { + var medium bytes.Buffer + enc := gob.NewEncoder(&medium) + dec := gob.NewDecoder(&medium) + for _, test := range encodingTests { + medium.Reset() // empty buffer for each test case (in case of failures) + var tx Rat + tx.SetString(test + ".14159265") + if err := enc.Encode(&tx); err != nil { + t.Errorf("encoding of %s failed: %s", &tx, err) + continue + } + var rx Rat + if err := dec.Decode(&rx); err != nil { + t.Errorf("decoding of %s failed: %s", &tx, err) + continue + } + if rx.Cmp(&tx) != 0 { + t.Errorf("transmission of %s failed: got %s want %s", &tx, &rx, &tx) + } + } +} + +// Sending a nil Rat pointer (inside a slice) on a round trip through gob should yield a zero. +// TODO: top-level nils. +func TestGobEncodingNilRatInSlice(t *testing.T) { + buf := new(bytes.Buffer) + enc := gob.NewEncoder(buf) + dec := gob.NewDecoder(buf) + + var in = make([]*Rat, 1) + err := enc.Encode(&in) + if err != nil { + t.Errorf("gob encode failed: %q", err) + } + var out []*Rat + err = dec.Decode(&out) + if err != nil { + t.Fatalf("gob decode failed: %q", err) + } + if len(out) != 1 { + t.Fatalf("wrong len; want 1 got %d", len(out)) + } + var zero Rat + if out[0].Cmp(&zero) != 0 { + t.Fatalf("transmission of (*Int)(nil) failed: got %s want 0", out) + } +} + +var ratNums = []string{ + "-141592653589793238462643383279502884197169399375105820974944592307816406286", + "-1415926535897932384626433832795028841971", + "-141592653589793", + "-1", + "0", + "1", + "141592653589793", + "1415926535897932384626433832795028841971", + "141592653589793238462643383279502884197169399375105820974944592307816406286", +} + +var ratDenoms = []string{ + "1", + "718281828459045", + "7182818284590452353602874713526624977572", + "718281828459045235360287471352662497757247093699959574966967627724076630353", +} + +func TestRatJSONEncoding(t *testing.T) { + for _, num := range ratNums { + for _, denom := range ratDenoms { + var tx Rat + tx.SetString(num + "/" + denom) + b, err := json.Marshal(&tx) + if err != nil { + t.Errorf("marshaling of %s failed: %s", &tx, err) + continue + } + var rx Rat + if err := json.Unmarshal(b, &rx); err != nil { + t.Errorf("unmarshaling of %s failed: %s", &tx, err) + continue + } + if rx.Cmp(&tx) != 0 { + t.Errorf("JSON encoding of %s failed: got %s want %s", &tx, &rx, &tx) + } + } + } +} + +func TestRatXMLEncoding(t *testing.T) { + for _, num := range ratNums { + for _, denom := range ratDenoms { + var tx Rat + tx.SetString(num + "/" + denom) + b, err := xml.Marshal(&tx) + if err != nil { + t.Errorf("marshaling of %s failed: %s", &tx, err) + continue + } + var rx Rat + if err := xml.Unmarshal(b, &rx); err != nil { + t.Errorf("unmarshaling of %s failed: %s", &tx, err) + continue + } + if rx.Cmp(&tx) != 0 { + t.Errorf("XML encoding of %s failed: got %s want %s", &tx, &rx, &tx) + } + } + } +} diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index b49f0fb552..ff0465f64c 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -877,7 +877,7 @@ func (p *exporter) byte(b byte) { // tracef is like fmt.Printf but it rewrites the format string // to take care of indentation. func (p *exporter) tracef(format string, args ...interface{}) { - if strings.IndexAny(format, "<>\n") >= 0 { + if strings.ContainsAny(format, "<>\n") { var buf bytes.Buffer for i := 0; i < len(format); i++ { // no need to deal with runes @@ -1035,6 +1035,9 @@ func predeclared() []*Type { // package unsafe Types[TUNSAFEPTR], + + // any type, for builtin export data + Types[TANY], } } return predecl diff --git a/src/cmd/compile/internal/gc/builtin.go b/src/cmd/compile/internal/gc/builtin.go index 7f2e80b52f..4a6e56fe47 100644 --- a/src/cmd/compile/internal/gc/builtin.go +++ b/src/cmd/compile/internal/gc/builtin.go @@ -3,7 +3,7 @@ package gc const runtimeimport = "" + - "package runtime\n" + + "package runtime safe\n" + "func @\"\".newobject (@\"\".typ·2 *byte) (? *any)\n" + "func @\"\".panicindex ()\n" + "func @\"\".panicslice ()\n" + @@ -44,7 +44,7 @@ const runtimeimport = "" + "func @\"\".stringtoslicerune (? *[32]rune, ? string) (? []rune)\n" + "func @\"\".stringiter (? string, ? int) (? int)\n" + "func @\"\".stringiter2 (? string, ? int) (@\"\".retk·1 int, @\"\".retv·2 rune)\n" + - "func @\"\".slicecopy (@\"\".to·2 any, @\"\".fr·3 any, @\"\".wid·4 uintptr) (? int)\n" + + "func @\"\".slicecopy (@\"\".to·2 any, @\"\".fr·3 any, @\"\".wid·4 uintptr \"unsafe-uintptr\") (? int)\n" + "func @\"\".slicestringcopy (@\"\".to·2 any, @\"\".fr·3 any) (? int)\n" + "func @\"\".typ2Itab (@\"\".typ·2 *byte, @\"\".typ2·3 *byte, @\"\".cache·4 **byte) (@\"\".ret·1 *byte)\n" + "func @\"\".convI2E (@\"\".elem·2 any) (@\"\".ret·1 any)\n" + @@ -66,8 +66,6 @@ const runtimeimport = "" + "func @\"\".panicdottype (@\"\".have·1 *byte, @\"\".want·2 *byte, @\"\".iface·3 *byte)\n" + "func @\"\".ifaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n" + "func @\"\".efaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n" + - "func @\"\".ifacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n" + - "func @\"\".efacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n" + "func @\"\".makemap (@\"\".mapType·2 *byte, @\"\".hint·3 int64, @\"\".mapbuf·4 *any, @\"\".bucketbuf·5 *any) (@\"\".hmap·1 map[any]any)\n" + "func @\"\".mapaccess1 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 *any) (@\"\".val·1 *any)\n" + "func @\"\".mapaccess1_fast32 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n" + @@ -91,31 +89,31 @@ const runtimeimport = "" + "func @\"\".writebarrierstring (@\"\".dst·1 *any, @\"\".src·2 any)\n" + "func @\"\".writebarrierslice (@\"\".dst·1 *any, @\"\".src·2 any)\n" + "func @\"\".writebarrieriface (@\"\".dst·1 *any, @\"\".src·2 any)\n" + - "func @\"\".writebarrierfat01 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat10 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat11 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat001 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat010 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat011 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat100 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat101 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat110 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat111 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat0001 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat0010 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat0011 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat0100 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat0101 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat0110 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat0111 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat1000 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat1001 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat1010 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat1011 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat1100 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat1101 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat1110 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat1111 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat01 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat10 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat11 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat001 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat010 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat011 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat100 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat101 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat110 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat111 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat0001 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat0010 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat0011 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat0100 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat0101 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat0110 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat0111 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat1000 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat1001 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat1010 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat1011 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat1100 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat1101 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat1110 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat1111 (@\"\".dst·1 *any, _ uintptr \"unsafe-uintptr\", @\"\".src·3 any)\n" + "func @\"\".typedmemmove (@\"\".typ·1 *byte, @\"\".dst·2 *any, @\"\".src·3 *any)\n" + "func @\"\".typedslicecopy (@\"\".typ·2 *byte, @\"\".dst·3 any, @\"\".src·4 any) (? int)\n" + "func @\"\".selectnbsend (@\"\".chanType·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 *any) (? bool)\n" + @@ -131,9 +129,9 @@ const runtimeimport = "" + "func @\"\".makeslice (@\"\".typ·2 *byte, @\"\".nel·3 int64, @\"\".cap·4 int64) (@\"\".ary·1 []any)\n" + "func @\"\".growslice (@\"\".typ·2 *byte, @\"\".old·3 []any, @\"\".cap·4 int) (@\"\".ary·1 []any)\n" + "func @\"\".growslice_n (@\"\".typ·2 *byte, @\"\".old·3 []any, @\"\".n·4 int) (@\"\".ary·1 []any)\n" + - "func @\"\".memmove (@\"\".to·1 *any, @\"\".frm·2 *any, @\"\".length·3 uintptr)\n" + - "func @\"\".memclr (@\"\".ptr·1 *byte, @\"\".length·2 uintptr)\n" + - "func @\"\".memequal (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n" + + "func @\"\".memmove (@\"\".to·1 *any, @\"\".frm·2 *any, @\"\".length·3 uintptr \"unsafe-uintptr\")\n" + + "func @\"\".memclr (@\"\".ptr·1 *byte, @\"\".length·2 uintptr \"unsafe-uintptr\")\n" + + "func @\"\".memequal (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr \"unsafe-uintptr\") (? bool)\n" + "func @\"\".memequal8 (@\"\".x·2 *any, @\"\".y·3 *any) (? bool)\n" + "func @\"\".memequal16 (@\"\".x·2 *any, @\"\".y·3 *any) (? bool)\n" + "func @\"\".memequal32 (@\"\".x·2 *any, @\"\".y·3 *any) (? bool)\n" + @@ -148,15 +146,14 @@ const runtimeimport = "" + "func @\"\".int64tofloat64 (? int64) (? float64)\n" + "func @\"\".uint64tofloat64 (? uint64) (? float64)\n" + "func @\"\".complex128div (@\"\".num·2 complex128, @\"\".den·3 complex128) (@\"\".quo·1 complex128)\n" + - "func @\"\".racefuncenter (? uintptr)\n" + - "func @\"\".racefuncenterfp (? *int32)\n" + + "func @\"\".racefuncenter (? uintptr \"unsafe-uintptr\")\n" + "func @\"\".racefuncexit ()\n" + - "func @\"\".raceread (? uintptr)\n" + - "func @\"\".racewrite (? uintptr)\n" + - "func @\"\".racereadrange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n" + - "func @\"\".racewriterange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n" + - "func @\"\".msanread (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n" + - "func @\"\".msanwrite (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n" + + "func @\"\".raceread (? uintptr \"unsafe-uintptr\")\n" + + "func @\"\".racewrite (? uintptr \"unsafe-uintptr\")\n" + + "func @\"\".racereadrange (@\"\".addr·1 uintptr \"unsafe-uintptr\", @\"\".size·2 uintptr \"unsafe-uintptr\")\n" + + "func @\"\".racewriterange (@\"\".addr·1 uintptr \"unsafe-uintptr\", @\"\".size·2 uintptr \"unsafe-uintptr\")\n" + + "func @\"\".msanread (@\"\".addr·1 uintptr \"unsafe-uintptr\", @\"\".size·2 uintptr \"unsafe-uintptr\")\n" + + "func @\"\".msanwrite (@\"\".addr·1 uintptr \"unsafe-uintptr\", @\"\".size·2 uintptr \"unsafe-uintptr\")\n" + "\n" + "$$\n" diff --git a/src/cmd/compile/internal/gc/builtin/runtime.go b/src/cmd/compile/internal/gc/builtin/runtime.go index 70663eeee4..0fe6242e74 100644 --- a/src/cmd/compile/internal/gc/builtin/runtime.go +++ b/src/cmd/compile/internal/gc/builtin/runtime.go @@ -8,7 +8,7 @@ // +build ignore -package PACKAGE +package runtime // emitted by compiler, not referred to by go programs @@ -83,8 +83,6 @@ func panicdottype(have, want, iface *byte) func ifaceeq(i1 any, i2 any) (ret bool) func efaceeq(i1 any, i2 any) (ret bool) -func ifacethash(i1 any) (ret uint32) -func efacethash(i1 any) (ret uint32) // *byte is really *runtime.Type func makemap(mapType *byte, hint int64, mapbuf *any, bucketbuf *any) (hmap map[any]any) @@ -192,7 +190,6 @@ func complex128div(num complex128, den complex128) (quo complex128) // race detection func racefuncenter(uintptr) -func racefuncenterfp(*int32) func racefuncexit() func raceread(uintptr) func racewrite(uintptr) diff --git a/src/cmd/compile/internal/gc/builtin/unsafe.go b/src/cmd/compile/internal/gc/builtin/unsafe.go index ce508692eb..a7fc8aa53e 100644 --- a/src/cmd/compile/internal/gc/builtin/unsafe.go +++ b/src/cmd/compile/internal/gc/builtin/unsafe.go @@ -8,7 +8,7 @@ // +build ignore -package PACKAGE +package unsafe type Pointer uintptr // not really; filled in by compiler diff --git a/src/cmd/compile/internal/gc/builtin_test.go b/src/cmd/compile/internal/gc/builtin_test.go new file mode 100644 index 0000000000..94111e640d --- /dev/null +++ b/src/cmd/compile/internal/gc/builtin_test.go @@ -0,0 +1,31 @@ +// Copyright 2016 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 gc_test + +import ( + "bytes" + "internal/testenv" + "io/ioutil" + "os/exec" + "testing" +) + +func TestBuiltin(t *testing.T) { + testenv.MustHaveGoRun(t) + + old, err := ioutil.ReadFile("builtin.go") + if err != nil { + t.Fatal(err) + } + + new, err := exec.Command("go", "run", "mkbuiltin.go", "-stdout").Output() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(old, new) { + t.Fatal("builtin.go out of date; run mkbuiltin.go") + } +} diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go index ff983e717e..1a5a433eeb 100644 --- a/src/cmd/compile/internal/gc/esc.go +++ b/src/cmd/compile/internal/gc/esc.go @@ -576,6 +576,12 @@ func esc(e *EscState, n *Node, up *Node) { if n == nil { return } + if n.Type != nil && n.Type.Etype == TFIELD { + // This is the left side of x:y in a struct literal. + // x is syntax, not an expression. + // See #14405. + return + } lno := int(setlineno(n)) @@ -602,9 +608,10 @@ func esc(e *EscState, n *Node, up *Node) { // Big stuff escapes unconditionally // "Big" conditions that were scattered around in walk have been gathered here - if n.Esc != EscHeap && n.Type != nil && (n.Type.Width > MaxStackVarSize || - n.Op == ONEW && n.Type.Type.Width >= 1<<16 || - n.Op == OMAKESLICE && !isSmallMakeSlice(n)) { + if n.Esc != EscHeap && n.Type != nil && + (n.Type.Width > MaxStackVarSize || + n.Op == ONEW && n.Type.Type.Width >= 1<<16 || + n.Op == OMAKESLICE && !isSmallMakeSlice(n)) { if Debug['m'] > 1 { Warnl(int(n.Lineno), "%v is too large for stack", n) } @@ -962,7 +969,7 @@ func escassign(e *EscState, dst *Node, src *Node) { dst = &e.theSink } - case ODOT: // treat "dst.x = src" as "dst = src" + case ODOT: // treat "dst.x = src" as "dst = src" escassign(e, dst.Left, src) return @@ -1042,7 +1049,6 @@ func escassign(e *EscState, dst *Node, src *Node) { ODOTMETH, // treat recv.meth as a value with recv in it, only happens in ODEFER and OPROC // iface.method already leaks iface in esccall, no need to put in extra ODOTINTER edge here - ODOTTYPE, ODOTTYPE2, OSLICE, OSLICE3, @@ -1052,6 +1058,12 @@ func escassign(e *EscState, dst *Node, src *Node) { // Conversions, field access, slice all preserve the input value. escassign(e, dst, src.Left) + case ODOTTYPE: + if src.Type != nil && !haspointers(src.Type) { + break + } + escassign(e, dst, src.Left) + case OAPPEND: // Append returns first argument. // Subsequent arguments are already leaked because they are operands to append. @@ -1549,9 +1561,9 @@ func escflows(e *EscState, dst *Node, src *Node) { // finding an OADDR just means we're following the upstream of a dereference, // so this address doesn't leak (yet). // If level == 0, it means the /value/ of this node can reach the root of this flood. -// so if this node is an OADDR, it's argument should be marked as escaping iff -// it's currfn/e->loopdepth are different from the flood's root. -// Once an object has been moved to the heap, all of it's upstream should be considered +// so if this node is an OADDR, its argument should be marked as escaping iff +// its currfn/e->loopdepth are different from the flood's root. +// Once an object has been moved to the heap, all of its upstream should be considered // escaping to the global scope. func escflood(e *EscState, dst *Node) { switch dst.Op { diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go index e50cf383d7..1b61d7f228 100644 --- a/src/cmd/compile/internal/gc/export.go +++ b/src/cmd/compile/internal/gc/export.go @@ -442,7 +442,7 @@ func importsym(s *Sym, op Op) *Sym { // mark the symbol so it is not reexported if s.Def == nil { - if exportname(s.Name) || initname(s.Name) { + if Debug['A'] != 0 || exportname(s.Name) || initname(s.Name) { s.Flags |= SymExport } else { s.Flags |= SymPackage // package scope diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index dbd8db16e5..69022814ef 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -749,7 +749,13 @@ func typefmt(t *Type, flag int) string { if name != "" { str = name + " " + typ } - if flag&obj.FmtShort == 0 && !fmtbody && t.Note != nil { + + // The fmtbody flag is intended to suppress escape analysis annotations + // when printing a function type used in a function body. + // (The escape analysis tags do not apply to func vars.) + // But it must not suppress struct field tags. + // See golang.org/issue/13777 and golang.org/issue/14331. + if flag&obj.FmtShort == 0 && (!fmtbody || !t.Funarg) && t.Note != nil { str += " " + strconv.Quote(*t.Note) } return str @@ -1537,7 +1543,7 @@ func nodedump(n *Node, flag int) string { } else { fmt.Fprintf(&buf, "%v%v", Oconv(int(n.Op), 0), Jconv(n, 0)) } - if recur && n.Type == nil && n.Name.Param.Ntype != nil { + if recur && n.Type == nil && n.Name != nil && n.Name.Param != nil && n.Name.Param.Ntype != nil { indent(&buf) fmt.Fprintf(&buf, "%v-ntype%v", Oconv(int(n.Op), 0), n.Name.Param.Ntype) } diff --git a/src/cmd/compile/internal/gc/gen.go b/src/cmd/compile/internal/gc/gen.go index 60b93ef805..721ef31247 100644 --- a/src/cmd/compile/internal/gc/gen.go +++ b/src/cmd/compile/internal/gc/gen.go @@ -838,7 +838,7 @@ func gen(n *Node) { Cgen_as_wb(n.Left, n.Right, true) case OAS2DOTTYPE: - cgen_dottype(n.Rlist.N, n.List.N, n.List.Next.N, false) + cgen_dottype(n.Rlist.N, n.List.N, n.List.Next.N, needwritebarrier(n.List.N, n.Rlist.N)) case OCALLMETH: cgen_callmeth(n, 0) diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index 08442a415b..f721fabfd7 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -28,30 +28,21 @@ const ( const ( // These values are known by runtime. - // The MEMx and NOEQx values must run in parallel. See algtype. - AMEM = iota + ANOEQ = iota AMEM0 AMEM8 AMEM16 AMEM32 AMEM64 AMEM128 - ANOEQ - ANOEQ0 - ANOEQ8 - ANOEQ16 - ANOEQ32 - ANOEQ64 - ANOEQ128 ASTRING AINTER ANILINTER - ASLICE AFLOAT32 AFLOAT64 ACPLX64 ACPLX128 - AUNK = 100 + AMEM = 100 ) const ( @@ -329,8 +320,7 @@ const ( const ( // types of channel - // must match ../../pkg/nreflect/type.go:/Chandir - Cxxx = 0 + // must match ../../../../reflect/type.go:/ChanDir Crecv = 1 << 0 Csend = 1 << 1 Cboth = Crecv | Csend @@ -385,27 +375,10 @@ type Sig struct { offset int32 } -type Io struct { - infile string - bin *obj.Biobuf - cp string // used for content when bin==nil - last int - peekc int - peekc1 int // second peekc for ... - nlsemi bool - eofnl bool - importsafe bool -} - type Dlist struct { field *Type } -type Idir struct { - link *Idir - dir string -} - // argument passing to/from // smagic and umagic type Magic struct { @@ -452,10 +425,6 @@ var sizeof_String int // runtime sizeof(String) var dotlist [10]Dlist // size is max depth of embeddeds -var curio Io - -var pushedio Io - var lexlineno int32 var lineno int32 @@ -493,8 +462,6 @@ var debugstr string var Debug_checknil int var Debug_typeassert int -var importmyname *Sym // my name for package - var localpkg *Pkg // package being compiled var importpkg *Pkg // package being imported @@ -527,8 +494,6 @@ var Tptr EType // either TPTR32 or TPTR64 var myimportpath string -var idirs *Idir - var localimport string var asmhdr string diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 64afd67438..a445f712e2 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -7,7 +7,7 @@ // saves a copy of the body. Then inlcalls walks each function body to // expand calls to inlinable functions. // -// The debug['l'] flag controls the agressiveness. Note that main() swaps level 0 and 1, +// The debug['l'] flag controls the aggressiveness. Note that main() swaps level 0 and 1, // making 1 the default and -l disable. -ll and more is useful to flush out bugs. // These additional levels (beyond -l) may be buggy and are not supported. // 0: disabled diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go index e4ce9c796b..51ad6162bf 100644 --- a/src/cmd/compile/internal/gc/lex.go +++ b/src/cmd/compile/internal/gc/lex.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. -//go:generate go run mkbuiltin.go runtime unsafe +//go:generate go run mkbuiltin.go package gc @@ -257,7 +257,7 @@ func Main() { msanpkg.Name = "msan" } if flag_race != 0 && flag_msan != 0 { - log.Fatal("can not use both -race and -msan") + log.Fatal("cannot use both -race and -msan") } else if flag_race != 0 || flag_msan != 0 { instrumenting = true } @@ -313,6 +313,8 @@ func Main() { lexlineno = 1 const BOM = 0xFEFF + loadsys() + for _, infile = range flag.Args() { if trace && Debug['x'] != 0 { fmt.Printf("--- %s ---\n", infile) @@ -320,23 +322,15 @@ func Main() { linehistpush(infile) - curio.infile = infile - var err error - curio.bin, err = obj.Bopenr(infile) + bin, err := obj.Bopenr(infile) if err != nil { fmt.Printf("open %s: %v\n", infile, err) errorexit() } - curio.peekc = 0 - curio.peekc1 = 0 - curio.nlsemi = false - curio.eofnl = false - curio.last = 0 - // Skip initial BOM if present. - if obj.Bgetrune(curio.bin) != BOM { - obj.Bungetrune(curio.bin) + if obj.Bgetrune(bin) != BOM { + obj.Bungetrune(bin) } block = 1 @@ -344,15 +338,18 @@ func Main() { imported_unsafe = false - parse_file() + parse_file(bin) if nsyntaxerrors != 0 { errorexit() } + // Instead of converting EOF into '\n' in getc and count it as an extra line + // for the line history to work, and which then has to be corrected elsewhere, + // just add a line here. + lexlineno++ + linehistpop() - if curio.bin != nil { - obj.Bterm(curio.bin) - } + obj.Bterm(bin) } testdclstack() @@ -571,17 +568,12 @@ func skiptopkgdef(b *obj.Biobuf) bool { return true } -func addidir(dir string) { - if dir == "" { - return - } +var idirs []string - var pp **Idir - for pp = &idirs; *pp != nil; pp = &(*pp).link { +func addidir(dir string) { + if dir != "" { + idirs = append(idirs, dir) } - *pp = new(Idir) - (*pp).link = nil - (*pp).dir = dir } // is this path a local name? begins with ./ or ../ or / @@ -620,12 +612,12 @@ func findpkg(name string) (file string, ok bool) { return "", false } - for p := idirs; p != nil; p = p.link { - file = fmt.Sprintf("%s/%s.a", p.dir, name) + for _, dir := range idirs { + file = fmt.Sprintf("%s/%s.a", dir, name) if _, err := os.Stat(file); err == nil { return file, true } - file = fmt.Sprintf("%s/%s.o", p.dir, name) + file = fmt.Sprintf("%s/%s.o", dir, name) if _, err := os.Stat(file); err == nil { return file, true } @@ -658,27 +650,45 @@ func findpkg(name string) (file string, ok bool) { return "", false } -func fakeimport() { - importpkg = mkpkg("fake") - cannedimports("fake.o", "$$\n") +// loadsys loads the definitions for the low-level runtime and unsafe functions, +// so that the compiler can generate calls to them, +// but does not make the names "runtime" or "unsafe" visible as packages. +func loadsys() { + if Debug['A'] != 0 { + return + } + + block = 1 + iota_ = -1000000 + incannedimport = 1 + + importpkg = Runtimepkg + parse_import(obj.Binitr(strings.NewReader(runtimeimport)), nil) + + importpkg = unsafepkg + parse_import(obj.Binitr(strings.NewReader(unsafeimport)), nil) + + importpkg = nil + incannedimport = 0 } -// TODO(gri) line argument doesn't appear to be used -func importfile(f *Val, line int) { - if _, ok := f.U.(string); !ok { +func importfile(f *Val, indent []byte) { + if importpkg != nil { + Fatalf("importpkg not nil") + } + + path_, ok := f.U.(string) + if !ok { Yyerror("import statement not a string") - fakeimport() return } - if len(f.U.(string)) == 0 { + if len(path_) == 0 { Yyerror("import path is empty") - fakeimport() return } - if isbadimport(f.U.(string)) { - fakeimport() + if isbadimport(path_) { return } @@ -686,18 +696,16 @@ func importfile(f *Val, line int) { // but we reserve the import path "main" to identify // the main package, just as we reserve the import // path "math" to identify the standard math package. - if f.U.(string) == "main" { + if path_ == "main" { Yyerror("cannot import \"main\"") errorexit() } - if myimportpath != "" && f.U.(string) == myimportpath { - Yyerror("import %q while compiling that package (import cycle)", f.U.(string)) + if myimportpath != "" && path_ == myimportpath { + Yyerror("import %q while compiling that package (import cycle)", path_) errorexit() } - path_ := f.U.(string) - if mapped, ok := importMap[path_]; ok { path_ = mapped } @@ -708,8 +716,7 @@ func importfile(f *Val, line int) { errorexit() } - importpkg = mkpkg(f.U.(string)) - cannedimports("unsafe.o", unsafeimport) + importpkg = unsafepkg imported_unsafe = true return } @@ -717,7 +724,6 @@ func importfile(f *Val, line int) { if islocalname(path_) { if path_[0] == '/' { Yyerror("import path cannot be absolute path") - fakeimport() return } @@ -725,48 +731,33 @@ func importfile(f *Val, line int) { if localimport != "" { prefix = localimport } - cleanbuf := prefix - cleanbuf += "/" - cleanbuf += path_ - cleanbuf = path.Clean(cleanbuf) - path_ = cleanbuf + path_ = path.Join(prefix, path_) if isbadimport(path_) { - fakeimport() return } } file, found := findpkg(path_) if !found { - Yyerror("can't find import: %q", f.U.(string)) + Yyerror("can't find import: %q", path_) errorexit() } importpkg = mkpkg(path_) - // If we already saw that package, feed a dummy statement - // to the lexer to avoid parsing export data twice. if importpkg.Imported { - tag := "" - if importpkg.Safe { - tag = "safe" - } - - p := fmt.Sprintf("package %s %s\n$$\n", importpkg.Name, tag) - cannedimports(file, p) return } importpkg.Imported = true - var err error - var imp *obj.Biobuf - imp, err = obj.Bopenr(file) + imp, err := obj.Bopenr(file) if err != nil { - Yyerror("can't open import: %q: %v", f.U.(string), err) + Yyerror("can't open import: %q: %v", path_, err) errorexit() } + defer obj.Bterm(imp) if strings.HasSuffix(file, ".a") { if !skiptopkgdef(imp) { @@ -822,76 +813,21 @@ func importfile(f *Val, line int) { switch c { case '\n': // old export format - pushedio = curio - - curio.bin = imp - curio.peekc = 0 - curio.peekc1 = 0 - curio.infile = file - curio.nlsemi = false - typecheckok = true - - push_parser() + parse_import(imp, indent) case 'B': // new export format obj.Bgetc(imp) // skip \n after $$B Import(imp) - // continue as if the package was imported before (see above) - tag := "" - if importpkg.Safe { - tag = "safe" - } - p := fmt.Sprintf("package %s %s\n$$\n", importpkg.Name, tag) - cannedimports(file, p) - // Reset incannedimport flag (we are not truly in a - // canned import) - this will cause importpkg.Direct to - // be set via parser.import_package (was issue #13977). - // - // TODO(gri) Remove this global variable and convoluted - // code in the process of streamlining the import code. - incannedimport = 0 - default: - Yyerror("no import in %q", f.U.(string)) - } -} - -func unimportfile() { - pop_parser() - - if curio.bin != nil { - obj.Bterm(curio.bin) - curio.bin = nil - } else { - lexlineno-- // re correct sys.6 line number + Yyerror("no import in %q", path_) + errorexit() } - curio = pushedio - - pushedio.bin = nil - incannedimport = 0 - typecheckok = false -} - -func cannedimports(file string, cp string) { - lexlineno++ // if sys.6 is included on line 1, - - pushedio = curio - - curio.bin = nil - curio.peekc = 0 - curio.peekc1 = 0 - curio.infile = file - curio.cp = cp - curio.nlsemi = false - curio.importsafe = false - - typecheckok = true - incannedimport = 1 - - push_parser() + if safemode != 0 && !importpkg.Safe { + Yyerror("cannot import unsafe package %q", importpkg.Path) + } } func isSpace(c int) bool { @@ -899,7 +835,7 @@ func isSpace(c int) bool { } func isAlpha(c int) bool { - return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' + return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' } func isDigit(c int) bool { @@ -933,10 +869,19 @@ func isfrog(c int) bool { return false } -type yySymType struct { - sym *Sym - val Val - op Op +type lexer struct { + // source + bin *obj.Biobuf + peekc int + peekc1 int // second peekc for ... + + nlsemi bool // if set, '\n' and EOF translate to ';' + + // current token + tok int32 + sym_ *Sym // valid if tok == LNAME + val Val // valid if tok == LLITERAL + op Op // valid if tok == LASOP } const ( @@ -987,7 +932,7 @@ const ( LRSH ) -func _yylex(yylval *yySymType) int32 { +func (l *lexer) next() { var c1 int var op Op var escflag int @@ -998,27 +943,31 @@ func _yylex(yylval *yySymType) int32 { prevlineno = lineno + nlsemi := l.nlsemi + l.nlsemi = false + l0: - c := getc() - if isSpace(c) { - if c == '\n' && curio.nlsemi { - ungetc(c) + // skip white space + c := l.getc() + for isSpace(c) { + if c == '\n' && nlsemi { + l.ungetc(c) if Debug['x'] != 0 { fmt.Printf("lex: implicit semi\n") } - return ';' + l.tok = ';' + return } - - goto l0 + c = l.getc() } - lineno = lexlineno // start of token + // start of token + lineno = lexlineno if c >= utf8.RuneSelf { // all multibyte runes are alpha cp = &lexbuf cp.Reset() - goto talph } @@ -1034,7 +983,7 @@ l0: if c != '0' { for { cp.WriteByte(byte(c)) - c = getc() + c = l.getc() if isDigit(c) { continue } @@ -1052,11 +1001,11 @@ l0: } cp.WriteByte(byte(c)) - c = getc() + c = l.getc() if c == 'x' || c == 'X' { for { cp.WriteByte(byte(c)) - c = getc() + c = l.getc() if isDigit(c) { continue } @@ -1089,7 +1038,7 @@ l0: c1 = 1 // not octal } cp.WriteByte(byte(c)) - c = getc() + c = l.getc() } if c == '.' { @@ -1110,8 +1059,18 @@ l0: switch c { case EOF: lineno = prevlineno - ungetc(EOF) - return -1 + l.ungetc(EOF) + // Treat EOF as "end of line" for the purposes + // of inserting a semicolon. + if nlsemi { + if Debug['x'] != 0 { + fmt.Printf("lex: implicit semi\n") + } + l.tok = ';' + return + } + l.tok = -1 + return case '_': cp = &lexbuf @@ -1119,7 +1078,7 @@ l0: goto talph case '.': - c1 = getc() + c1 = l.getc() if isDigit(c1) { cp = &lexbuf cp.Reset() @@ -1129,13 +1088,13 @@ l0: } if c1 == '.' { - c1 = getc() + c1 = l.getc() if c1 == '.' { c = LDDD goto lx } - ungetc(c1) + l.ungetc(c1) c1 = '.' } @@ -1148,7 +1107,7 @@ l0: cp.Reset() for { - if escchar('"', &escflag, &v) { + if l.escchar('"', &escflag, &v) { break } if v < utf8.RuneSelf || escflag != 0 { @@ -1169,7 +1128,7 @@ l0: cp.Reset() for { - c = int(getr()) + c = int(l.getr()) if c == '\r' { continue } @@ -1188,40 +1147,42 @@ l0: // '.' case '\'': - if escchar('\'', &escflag, &v) { + if l.escchar('\'', &escflag, &v) { Yyerror("empty character literal or unescaped ' in character literal") v = '\'' } - if !escchar('\'', &escflag, &v) { + if !l.escchar('\'', &escflag, &v) { Yyerror("missing '") - ungetc(int(v)) + l.ungetc(int(v)) } x := new(Mpint) - yylval.val.U = x + l.val.U = x Mpmovecfix(x, v) x.Rune = true if Debug['x'] != 0 { fmt.Printf("lex: codepoint literal\n") } litbuf = "string literal" - return LLITERAL + l.nlsemi = true + l.tok = LLITERAL + return case '/': - c1 = getc() + c1 = l.getc() if c1 == '*' { nl := false for { - c = int(getr()) + c = int(l.getr()) if c == '\n' { nl = true } for c == '*' { - c = int(getr()) + c = int(l.getr()) if c == '/' { if nl { - ungetc('\n') + l.ungetc('\n') } goto l0 } @@ -1239,14 +1200,14 @@ l0: } if c1 == '/' { - c = getlinepragma() + c = l.getlinepragma() for { if c == '\n' || c == EOF { - ungetc(c) + l.ungetc(c) goto l0 } - c = int(getr()) + c = int(l.getr()) } } @@ -1256,29 +1217,30 @@ l0: } case ':': - c1 = getc() + c1 = l.getc() if c1 == '=' { c = int(LCOLAS) goto lx } case '*': - c1 = getc() + c1 = l.getc() if c1 == '=' { op = OMUL goto asop } case '%': - c1 = getc() + c1 = l.getc() if c1 == '=' { op = OMOD goto asop } case '+': - c1 = getc() + c1 = l.getc() if c1 == '+' { + l.nlsemi = true c = int(LINC) goto lx } @@ -1289,8 +1251,9 @@ l0: } case '-': - c1 = getc() + c1 = l.getc() if c1 == '-' { + l.nlsemi = true c = int(LDEC) goto lx } @@ -1301,10 +1264,10 @@ l0: } case '>': - c1 = getc() + c1 = l.getc() if c1 == '>' { c = int(LRSH) - c1 = getc() + c1 = l.getc() if c1 == '=' { op = ORSH goto asop @@ -1321,10 +1284,10 @@ l0: c = int(LGT) case '<': - c1 = getc() + c1 = l.getc() if c1 == '<' { c = int(LLSH) - c1 = getc() + c1 = l.getc() if c1 == '=' { op = OLSH goto asop @@ -1346,21 +1309,21 @@ l0: c = int(LLT) case '=': - c1 = getc() + c1 = l.getc() if c1 == '=' { c = int(LEQ) goto lx } case '!': - c1 = getc() + c1 = l.getc() if c1 == '=' { c = int(LNE) goto lx } case '&': - c1 = getc() + c1 = l.getc() if c1 == '&' { c = int(LANDAND) goto lx @@ -1368,7 +1331,7 @@ l0: if c1 == '^' { c = int(LANDNOT) - c1 = getc() + c1 = l.getc() if c1 == '=' { op = OANDNOT goto asop @@ -1383,7 +1346,7 @@ l0: } case '|': - c1 = getc() + c1 = l.getc() if c1 == '|' { c = int(LOROR) goto lx @@ -1395,17 +1358,21 @@ l0: } case '^': - c1 = getc() + c1 = l.getc() if c1 == '=' { op = OXOR goto asop } + case ')', ']', '}': + l.nlsemi = true + goto lx + default: goto lx } - ungetc(c1) + l.ungetc(c1) lx: if Debug['x'] != 0 { @@ -1425,22 +1392,24 @@ lx: goto l0 } - return int32(c) + l.tok = int32(c) + return asop: - yylval.op = op + l.op = op if Debug['x'] != 0 { fmt.Printf("lex: TOKEN ASOP %s=\n", goopnames[op]) } - return LASOP + l.tok = LASOP + return // cp is set to lexbuf and some // prefix has been stored talph: for { if c >= utf8.RuneSelf { - ungetc(c) - r := rune(getr()) + l.ungetc(c) + r := rune(l.getr()) // 0xb7 · is used for internal names if !unicode.IsLetter(r) && !unicode.IsDigit(r) && (importpkg == nil || r != 0xb7) { @@ -1455,11 +1424,11 @@ talph: } else { cp.WriteByte(byte(c)) } - c = getc() + c = l.getc() } cp = nil - ungetc(c) + l.ungetc(c) s = LookupBytes(lexbuf.Bytes()) if s.Lexical == LIGNORE { @@ -1469,31 +1438,38 @@ talph: if Debug['x'] != 0 { fmt.Printf("lex: %s %s\n", s, lexname(int(s.Lexical))) } - yylval.sym = s - return int32(s.Lexical) + l.sym_ = s + switch s.Lexical { + case LNAME, LRETURN, LBREAK, LCONTINUE, LFALL: + l.nlsemi = true + } + l.tok = int32(s.Lexical) + return ncu: cp = nil - ungetc(c) + l.ungetc(c) str = lexbuf.String() - yylval.val.U = new(Mpint) - mpatofix(yylval.val.U.(*Mpint), str) - if yylval.val.U.(*Mpint).Ovf { + l.val.U = new(Mpint) + mpatofix(l.val.U.(*Mpint), str) + if l.val.U.(*Mpint).Ovf { Yyerror("overflow in constant") - Mpmovecfix(yylval.val.U.(*Mpint), 0) + Mpmovecfix(l.val.U.(*Mpint), 0) } if Debug['x'] != 0 { fmt.Printf("lex: integer literal\n") } litbuf = "literal " + str - return LLITERAL + l.nlsemi = true + l.tok = LLITERAL + return casedot: for { cp.WriteByte(byte(c)) - c = getc() + c = l.getc() if !isDigit(c) { break } @@ -1513,10 +1489,10 @@ caseep: Yyerror("malformed floating point constant") } cp.WriteByte(byte(c)) - c = getc() + c = l.getc() if c == '+' || c == '-' { cp.WriteByte(byte(c)) - c = getc() + c = l.getc() } if !isDigit(c) { @@ -1524,7 +1500,7 @@ caseep: } for isDigit(c) { cp.WriteByte(byte(c)) - c = getc() + c = l.getc() } if c == 'i' { @@ -1537,56 +1513,60 @@ casei: cp = nil str = lexbuf.String() - yylval.val.U = new(Mpcplx) - Mpmovecflt(&yylval.val.U.(*Mpcplx).Real, 0.0) - mpatoflt(&yylval.val.U.(*Mpcplx).Imag, str) - if yylval.val.U.(*Mpcplx).Imag.Val.IsInf() { + l.val.U = new(Mpcplx) + Mpmovecflt(&l.val.U.(*Mpcplx).Real, 0.0) + mpatoflt(&l.val.U.(*Mpcplx).Imag, str) + if l.val.U.(*Mpcplx).Imag.Val.IsInf() { Yyerror("overflow in imaginary constant") - Mpmovecflt(&yylval.val.U.(*Mpcplx).Imag, 0.0) + Mpmovecflt(&l.val.U.(*Mpcplx).Imag, 0.0) } if Debug['x'] != 0 { fmt.Printf("lex: imaginary literal\n") } litbuf = "literal " + str - return LLITERAL + l.nlsemi = true + l.tok = LLITERAL + return caseout: cp = nil - ungetc(c) + l.ungetc(c) str = lexbuf.String() - yylval.val.U = newMpflt() - mpatoflt(yylval.val.U.(*Mpflt), str) - if yylval.val.U.(*Mpflt).Val.IsInf() { + l.val.U = newMpflt() + mpatoflt(l.val.U.(*Mpflt), str) + if l.val.U.(*Mpflt).Val.IsInf() { Yyerror("overflow in float constant") - Mpmovecflt(yylval.val.U.(*Mpflt), 0.0) + Mpmovecflt(l.val.U.(*Mpflt), 0.0) } if Debug['x'] != 0 { fmt.Printf("lex: floating literal\n") } litbuf = "literal " + str - return LLITERAL + l.nlsemi = true + l.tok = LLITERAL + return strlit: - yylval.val.U = internString(cp.Bytes()) + l.val.U = internString(cp.Bytes()) if Debug['x'] != 0 { fmt.Printf("lex: string literal\n") } litbuf = "string literal" - return LLITERAL + l.nlsemi = true + l.tok = LLITERAL } var internedStrings = map[string]string{} func internString(b []byte) string { s, ok := internedStrings[string(b)] // string(b) here doesn't allocate - if ok { - return s + if !ok { + s = string(b) + internedStrings[s] = s } - s = string(b) - internedStrings[s] = s return s } @@ -1603,16 +1583,16 @@ func more(pp *string) bool { // //line parse.y:15 // as a discontinuity in sequential line numbers. // the next line of input comes from parse.y:15 -func getlinepragma() int { +func (l *lexer) getlinepragma() int { var cmd, verb, name string - c := int(getr()) + c := int(l.getr()) if c == 'g' { cp := &lexbuf cp.Reset() cp.WriteByte('g') // already read for { - c = int(getr()) + c = int(l.getr()) if c == EOF || c >= utf8.RuneSelf { return c } @@ -1704,7 +1684,7 @@ func getlinepragma() int { return c } for i := 1; i < 5; i++ { - c = int(getr()) + c = int(l.getr()) if c != int("line "[i]) { return c } @@ -1714,7 +1694,7 @@ func getlinepragma() int { cp.Reset() linep := 0 for { - c = int(getr()) + c = int(l.getr()) if c == EOF { return c } @@ -1895,113 +1875,57 @@ func pragcgo(text string) { } } -func yylex(yylval *yySymType) int32 { - lx := _yylex(yylval) - - if curio.nlsemi && lx == EOF { - // Treat EOF as "end of line" for the purposes - // of inserting a semicolon. - lx = ';' - } - - switch lx { - case LNAME, - LLITERAL, - LBREAK, - LCONTINUE, - LFALL, - LRETURN, - LINC, - LDEC, - ')', - '}', - ']': - curio.nlsemi = true - - default: - curio.nlsemi = false - } - - return lx -} - -func getc() int { - c := curio.peekc +func (l *lexer) getc() int { + c := l.peekc if c != 0 { - curio.peekc = curio.peekc1 - curio.peekc1 = 0 + l.peekc = l.peekc1 + l.peekc1 = 0 goto check } - if curio.bin == nil { - if len(curio.cp) == 0 { - c = 0 - } else { - c = int(curio.cp[0]) - curio.cp = curio.cp[1:] +loop: + c = obj.Bgetc(l.bin) + // recognize BOM (U+FEFF): UTF-8 encoding is 0xef 0xbb 0xbf + if c == 0xef { + buf, err := l.bin.Peek(2) + if err != nil { + yyerrorl(int(lexlineno), "illegal UTF-8 sequence ef % x followed by read error (%v)", string(buf), err) + errorexit() } - } else { - loop: - c = obj.Bgetc(curio.bin) - // recognize BOM (U+FEFF): UTF-8 encoding is 0xef 0xbb 0xbf - if c == 0xef { - buf, err := curio.bin.Peek(2) - if err != nil { - yyerrorl(int(lexlineno), "illegal UTF-8 sequence ef % x followed by read error (%v)", string(buf), err) - errorexit() - } - if buf[0] == 0xbb && buf[1] == 0xbf { - yyerrorl(int(lexlineno), "Unicode (UTF-8) BOM in middle of file") + if buf[0] == 0xbb && buf[1] == 0xbf { + yyerrorl(int(lexlineno), "Unicode (UTF-8) BOM in middle of file") - // consume BOM bytes - obj.Bgetc(curio.bin) - obj.Bgetc(curio.bin) - goto loop - } + // consume BOM bytes + obj.Bgetc(l.bin) + obj.Bgetc(l.bin) + goto loop } } check: - switch c { - case 0: - if curio.bin != nil { - Yyerror("illegal NUL byte") - break - } - fallthrough - - // insert \n at EOF - case EOF: - if curio.eofnl || curio.last == '\n' { - return EOF - } - curio.eofnl = true - c = '\n' - fallthrough - - case '\n': - if pushedio.bin == nil { - lexlineno++ - } + if c == 0 { + Yyerror("illegal NUL byte") + return 0 + } + if c == '\n' && importpkg == nil { + lexlineno++ } - - curio.last = c return c } -func ungetc(c int) { - curio.peekc1 = curio.peekc - curio.peekc = c - if c == '\n' && pushedio.bin == nil { +func (l *lexer) ungetc(c int) { + l.peekc1 = l.peekc + l.peekc = c + if c == '\n' && importpkg == nil { lexlineno-- } } -func getr() int32 { +func (l *lexer) getr() int32 { var buf [utf8.UTFMax]byte for i := 0; ; i++ { - c := getc() + c := l.getc() if i == 0 && c < utf8.RuneSelf { return int32(c) } @@ -2020,10 +1944,10 @@ func getr() int32 { } } -func escchar(e int, escflg *int, val *int64) bool { +func (l *lexer) escchar(e int, escflg *int, val *int64) bool { *escflg = 0 - c := int(getr()) + c := int(l.getr()) switch c { case EOF: Yyerror("eof in string") @@ -2045,7 +1969,7 @@ func escchar(e int, escflg *int, val *int64) bool { } u := 0 - c = int(getr()) + c = int(l.getr()) var i int switch c { case 'x': @@ -2072,23 +1996,23 @@ func escchar(e int, escflg *int, val *int64) bool { '6', '7': *escflg = 1 // it's a byte - l := int64(c) - '0' + x := int64(c) - '0' for i := 2; i > 0; i-- { - c = getc() + c = l.getc() if c >= '0' && c <= '7' { - l = l*8 + int64(c) - '0' + x = x*8 + int64(c) - '0' continue } Yyerror("non-octal character in escape sequence: %c", c) - ungetc(c) + l.ungetc(c) } - if l > 255 { - Yyerror("octal escape value > 255: %d", l) + if x > 255 { + Yyerror("octal escape value > 255: %d", x) } - *val = l + *val = x return false case 'a': @@ -2118,35 +2042,35 @@ func escchar(e int, escflg *int, val *int64) bool { return false hex: - l := int64(0) + x := int64(0) for ; i > 0; i-- { - c = getc() + c = l.getc() if c >= '0' && c <= '9' { - l = l*16 + int64(c) - '0' + x = x*16 + int64(c) - '0' continue } if c >= 'a' && c <= 'f' { - l = l*16 + int64(c) - 'a' + 10 + x = x*16 + int64(c) - 'a' + 10 continue } if c >= 'A' && c <= 'F' { - l = l*16 + int64(c) - 'A' + 10 + x = x*16 + int64(c) - 'A' + 10 continue } Yyerror("non-hex character in escape sequence: %c", c) - ungetc(c) + l.ungetc(c) break } - if u != 0 && (l > utf8.MaxRune || (0xd800 <= l && l < 0xe000)) { - Yyerror("invalid Unicode code point in escape sequence: %#x", l) - l = utf8.RuneError + if u != 0 && (x > utf8.MaxRune || (0xd800 <= x && x < 0xe000)) { + Yyerror("invalid Unicode code point in escape sequence: %#x", x) + x = utf8.RuneError } - *val = l + *val = x return false } diff --git a/src/cmd/compile/internal/gc/mkbuiltin.go b/src/cmd/compile/internal/gc/mkbuiltin.go index b1e4458692..13cde5e8aa 100644 --- a/src/cmd/compile/internal/gc/mkbuiltin.go +++ b/src/cmd/compile/internal/gc/mkbuiltin.go @@ -4,95 +4,90 @@ // +build ignore -// Generate builtin.go from builtin/runtime.go and builtin/unsafe.go -// (passed as arguments on the command line by a go:generate comment). +// Generate builtin.go from builtin/runtime.go and builtin/unsafe.go. // Run this after changing builtin/runtime.go and builtin/unsafe.go // or after changing the export metadata format in the compiler. // Either way, you need to have a working compiler binary first. package main import ( - "bufio" + "bytes" + "flag" "fmt" "io" + "io/ioutil" "log" "os" "os/exec" - "strings" ) +var stdout = flag.Bool("stdout", false, "write to stdout instead of builtin.go") + func main() { - f, err := os.Create("builtin.go") + flag.Parse() + + var b bytes.Buffer + fmt.Fprintln(&b, "// AUTO-GENERATED by mkbuiltin.go; DO NOT EDIT") + fmt.Fprintln(&b, "") + fmt.Fprintln(&b, "package gc") + + mkbuiltin(&b, "runtime") + mkbuiltin(&b, "unsafe") + + var err error + if *stdout { + _, err = os.Stdout.Write(b.Bytes()) + } else { + err = ioutil.WriteFile("builtin.go", b.Bytes(), 0666) + } if err != nil { log.Fatal(err) } - defer f.Close() - w := bufio.NewWriter(f) - - fmt.Fprintln(w, "// AUTO-GENERATED by mkbuiltin.go; DO NOT EDIT") - fmt.Fprintln(w, "") - fmt.Fprintln(w, "package gc") - - for _, name := range os.Args[1:] { - mkbuiltin(w, name) - } - - if err := w.Flush(); err != nil { - log.Fatal(err) - } } -// Compile .go file, import data from .6 file, and write Go string version. +// Compile .go file, import data from .o file, and write Go string version. func mkbuiltin(w io.Writer, name string) { - if err := exec.Command("go", "tool", "compile", "-A", "builtin/"+name+".go").Run(); err != nil { + args := []string{"tool", "compile", "-A"} + if name == "runtime" { + args = append(args, "-u") + } + args = append(args, "builtin/"+name+".go") + + if err := exec.Command("go", args...).Run(); err != nil { log.Fatal(err) } obj := name + ".o" defer os.Remove(obj) - r, err := os.Open(obj) + b, err := ioutil.ReadFile(obj) if err != nil { log.Fatal(err) } - defer r.Close() - scanner := bufio.NewScanner(r) // Look for $$ that introduces imports. - for scanner.Scan() { - if strings.Contains(scanner.Text(), "$$") { - goto Begin - } + i := bytes.Index(b, []byte("\n$$\n")) + if i < 0 { + log.Fatal("did not find beginning of imports") } - log.Fatal("did not find beginning of imports") + i += 4 -Begin: - initfunc := fmt.Sprintf("init_%s_function", name) - - fmt.Fprintf(w, "\nconst %simport = \"\" +\n", name) - - // sys.go claims to be in package PACKAGE to avoid - // conflicts during "go tool compile sys.go". Rename PACKAGE to $2. - replacer := strings.NewReplacer("PACKAGE", name) - - // Process imports, stopping at $$ that closes them. - for scanner.Scan() { - p := scanner.Text() - if strings.Contains(p, "$$") { - goto End - } + // Look for $$ that closes imports. + j := bytes.Index(b[i:], []byte("\n$$\n")) + if j < 0 { + log.Fatal("did not find end of imports") + } + j += i + 4 + // Process and reformat imports. + fmt.Fprintf(w, "\nconst %simport = \"\"", name) + for _, p := range bytes.SplitAfter(b[i:j], []byte("\n")) { // Chop leading white space. - p = strings.TrimLeft(p, " \t") - - // Cut out decl of init_$1_function - it doesn't exist. - if strings.Contains(p, initfunc) { + p = bytes.TrimLeft(p, " \t") + if len(p) == 0 { continue } - fmt.Fprintf(w, "\t%q +\n", replacer.Replace(p)+"\n") + fmt.Fprintf(w, " +\n\t%q", p) } - log.Fatal("did not find end of imports") - -End: - fmt.Fprintf(w, "\t\"$$\\n\"\n") + fmt.Fprintf(w, "\n") } diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index 66549be5c4..0a96da61fa 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -233,8 +233,7 @@ func stringsym(s string) (hdr, data *Sym) { off = dsname(symdata, off, s[n:n+m]) } - off = duint8(symdata, off, 0) // terminating NUL for runtime - off = (off + Widthptr - 1) &^ (Widthptr - 1) // round to pointer alignment + off = duint8(symdata, off, 0) // terminating NUL for runtime ggloblsym(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL) return symhdr, symdata diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index 05cd53af41..12405d5f38 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -42,8 +42,7 @@ import ( // Order holds state during the ordering process. type Order struct { out *NodeList // list of generated statements - temp *NodeList // head of stack of temporary variables - free *NodeList // free list of NodeList* structs (for use in temp) + temp []*Node // stack of temporary variables } // Order rewrites fn->nbody to apply the ordering constraints @@ -68,14 +67,7 @@ func ordertemp(t *Type, order *Order, clear bool) *Node { order.out = list(order.out, a) } - l := order.free - if l == nil { - l = new(NodeList) - } - order.free = l.Next - l.Next = order.temp - l.N = var_ - order.temp = l + order.temp = append(order.temp, var_) return var_ } @@ -215,42 +207,35 @@ func orderaddrtemp(np **Node, order *Order) { *np = ordercopyexpr(n, n.Type, order, 0) } +type ordermarker int + // Marktemp returns the top of the temporary variable stack. -func marktemp(order *Order) *NodeList { - return order.temp +func marktemp(order *Order) ordermarker { + return ordermarker(len(order.temp)) } // Poptemp pops temporaries off the stack until reaching the mark, // which must have been returned by marktemp. -func poptemp(mark *NodeList, order *Order) { - var l *NodeList - - for { - l = order.temp - if l == mark { - break - } - order.temp = l.Next - l.Next = order.free - order.free = l - } +func poptemp(mark ordermarker, order *Order) { + order.temp = order.temp[:mark] } // Cleantempnopop emits to *out VARKILL instructions for each temporary // above the mark on the temporary stack, but it does not pop them // from the stack. -func cleantempnopop(mark *NodeList, order *Order, out **NodeList) { +func cleantempnopop(mark ordermarker, order *Order, out **NodeList) { var kill *Node - for l := order.temp; l != mark; l = l.Next { - if l.N.Name.Keepalive { - l.N.Name.Keepalive = false - l.N.Addrtaken = true // ensure SSA keeps the l.N variable - kill = Nod(OVARLIVE, l.N, nil) + for i := len(order.temp) - 1; i >= int(mark); i-- { + n := order.temp[i] + if n.Name.Keepalive { + n.Name.Keepalive = false + n.Addrtaken = true // ensure SSA keeps the n variable + kill = Nod(OVARLIVE, n, nil) typecheck(&kill, Etop) *out = list(*out, kill) } - kill = Nod(OVARKILL, l.N, nil) + kill = Nod(OVARKILL, n, nil) typecheck(&kill, Etop) *out = list(*out, kill) } @@ -258,7 +243,7 @@ func cleantempnopop(mark *NodeList, order *Order, out **NodeList) { // Cleantemp emits VARKILL instructions for each temporary above the // mark on the temporary stack and removes them from the stack. -func cleantemp(top *NodeList, order *Order) { +func cleantemp(top ordermarker, order *Order) { cleantempnopop(top, order, &order.out) poptemp(top, order) } @@ -290,13 +275,7 @@ func orderexprinplace(np **Node, outer *Order) { // insert new temporaries from order // at head of outer list. - lp := &order.temp - - for *lp != nil { - lp = &(*lp).Next - } - *lp = outer.temp - outer.temp = order.temp + outer.temp = append(outer.temp, order.temp...) *np = n } diff --git a/src/cmd/compile/internal/gc/parser.go b/src/cmd/compile/internal/gc/parser.go index 054cf73656..fbc5a5e1eb 100644 --- a/src/cmd/compile/internal/gc/parser.go +++ b/src/cmd/compile/internal/gc/parser.go @@ -5,7 +5,7 @@ package gc // The recursive-descent parser is built around a slighty modified grammar -// of Go to accomodate for the constraints imposed by strict one token look- +// of Go to accommodate for the constraints imposed by strict one token look- // ahead, and for better error handling. Subsequent checks of the constructed // syntax tree restrict the language accepted by the compiler to proper Go. // @@ -13,6 +13,7 @@ package gc // to handle optional commas and semicolons before a closing ) or } . import ( + "cmd/internal/obj" "fmt" "strconv" "strings" @@ -20,81 +21,31 @@ import ( const trace = false // if set, parse tracing can be enabled with -x -// TODO(gri) Once we handle imports w/o redirecting the underlying -// source of the lexer we can get rid of these. They are here for -// compatibility with the existing yacc-based parser setup (issue 13242). -var thenewparser parser // the parser in use -var savedstate []parser // saved parser state, used during import - -func push_parser() { - // Indentation (for tracing) must be preserved across parsers - // since we are changing the lexer source (and parser state) - // under foot, in the middle of productions. This won't be - // needed anymore once we fix issue 13242, but neither will - // be the push/pop_parser functionality. - // (Instead we could just use a global variable indent, but - // but eventually indent should be parser-specific anyway.) - indent := thenewparser.indent - savedstate = append(savedstate, thenewparser) - thenewparser = parser{indent: indent} // preserve indentation - thenewparser.next() +// parse_import parses the export data of a package that is imported. +func parse_import(bin *obj.Biobuf, indent []byte) { + newparser(bin, indent).import_package() } -func pop_parser() { - indent := thenewparser.indent - n := len(savedstate) - 1 - thenewparser = savedstate[n] - thenewparser.indent = indent // preserve indentation - savedstate = savedstate[:n] -} - -// parse_file sets up a new parser and parses a single Go source file. -func parse_file() { - thenewparser = parser{} - thenewparser.loadsys() - thenewparser.next() - thenewparser.file() -} - -// loadsys loads the definitions for the low-level runtime functions, -// so that the compiler can generate calls to them, -// but does not make the name "runtime" visible as a package. -func (p *parser) loadsys() { - if trace && Debug['x'] != 0 { - defer p.trace("loadsys")() - } - - importpkg = Runtimepkg - - if Debug['A'] != 0 { - cannedimports("runtime.Builtin", "package runtime\n\n$$\n\n") - } else { - cannedimports("runtime.Builtin", runtimeimport) - } - curio.importsafe = true - - p.import_package() - p.import_there() - - importpkg = nil +// parse_file parses a single Go source file. +func parse_file(bin *obj.Biobuf) { + newparser(bin, nil).file() } type parser struct { - tok int32 // next token (one-token look-ahead) - op Op // valid if tok == LASOP - val Val // valid if tok == LLITERAL - sym_ *Sym // valid if tok == LNAME - fnest int // function nesting level (for error handling) - xnest int // expression nesting level (for complit ambiguity resolution) - yy yySymType // for temporary use by next - indent []byte // tracing support + lexer + fnest int // function nesting level (for error handling) + xnest int // expression nesting level (for complit ambiguity resolution) + indent []byte // tracing support } -func (p *parser) next() { - p.tok = yylex(&p.yy) - p.op = p.yy.op - p.val = p.yy.val - p.sym_ = p.yy.sym +// newparser returns a new parser ready to parse from src. +// indent is the initial indentation for tracing output. +func newparser(src *obj.Biobuf, indent []byte) *parser { + var p parser + p.bin = src + p.indent = indent + p.next() + return &p } func (p *parser) got(tok int32) bool { @@ -347,108 +298,87 @@ func (p *parser) import_() { p.want(LIMPORT) if p.got('(') { for p.tok != EOF && p.tok != ')' { - p.import_stmt() + p.importdcl() if !p.osemi(')') { break } } p.want(')') } else { - p.import_stmt() - } -} - -func (p *parser) import_stmt() { - if trace && Debug['x'] != 0 { - defer p.trace("import_stmt")() - } - - line := int32(p.import_here()) - if p.tok == LPACKAGE { - p.import_package() - p.import_there() - - ipkg := importpkg - my := importmyname - importpkg = nil - importmyname = nil - - if my == nil { - my = Lookup(ipkg.Name) - } - - pack := Nod(OPACK, nil, nil) - pack.Sym = my - pack.Name.Pkg = ipkg - pack.Lineno = line - - if strings.HasPrefix(my.Name, ".") { - importdot(ipkg, pack) - return - } - if my.Name == "init" { - lineno = line - Yyerror("cannot import package as init - init must be a func") - return - } - if my.Name == "_" { - return - } - if my.Def != nil { - lineno = line - redeclare(my, "as imported package name") - } - my.Def = pack - my.Lastlineno = line - my.Block = 1 // at top level - - return - } - - p.import_there() - // When an invalid import path is passed to importfile, - // it calls Yyerror and then sets up a fake import with - // no package statement. This allows us to test more - // than one invalid import statement in a single file. - if nerrors == 0 { - Fatalf("phase error in import") + p.importdcl() } } // ImportSpec = [ "." | PackageName ] ImportPath . // ImportPath = string_lit . -// -// import_here switches the underlying lexed source to the export data -// of the imported package. -func (p *parser) import_here() int { +func (p *parser) importdcl() { if trace && Debug['x'] != 0 { - defer p.trace("import_here")() + defer p.trace("importdcl")() } - importmyname = nil + var my *Sym switch p.tok { case LNAME, '@', '?': // import with given name - importmyname = p.sym() + my = p.sym() case '.': // import into my name space - importmyname = Lookup(".") + my = Lookup(".") p.next() } - var path Val - if p.tok == LLITERAL { - path = p.val - p.next() - } else { + if p.tok != LLITERAL { p.syntax_error("missing import path; require quoted string") p.advance(';', ')') + return } - line := parserline() - importfile(&path, line) - return line + line := int32(parserline()) + path := p.val + p.next() + + importfile(&path, p.indent) + if importpkg == nil { + if nerrors == 0 { + Fatalf("phase error in import") + } + return + } + + ipkg := importpkg + importpkg = nil + + ipkg.Direct = true + + if my == nil { + my = Lookup(ipkg.Name) + } + + pack := Nod(OPACK, nil, nil) + pack.Sym = my + pack.Name.Pkg = ipkg + pack.Lineno = line + + if strings.HasPrefix(my.Name, ".") { + importdot(ipkg, pack) + return + } + if my.Name == "init" { + lineno = line + Yyerror("cannot import package as init - init must be a func") + return + } + if my.Name == "_" { + return + } + if my.Def != nil { + lineno = line + redeclare(my, "as imported package name") + } + my.Def = pack + my.Lastlineno = line + my.Block = 1 // at top level } // import_package parses the header of an imported package as exported @@ -467,9 +397,10 @@ func (p *parser) import_package() { p.import_error() } + importsafe := false if p.tok == LNAME { if p.sym_.Name == "safe" { - curio.importsafe = true + importsafe = true } p.next() } @@ -481,23 +412,9 @@ func (p *parser) import_package() { } else if importpkg.Name != name { Yyerror("conflicting names %s and %s for package %q", importpkg.Name, name, importpkg.Path) } - if incannedimport == 0 { - importpkg.Direct = true - } - importpkg.Safe = curio.importsafe - - if safemode != 0 && !curio.importsafe { - Yyerror("cannot import unsafe package %q", importpkg.Path) - } -} - -// import_there parses the imported package definitions and then switches -// the underlying lexed source back to the importing package. -func (p *parser) import_there() { - if trace && Debug['x'] != 0 { - defer p.trace("import_there")() - } + importpkg.Safe = importsafe + typecheckok = true defercheckwidth() p.hidden_import_list() @@ -508,7 +425,7 @@ func (p *parser) import_there() { } resumecheckwidth() - unimportfile() + typecheckok = false } // Declaration = ConstDecl | TypeDecl | VarDecl . @@ -1136,65 +1053,16 @@ func (p *parser) if_stmt() *Node { stmt.Nbody = p.loop_body("if clause") - l := p.elseif_list_else() // does markdcl - - n := stmt - popdcl() - for nn := l; nn != nil; nn = nn.Next { - if nn.N.Op == OIF { - popdcl() - } - n.Rlist = list1(nn.N) - n = nn.N - } - - return stmt -} - -func (p *parser) elseif() *NodeList { - if trace && Debug['x'] != 0 { - defer p.trace("elseif")() - } - - // LELSE LIF already consumed - markdcl() // matching popdcl in if_stmt - - stmt := p.if_header() - if stmt.Left == nil { - Yyerror("missing condition in if statement") - } - - stmt.Nbody = p.loop_body("if clause") - - return list1(stmt) -} - -func (p *parser) elseif_list_else() (l *NodeList) { - if trace && Debug['x'] != 0 { - defer p.trace("elseif_list_else")() - } - - for p.got(LELSE) { - if p.got(LIF) { - l = concat(l, p.elseif()) + if p.got(LELSE) { + if p.tok == LIF { + stmt.Rlist = list1(p.if_stmt()) } else { - l = concat(l, p.else_()) - break + stmt.Rlist = list1(p.compound_stmt(true)) } } - return l -} - -func (p *parser) else_() *NodeList { - if trace && Debug['x'] != 0 { - defer p.trace("else")() - } - - l := &NodeList{N: p.compound_stmt(true)} - l.End = l - return l - + popdcl() + return stmt } // switch_stmt parses both expression and type switch statements. diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index f90f89a805..987da2bfdd 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -187,21 +187,12 @@ func emitptrargsmap() { // the top of the stack and increasing in size. // Non-autos sort on offset. func cmpstackvarlt(a, b *Node) bool { - if a.Class != b.Class { - if a.Class == PAUTO { - return false - } - return true + if (a.Class == PAUTO) != (b.Class == PAUTO) { + return b.Class == PAUTO } if a.Class != PAUTO { - if a.Xoffset < b.Xoffset { - return true - } - if a.Xoffset > b.Xoffset { - return false - } - return false + return a.Xoffset < b.Xoffset } if a.Used != b.Used { @@ -220,11 +211,8 @@ func cmpstackvarlt(a, b *Node) bool { return ap } - if a.Type.Width < b.Type.Width { - return false - } - if a.Type.Width > b.Type.Width { - return true + if a.Type.Width != b.Type.Width { + return a.Type.Width > b.Type.Width } return a.Sym.Name < b.Sym.Name diff --git a/src/cmd/compile/internal/gc/pgen_test.go b/src/cmd/compile/internal/gc/pgen_test.go index ebc9101135..909b8a9507 100644 --- a/src/cmd/compile/internal/gc/pgen_test.go +++ b/src/cmd/compile/internal/gc/pgen_test.go @@ -40,6 +40,16 @@ func TestCmpstackvar(t *testing.T) { Node{Class: PFUNC, Xoffset: 10}, false, }, + { + Node{Class: PPARAM, Xoffset: 10}, + Node{Class: PPARAMOUT, Xoffset: 20}, + true, + }, + { + Node{Class: PPARAMOUT, Xoffset: 10}, + Node{Class: PPARAM, Xoffset: 20}, + true, + }, { Node{Class: PAUTO, Used: true}, Node{Class: PAUTO, Used: false}, @@ -101,6 +111,10 @@ func TestCmpstackvar(t *testing.T) { if got != d.lt { t.Errorf("want %#v < %#v", d.a, d.b) } + // If we expect a < b to be true, check that b < a is false. + if d.lt && cmpstackvarlt(&d.b, &d.a) { + t.Errorf("unexpected %#v < %#v", d.b, d.a) + } } } diff --git a/src/cmd/compile/internal/gc/popt.go b/src/cmd/compile/internal/gc/popt.go index 4d71ab643d..b708222845 100644 --- a/src/cmd/compile/internal/gc/popt.go +++ b/src/cmd/compile/internal/gc/popt.go @@ -241,6 +241,19 @@ var flowmark int // will not have flow graphs and consequently will not be optimized. const MaxFlowProg = 50000 +var ffcache []Flow // reusable []Flow, to reduce allocation + +func growffcache(n int) { + if n > cap(ffcache) { + n = (n * 5) / 4 + if n > MaxFlowProg { + n = MaxFlowProg + } + ffcache = make([]Flow, n) + } + ffcache = ffcache[:n] +} + func Flowstart(firstp *obj.Prog, newData func() interface{}) *Graph { // Count and mark instructions to annotate. nf := 0 @@ -268,7 +281,9 @@ func Flowstart(firstp *obj.Prog, newData func() interface{}) *Graph { // Allocate annotations and assign to instructions. graph := new(Graph) - ff := make([]Flow, nf) + + growffcache(nf) + ff := ffcache start := &ff[0] id := 0 var last *Flow @@ -331,6 +346,10 @@ func Flowend(graph *Graph) { f.Prog.Info.Flags = 0 // drop cached proginfo f.Prog.Opt = nil } + clear := ffcache[:graph.Num] + for i := range clear { + clear[i] = Flow{} + } } // find looping structure diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index f6dd75ec4a..37c3bc9e6d 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -45,7 +45,7 @@ func siglt(a, b *Sig) bool { // the given map type. This type is not visible to users - // we include only enough information to generate a correct GC // program for it. -// Make sure this stays in sync with ../../runtime/hashmap.go! +// Make sure this stays in sync with ../../../../runtime/hashmap.go! const ( BUCKETSIZE = 8 MAXKEYSIZE = 128 @@ -149,7 +149,7 @@ func mapbucket(t *Type) *Type { } // Builds a type representing a Hmap structure for the given map type. -// Make sure this stays in sync with ../../runtime/hashmap.go! +// Make sure this stays in sync with ../../../../runtime/hashmap.go! func hmap(t *Type) *Type { if t.Hmap != nil { return t.Hmap @@ -186,7 +186,7 @@ func hiter(t *Type) *Type { } // build a struct: - // hash_iter { + // hiter { // key *Key // val *Value // t *MapType @@ -200,7 +200,7 @@ func hiter(t *Type) *Type { // bucket uintptr // checkBucket uintptr // } - // must match ../../runtime/hashmap.go:hash_iter. + // must match ../../../../runtime/hashmap.go:hiter. var field [12]*Type field[0] = makefield("key", Ptrto(t.Down)) @@ -473,7 +473,7 @@ func dgopkgpath(s *Sym, ot int, pkg *Pkg) int { } // uncommonType -// ../../runtime/type.go:/uncommonType +// ../../../../runtime/type.go:/uncommonType func dextratype(sym *Sym, off int, t *Type, ptroff int) int { m := methods(t) if t.Sym == nil && len(m) == 0 { @@ -513,7 +513,7 @@ func dextratype(sym *Sym, off int, t *Type, ptroff int) int { // methods for _, a := range m { // method - // ../../runtime/type.go:/method + // ../../../../runtime/type.go:/method ot = dgostringptr(s, ot, a.name) ot = dgopkgpath(s, ot, a.pkg) @@ -710,21 +710,21 @@ func dcommontype(s *Sym, ot int, t *Type) int { gcsym, useGCProg, ptrdata := dgcsym(t) - // ../../pkg/reflect/type.go:/^type.commonType + // ../../../../reflect/type.go:/^type.rtype // actual type structure - // type commonType struct { + // type rtype struct { // size uintptr - // ptrsize uintptr + // ptrdata uintptr // hash uint32 // _ uint8 // align uint8 // fieldAlign uint8 // kind uint8 - // alg unsafe.Pointer - // gcdata unsafe.Pointer + // alg *typeAlg + // gcdata *byte // string *string - // *extraType - // ptrToThis *Type + // *uncommonType + // ptrToThis *rtype // } ot = duintptr(s, ot, uint64(t.Width)) ot = duintptr(s, ot, uint64(ptrdata)) @@ -1010,7 +1010,7 @@ ok: case TARRAY: if t.Bound >= 0 { - // ../../runtime/type.go:/ArrayType + // ../../../../runtime/type.go:/arrayType s1 := dtypesym(t.Type) t2 := typ(TARRAY) @@ -1023,7 +1023,7 @@ ok: ot = dsymptr(s, ot, s2, 0) ot = duintptr(s, ot, uint64(t.Bound)) } else { - // ../../runtime/type.go:/SliceType + // ../../../../runtime/type.go:/sliceType s1 := dtypesym(t.Type) ot = dcommontype(s, ot, t) @@ -1031,7 +1031,7 @@ ok: ot = dsymptr(s, ot, s1, 0) } - // ../../runtime/type.go:/ChanType + // ../../../../runtime/type.go:/chanType case TCHAN: s1 := dtypesym(t.Type) @@ -1090,7 +1090,7 @@ ok: dtypesym(a.type_) } - // ../../../runtime/type.go:/InterfaceType + // ../../../../runtime/type.go:/interfaceType ot = dcommontype(s, ot, t) xt = ot - 2*Widthptr @@ -1098,14 +1098,14 @@ ok: ot = duintxx(s, ot, uint64(n), Widthint) ot = duintxx(s, ot, uint64(n), Widthint) for _, a := range m { - // ../../../runtime/type.go:/imethod + // ../../../../runtime/type.go:/imethod ot = dgostringptr(s, ot, a.name) ot = dgopkgpath(s, ot, a.pkg) ot = dsymptr(s, ot, dtypesym(a.type_), 0) } - // ../../../runtime/type.go:/MapType + // ../../../../runtime/type.go:/mapType case TMAP: s1 := dtypesym(t.Down) @@ -1140,20 +1140,20 @@ ok: case TPTR32, TPTR64: if t.Type.Etype == TANY { - // ../../runtime/type.go:/UnsafePointerType + // ../../../../runtime/type.go:/UnsafePointerType ot = dcommontype(s, ot, t) break } - // ../../runtime/type.go:/PtrType + // ../../../../runtime/type.go:/ptrType s1 := dtypesym(t.Type) ot = dcommontype(s, ot, t) xt = ot - 2*Widthptr ot = dsymptr(s, ot, s1, 0) - // ../../runtime/type.go:/StructType + // ../../../../runtime/type.go:/structType // for security, only the exported fields. case TSTRUCT: n := 0 @@ -1169,7 +1169,7 @@ ok: ot = duintxx(s, ot, uint64(n), Widthint) ot = duintxx(s, ot, uint64(n), Widthint) for t1 := t.Type; t1 != nil; t1 = t1.Down { - // ../../runtime/type.go:/structField + // ../../../../runtime/type.go:/structField if t1.Sym != nil && t1.Embedded == 0 { ot = dgostringptr(s, ot, t1.Sym.Name) if exportname(t1.Sym.Name) { @@ -1349,7 +1349,7 @@ func dalgsym(t *Type) *Sym { ggloblsym(eqfunc, int32(Widthptr), obj.DUPOK|obj.RODATA) } - // ../../runtime/alg.go:/typeAlg + // ../../../../runtime/alg.go:/typeAlg ot := 0 ot = dsymptr(s, ot, hashfunc, 0) diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 19fda373bf..13d8663706 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -561,7 +561,7 @@ func (s *state) stmt(n *Node) { case OAS2DOTTYPE: res, resok := s.dottype(n.Rlist.N, true) - s.assign(n.List.N, res, false, false, n.Lineno) + s.assign(n.List.N, res, needwritebarrier(n.List.N, n.Rlist.N), false, n.Lineno) s.assign(n.List.Next.N, resok, false, false, n.Lineno) return diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index 0d25ddf2af..b6a26489e6 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -116,12 +116,6 @@ func Yyerror(format string, args ...interface{}) { if strings.HasPrefix(msg, "syntax error") { nsyntaxerrors++ - // An unexpected EOF caused a syntax error. Use the previous - // line number since getc generated a fake newline character. - if curio.eofnl { - lexlineno = prevlineno - } - // only one syntax error per line if int32(yyerror_lastsyntax) == lexlineno { return @@ -465,6 +459,15 @@ func algtype1(t *Type, bad **Type) int { return a } + switch t.Bound { + case 0: + // We checked above that the element type is comparable. + return AMEM + case 1: + // Single-element array is same as its lone element. + return a + } + return -1 // needs special compare case TSTRUCT: @@ -500,28 +503,20 @@ func algtype1(t *Type, bad **Type) int { func algtype(t *Type) int { a := algtype1(t, nil) - if a == AMEM || a == ANOEQ { - if Isslice(t) { - return ASLICE - } + if a == AMEM { switch t.Width { case 0: - return a + AMEM0 - AMEM - + return AMEM0 case 1: - return a + AMEM8 - AMEM - + return AMEM8 case 2: - return a + AMEM16 - AMEM - + return AMEM16 case 4: - return a + AMEM32 - AMEM - + return AMEM32 case 8: - return a + AMEM64 - AMEM - + return AMEM64 case 16: - return a + AMEM128 - AMEM + return AMEM128 } } @@ -2640,17 +2635,13 @@ func genhash(sym *Sym, t *Type) { safemode = old_safemode } -// Return node for -// if p.field != q.field { return false } +// eqfield returns the node +// p.field == q.field func eqfield(p *Node, q *Node, field *Node) *Node { nx := Nod(OXDOT, p, field) ny := Nod(OXDOT, q, field) - nif := Nod(OIF, nil, nil) - nif.Left = Nod(ONE, nx, ny) - r := Nod(ORETURN, nil, nil) - r.List = list(r.List, Nodbool(false)) - nif.Nbody = list(nif.Nbody, r) - return nif + ne := Nod(OEQ, nx, ny) + return ne } func eqmemfunc(size int64, type_ *Type, needsize *int) *Node { @@ -2671,8 +2662,8 @@ func eqmemfunc(size int64, type_ *Type, needsize *int) *Node { return fn } -// Return node for -// if !memequal(&p.field, &q.field [, size]) { return false } +// eqmem returns the node +// memequal(&p.field, &q.field [, size]) func eqmem(p *Node, q *Node, field *Node, size int64) *Node { var needsize int @@ -2690,15 +2681,11 @@ func eqmem(p *Node, q *Node, field *Node, size int64) *Node { call.List = list(call.List, Nodintconst(size)) } - nif := Nod(OIF, nil, nil) - nif.Left = Nod(ONOT, call, nil) - r := Nod(ORETURN, nil, nil) - r.List = list(r.List, Nodbool(false)) - nif.Nbody = list(nif.Nbody, r) - return nif + return call } -// Generate a helper function to check equality of two values of type t. +// geneq generates a helper function to +// check equality of two values of type t. func geneq(sym *Sym, t *Type) { if Debug['r'] != 0 { fmt.Printf("geneq %v %v\n", sym, t) @@ -2768,12 +2755,18 @@ func geneq(sym *Sym, t *Type) { nrange.Nbody = list(nrange.Nbody, nif) fn.Nbody = list(fn.Nbody, nrange) - // Walk the struct using memequal for runs of AMEM + // return true + ret := Nod(ORETURN, nil, nil) + ret.List = list(ret.List, Nodbool(true)) + fn.Nbody = list(fn.Nbody, ret) + + // Walk the struct using memequal for runs of AMEM // and calling specific equality tests for the others. // Skip blank-named fields. case TSTRUCT: var first *Type + var conjuncts []*Node offend := int64(0) var size int64 for t1 := t.Type; ; t1 = t1.Down { @@ -2796,17 +2789,17 @@ func geneq(sym *Sym, t *Type) { // cross-package unexported fields. if first != nil { if first.Down == t1 { - fn.Nbody = list(fn.Nbody, eqfield(np, nq, newname(first.Sym))) + conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym))) } else if first.Down.Down == t1 { - fn.Nbody = list(fn.Nbody, eqfield(np, nq, newname(first.Sym))) + conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym))) first = first.Down if !isblanksym(first.Sym) { - fn.Nbody = list(fn.Nbody, eqfield(np, nq, newname(first.Sym))) + conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym))) } } else { // More than two fields: use memequal. size = offend - first.Width // first->width is offset - fn.Nbody = list(fn.Nbody, eqmem(np, nq, newname(first.Sym), size)) + conjuncts = append(conjuncts, eqmem(np, nq, newname(first.Sym), size)) } first = nil @@ -2820,16 +2813,27 @@ func geneq(sym *Sym, t *Type) { } // Check this field, which is not just memory. - fn.Nbody = list(fn.Nbody, eqfield(np, nq, newname(t1.Sym))) + conjuncts = append(conjuncts, eqfield(np, nq, newname(t1.Sym))) } + + var and *Node + switch len(conjuncts) { + case 0: + and = Nodbool(true) + case 1: + and = conjuncts[0] + default: + and = Nod(OANDAND, conjuncts[0], conjuncts[1]) + for _, conjunct := range conjuncts[2:] { + and = Nod(OANDAND, and, conjunct) + } + } + + ret := Nod(ORETURN, nil, nil) + ret.List = list(ret.List, and) + fn.Nbody = list(fn.Nbody, ret) } - // return true - r := Nod(ORETURN, nil, nil) - - r.List = list(r.List, Nodbool(true)) - fn.Nbody = list(fn.Nbody, r) - if Debug['r'] != 0 { dumplist("geneq body", fn.Nbody) } @@ -2847,10 +2851,18 @@ func geneq(sym *Sym, t *Type) { // for a struct containing a reflect.Value, which itself has // an unexported field of type unsafe.Pointer. old_safemode := safemode - safemode = 0 + + // Disable checknils while compiling this code. + // We are comparing a struct or an array, + // neither of which can be nil, and our comparisons + // are shallow. + Disable_checknil++ + funccompile(fn) + safemode = old_safemode + Disable_checknil-- } func ifacelookdot(s *Sym, t *Type, followptr *bool, ignorecase int) *Type { diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go index f0433f3df7..661b3ee5a9 100644 --- a/src/cmd/compile/internal/gc/swt.go +++ b/src/cmd/compile/internal/gc/swt.go @@ -549,20 +549,6 @@ func (s *typeSwitch) walk(sw *Node) { // set up labels and jumps casebody(sw, s.facename) - // calculate type hash - t := cond.Right.Type - if isnilinter(t) { - a = syslook("efacethash", 1) - } else { - a = syslook("ifacethash", 1) - } - substArgTypes(a, t) - a = Nod(OCALL, a, nil) - a.List = list1(s.facename) - a = Nod(OAS, s.hashname, a) - typecheck(&a, Etop) - cas = list(cas, a) - cc := caseClauses(sw, switchKindType) sw.List = nil var def *Node @@ -572,22 +558,66 @@ func (s *typeSwitch) walk(sw *Node) { } else { def = Nod(OBREAK, nil, nil) } + var typenil *Node + if len(cc) > 0 && cc[0].typ == caseKindTypeNil { + typenil = cc[0].node.Right + cc = cc[1:] + } + + // For empty interfaces, do: + // if e._type == nil { + // do nil case if it exists, otherwise default + // } + // h := e._type.hash + // Use a similar strategy for non-empty interfaces. + + // Get interface descriptor word. + typ := Nod(OITAB, s.facename, nil) + + // Check for nil first. + i := Nod(OIF, nil, nil) + i.Left = Nod(OEQ, typ, nodnil()) + if typenil != nil { + // Do explicit nil case right here. + i.Nbody = list1(typenil) + } else { + // Jump to default case. + lbl := newCaseLabel() + i.Nbody = list1(Nod(OGOTO, lbl, nil)) + // Wrap default case with label. + blk := Nod(OBLOCK, nil, nil) + blk.List = list(list1(Nod(OLABEL, lbl, nil)), def) + def = blk + } + typecheck(&i.Left, Erv) + cas = list(cas, i) + + if !isnilinter(cond.Right.Type) { + // Load type from itab. + typ = Nod(ODOTPTR, typ, nil) + typ.Type = Ptrto(Types[TUINT8]) + typ.Typecheck = 1 + typ.Xoffset = int64(Widthptr) // offset of _type in runtime.itab + typ.Bounded = true // guaranteed not to fault + } + // Load hash from type. + h := Nod(ODOTPTR, typ, nil) + h.Type = Types[TUINT32] + h.Typecheck = 1 + h.Xoffset = int64(2 * Widthptr) // offset of hash in runtime._type + h.Bounded = true // guaranteed not to fault + a = Nod(OAS, s.hashname, h) + typecheck(&a, Etop) + cas = list(cas, a) // insert type equality check into each case block for _, c := range cc { n := c.node switch c.typ { - case caseKindTypeNil: - var v Val - v.U = new(NilVal) - a = Nod(OIF, nil, nil) - a.Left = Nod(OEQ, s.facename, nodlit(v)) - typecheck(&a.Left, Erv) - a.Nbody = list1(n.Right) // if i==nil { goto l } - n.Right = a - case caseKindTypeVar, caseKindTypeConst: n.Right = s.typeone(n) + default: + Fatalf("typeSwitch with bad kind: %d", c.typ) } } diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index f74bb334aa..8fd6f85575 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -936,7 +936,6 @@ OpSwitch: n.Type = n.Right.Type n.Right = nil if n.Type == nil { - n.Type = nil return } } diff --git a/src/cmd/compile/internal/gc/util.go b/src/cmd/compile/internal/gc/util.go index 7ed3b39b83..18e990a91a 100644 --- a/src/cmd/compile/internal/gc/util.go +++ b/src/cmd/compile/internal/gc/util.go @@ -1,3 +1,7 @@ +// Copyright 2015 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 gc import ( diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index dddcb689ed..45b85b9b02 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -3193,6 +3193,21 @@ func walkcompare(np **Node, init **NodeList) { return } + if t.Etype == TARRAY { + // Zero- or single-element array, of any type. + switch t.Bound { + case 0: + finishcompare(np, n, Nodbool(n.Op == OEQ), init) + return + case 1: + l0 := Nod(OINDEX, l, Nodintconst(0)) + r0 := Nod(OINDEX, r, Nodintconst(0)) + a := Nod(n.Op, l0, r0) + finishcompare(np, n, a, init) + return + } + } + if t.Etype == TSTRUCT && countfield(t) <= 4 { // Struct of four or fewer fields. // Inline comparisons. diff --git a/src/cmd/cover/cover.go b/src/cmd/cover/cover.go index 31ec434546..c5d1682651 100644 --- a/src/cmd/cover/cover.go +++ b/src/cmd/cover/cover.go @@ -181,6 +181,10 @@ func (f *File) Visit(node ast.Node) ast.Visitor { } n.List = f.addCounters(n.Lbrace, n.Rbrace+1, n.List, true) // +1 to step past closing brace. case *ast.IfStmt: + if n.Init != nil { + ast.Walk(f, n.Init) + } + ast.Walk(f, n.Cond) ast.Walk(f, n.Body) if n.Else == nil { return nil @@ -219,11 +223,21 @@ func (f *File) Visit(node ast.Node) ast.Visitor { case *ast.SwitchStmt: // Don't annotate an empty switch - creates a syntax error. if n.Body == nil || len(n.Body.List) == 0 { + if n.Init != nil { + ast.Walk(f, n.Init) + } + if n.Tag != nil { + ast.Walk(f, n.Tag) + } return nil } case *ast.TypeSwitchStmt: // Don't annotate an empty type switch - creates a syntax error. if n.Body == nil || len(n.Body.List) == 0 { + if n.Init != nil { + ast.Walk(f, n.Init) + } + ast.Walk(f, n.Assign) return nil } } diff --git a/src/cmd/cover/testdata/test.go b/src/cmd/cover/testdata/test.go index 9013950a2b..c4c0e15b0b 100644 --- a/src/cmd/cover/testdata/test.go +++ b/src/cmd/cover/testdata/test.go @@ -24,6 +24,7 @@ func testAll() { testSelect2() testPanic() testEmptySwitches() + testFunctionLiteral() } // The indexes of the counters in testPanic are known to main.go @@ -216,3 +217,32 @@ func testEmptySwitches() { <-c check(LINE, 1) } + +func testFunctionLiteral() { + a := func(f func()) error { + f() + f() + return nil + } + + b := func(f func()) bool { + f() + f() + return true + } + + check(LINE, 1) + a(func() { + check(LINE, 2) + }) + + if err := a(func() { + check(LINE, 2) + }); err != nil { + } + + switch b(func() { + check(LINE, 2) + }) { + } +} diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 39a88ccab5..7f2f75341f 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -754,7 +754,7 @@ func matchtag(tag string) bool { } return !matchtag(tag[1:]) } - return tag == goos || tag == goarch || tag == "cmd_go_bootstrap" || tag == "go1.1" || (goos == "android" && tag == "linux") + return tag == "gc" || tag == goos || tag == goarch || tag == "cmd_go_bootstrap" || tag == "go1.1" || (goos == "android" && tag == "linux") } // shouldbuild reports whether we should build this file. @@ -798,10 +798,15 @@ func shouldbuild(file, dir string) bool { if p == "" { continue } - if strings.Contains(p, "package documentation") { + code := p + i := strings.Index(code, "//") + if i > 0 { + code = strings.TrimSpace(code[:i]) + } + if code == "package documentation" { return false } - if strings.Contains(p, "package main") && dir != "cmd/go" && dir != "cmd/cgo" { + if code == "package main" && dir != "cmd/go" && dir != "cmd/cgo" { return false } if !strings.HasPrefix(p, "//") { @@ -810,11 +815,11 @@ func shouldbuild(file, dir string) bool { if !strings.Contains(p, "+build") { continue } - fields := splitfields(p) - if len(fields) < 2 || fields[1] != "+build" { + fields := splitfields(p[2:]) + if len(fields) < 1 || fields[0] != "+build" { continue } - for _, p := range fields[2:] { + for _, p := range fields[1:] { if matchfield(p) { goto fieldmatch } diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 156b868109..36c829d1b9 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -947,6 +947,11 @@ func (t *tester) raceTest(dt *distTest) error { t.addCmd(dt, "src", "go", "test", "-race", "-i", "runtime/race", "flag", "os/exec") t.addCmd(dt, "src", "go", "test", "-race", "-run=Output", "runtime/race") t.addCmd(dt, "src", "go", "test", "-race", "-short", "-run=TestParse|TestEcho", "flag", "os/exec") + // We don't want the following line, because it + // slows down all.bash (by 10 seconds on my laptop). + // The race builder should catch any error here, but doesn't. + // TODO(iant): Figure out how to catch this. + // t.addCmd(dt, "src", "go", "test", "-race", "-run=TestParallelTest", "cmd/go") if t.cgoEnabled { env := mergeEnvLists([]string{"GOTRACEBACK=2"}, os.Environ()) cmd := t.addCmd(dt, "misc/cgo/test", "go", "test", "-race", "-short") diff --git a/src/cmd/doc/doc_test.go b/src/cmd/doc/doc_test.go index ed1d0e7c79..20b61702b4 100644 --- a/src/cmd/doc/doc_test.go +++ b/src/cmd/doc/doc_test.go @@ -221,6 +221,7 @@ var tests = []test{ `type ExportedType struct`, // Type definition. `Comment before exported field.*\n.*ExportedField +int` + `.*Comment on line with exported field.`, + `ExportedEmbeddedType.*Comment on line with exported embedded field.`, `Has unexported fields`, `func \(ExportedType\) ExportedMethod\(a int\) bool`, `const ExportedTypedConstant ExportedType = iota`, // Must include associated constant. @@ -228,6 +229,7 @@ var tests = []test{ }, []string{ `unexportedField`, // No unexported field. + `int.*embedded`, // No unexported embedded field. `Comment about exported method.`, // No comment about exported method. `unexportedMethod`, // No unexported method. `unexportedTypedConstant`, // No unexported constant. @@ -241,7 +243,11 @@ var tests = []test{ `Comment about exported type`, // Include comment. `type ExportedType struct`, // Type definition. `Comment before exported field.*\n.*ExportedField +int`, - `unexportedField int.*Comment on line with unexported field.`, + `unexportedField.*int.*Comment on line with unexported field.`, + `ExportedEmbeddedType.*Comment on line with exported embedded field.`, + `\*ExportedEmbeddedType.*Comment on line with exported embedded \*field.`, + `unexportedType.*Comment on line with unexported embedded field.`, + `\*unexportedType.*Comment on line with unexported embedded \*field.`, `func \(ExportedType\) unexportedMethod\(a int\) bool`, `unexportedTypedConstant`, }, @@ -448,7 +454,6 @@ var trimTests = []trimTest{ {"", "", "", true}, {"/usr/gopher", "/usr/gopher", "/usr/gopher", true}, {"/usr/gopher/bar", "/usr/gopher", "bar", true}, - {"/usr/gopher", "/usr/gopher", "/usr/gopher", true}, {"/usr/gopherflakes", "/usr/gopher", "/usr/gopherflakes", false}, {"/usr/gopher/bar", "/usr/zot", "/usr/gopher/bar", false}, } diff --git a/src/cmd/doc/pkg.go b/src/cmd/doc/pkg.go index 0b07f7cc7c..a14ccdb59b 100644 --- a/src/cmd/doc/pkg.go +++ b/src/cmd/doc/pkg.go @@ -487,9 +487,27 @@ func trimUnexportedFields(fields *ast.FieldList, what string) *ast.FieldList { trimmed := false list := make([]*ast.Field, 0, len(fields.List)) for _, field := range fields.List { + names := field.Names + if len(names) == 0 { + // Embedded type. Use the name of the type. It must be of type ident or *ident. + // Nothing else is allowed. + switch ident := field.Type.(type) { + case *ast.Ident: + names = []*ast.Ident{ident} + case *ast.StarExpr: + // Must have the form *identifier. + if ident, ok := ident.X.(*ast.Ident); ok { + names = []*ast.Ident{ident} + } + } + if names == nil { + // Can only happen if AST is incorrect. Safe to continue with a nil list. + log.Print("invalid program: unexpected type for embedded field") + } + } // Trims if any is unexported. Good enough in practice. ok := true - for _, name := range field.Names { + for _, name := range names { if !isExported(name.Name) { trimmed = true ok = false diff --git a/src/cmd/doc/testdata/pkg.go b/src/cmd/doc/testdata/pkg.go index 3e7acee50b..1a673f78d4 100644 --- a/src/cmd/doc/testdata/pkg.go +++ b/src/cmd/doc/testdata/pkg.go @@ -60,8 +60,12 @@ func internalFunc(a int) bool // Comment about exported type. type ExportedType struct { // Comment before exported field. - ExportedField int // Comment on line with exported field. - unexportedField int // Comment on line with unexported field. + ExportedField int // Comment on line with exported field. + unexportedField int // Comment on line with unexported field. + ExportedEmbeddedType // Comment on line with exported embedded field. + *ExportedEmbeddedType // Comment on line with exported embedded *field. + unexportedType // Comment on line with unexported embedded field. + *unexportedType // Comment on line with unexported embedded *field. } // Comment about exported method. diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index c81bd40864..bbad8d40e9 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -1022,12 +1022,6 @@ Vendor directories do not affect the placement of new repositories being checked out for the first time by 'go get': those are always placed in the main GOPATH, never in a vendor subtree. -In Go 1.5, as an experiment, setting the environment variable -GO15VENDOREXPERIMENT=1 enabled these features. -As of Go 1.6 they are on by default. To turn them off, set -GO15VENDOREXPERIMENT=0. In Go 1.7, the environment -variable will stop having any effect. - See https://golang.org/s/go15vendor for details. @@ -1094,8 +1088,6 @@ Special-purpose environment variables: installed in a location other than where it is built. File names in stack traces are rewritten from GOROOT to GOROOT_FINAL. - GO15VENDOREXPERIMENT - Set to 0 to disable vendoring semantics. GO_EXTLINK_ENABLED Whether the linker should use external linking mode when using -linkmode=auto with code that uses cgo. diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index a1f925ed0b..e65aee4a27 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -667,6 +667,7 @@ var ( goarch string goos string exeSuffix string + gopath []string ) func init() { @@ -675,6 +676,7 @@ func init() { if goos == "windows" { exeSuffix = ".exe" } + gopath = filepath.SplitList(buildContext.GOPATH) } // A builder holds global state about a build. @@ -684,6 +686,7 @@ type builder struct { work string // the temporary work directory (ends in filepath.Separator) actionCache map[cacheKey]*action // a cache of already-constructed actions mkdirCache map[string]bool // a cache of created directories + flagCache map[string]bool // a cache of supported compiler flags print func(args ...interface{}) (int, error) output sync.Mutex @@ -1684,6 +1687,22 @@ func (b *builder) includeArgs(flag string, all []*action) []string { inc = append(inc, flag, b.work) // Finally, look in the installed package directories for each action. + // First add the package dirs corresponding to GOPATH entries + // in the original GOPATH order. + need := map[string]*build.Package{} + for _, a1 := range all { + if a1.p != nil && a1.pkgdir == a1.p.build.PkgRoot { + need[a1.p.build.Root] = a1.p.build + } + } + for _, root := range gopath { + if p := need[root]; p != nil && !incMap[p.PkgRoot] { + incMap[p.PkgRoot] = true + inc = append(inc, flag, p.PkgTargetRoot) + } + } + + // Then add anything that's left. for _, a1 := range all { if a1.p == nil { continue @@ -2909,6 +2928,17 @@ func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string { // disable word wrapping in error messages a = append(a, "-fmessage-length=0") + // Tell gcc not to include the work directory in object files. + if b.gccSupportsFlag("-fdebug-prefix-map=a=b") { + a = append(a, "-fdebug-prefix-map="+b.work+"=/tmp/go-build") + } + + // Tell gcc not to include flags in object files, which defeats the + // point of -fdebug-prefix-map above. + if b.gccSupportsFlag("-gno-record-gcc-switches") { + a = append(a, "-gno-record-gcc-switches") + } + // On OS X, some of the compilers behave as if -fno-common // is always set, and the Mach-O linker in 6l/8l assumes this. // See https://golang.org/issue/3253. @@ -2923,19 +2953,24 @@ func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string { // -no-pie must be passed when doing a partial link with -Wl,-r. But -no-pie is // not supported by all compilers. func (b *builder) gccSupportsNoPie() bool { - if goos != "linux" { - // On some BSD platforms, error messages from the - // compiler make it to the console despite cmd.Std* - // all being nil. As -no-pie is only required on linux - // systems so far, we only test there. - return false + return b.gccSupportsFlag("-no-pie") +} + +// gccSupportsFlag checks to see if the compiler supports a flag. +func (b *builder) gccSupportsFlag(flag string) bool { + b.exec.Lock() + defer b.exec.Unlock() + if b, ok := b.flagCache[flag]; ok { + return b } - src := filepath.Join(b.work, "trivial.c") - if err := ioutil.WriteFile(src, []byte{}, 0666); err != nil { - return false + if b.flagCache == nil { + src := filepath.Join(b.work, "trivial.c") + if err := ioutil.WriteFile(src, []byte{}, 0666); err != nil { + return false + } + b.flagCache = make(map[string]bool) } - cmdArgs := b.gccCmd(b.work) - cmdArgs = append(cmdArgs, "-no-pie", "-c", "trivial.c") + cmdArgs := append(envList("CC", defaultCC), flag, "-c", "trivial.c") if buildN || buildX { b.showcmd(b.work, "%s", joinUnambiguously(cmdArgs)) if buildN { @@ -2946,7 +2981,9 @@ func (b *builder) gccSupportsNoPie() bool { cmd.Dir = b.work cmd.Env = envForDir(cmd.Dir, os.Environ()) out, err := cmd.CombinedOutput() - return err == nil && !bytes.Contains(out, []byte("unrecognized")) + supported := err == nil && !bytes.Contains(out, []byte("unrecognized")) + b.flagCache[flag] = supported + return supported } // gccArchArgs returns arguments to pass to gcc based on the architecture. diff --git a/src/cmd/go/env.go b/src/cmd/go/env.go index 24f612756b..8d427b37c2 100644 --- a/src/cmd/go/env.go +++ b/src/cmd/go/env.go @@ -33,11 +33,6 @@ func mkEnv() []envVar { var b builder b.init() - vendorExpValue := "0" - if go15VendorExperiment { - vendorExpValue = "1" - } - env := []envVar{ {"GOARCH", goarch}, {"GOBIN", gobin}, @@ -49,7 +44,6 @@ func mkEnv() []envVar { {"GORACE", os.Getenv("GORACE")}, {"GOROOT", goroot}, {"GOTOOLDIR", toolDir}, - {"GO15VENDOREXPERIMENT", vendorExpValue}, // disable escape codes in clang errors {"TERM", "dumb"}, diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index 6d12f75073..51931769d5 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -10,6 +10,7 @@ import ( "fmt" "go/build" "go/format" + "internal/race" "internal/testenv" "io" "io/ioutil" @@ -69,7 +70,11 @@ func TestMain(m *testing.M) { flag.Parse() if canRun { - out, err := exec.Command("go", "build", "-tags", "testgo", "-o", "testgo"+exeSuffix).CombinedOutput() + args := []string{"build", "-tags", "testgo", "-o", "testgo" + exeSuffix} + if race.Enabled { + args = append(args, "-race") + } + out, err := exec.Command("go", args...).CombinedOutput() if err != nil { fmt.Fprintf(os.Stderr, "building testgo failed: %v\n%s", err, out) os.Exit(2) @@ -1652,8 +1657,8 @@ func TestLdflagsArgumentsWithSpacesIssue3941(t *testing.T) { func main() { println(extern) }`) - tg.run("run", "-ldflags", `-X main.extern "hello world"`, tg.path("main.go")) - tg.grepStderr("^hello world", `ldflags -X main.extern 'hello world' failed`) + tg.run("run", "-ldflags", `-X "main.extern=hello world"`, tg.path("main.go")) + tg.grepStderr("^hello world", `ldflags -X "main.extern=hello world"' failed`) } func TestGoTestCpuprofileLeavesBinaryBehind(t *testing.T) { @@ -1721,7 +1726,6 @@ func TestSymlinksVendor(t *testing.T) { tg := testgo(t) defer tg.cleanup() - tg.setenv("GO15VENDOREXPERIMENT", "1") tg.tempDir("gopath/src/dir1/vendor/v") tg.tempFile("gopath/src/dir1/p.go", "package main\nimport _ `v`\nfunc main(){}") tg.tempFile("gopath/src/dir1/vendor/v/v.go", "package v") @@ -2333,7 +2337,7 @@ func TestGoGetHTTPS404(t *testing.T) { tg.run("get", "bazil.org/fuse/fs/fstestutil") } -// Test that you can not import a main package. +// Test that you cannot import a main package. func TestIssue4210(t *testing.T) { tg := testgo(t) defer tg.cleanup() @@ -2565,6 +2569,59 @@ func TestGoInstallShadowedGOPATH(t *testing.T) { tg.grepStderr("no install location for.*gopath2.src.test: hidden by .*gopath1.src.test", "missing error") } +func TestGoBuildGOPATHOrder(t *testing.T) { + // golang.org/issue/14176#issuecomment-179895769 + // golang.org/issue/14192 + // -I arguments to compiler could end up not in GOPATH order, + // leading to unexpected import resolution in the compiler. + // This is still not a complete fix (see golang.org/issue/14271 and next test) + // but it is clearly OK and enough to fix both of the two reported + // instances of the underlying problem. It will have to do for now. + + tg := testgo(t) + defer tg.cleanup() + tg.makeTempdir() + tg.setenv("GOPATH", tg.path("p1")+string(filepath.ListSeparator)+tg.path("p2")) + + tg.tempFile("p1/src/foo/foo.go", "package foo\n") + tg.tempFile("p2/src/baz/baz.go", "package baz\n") + tg.tempFile("p2/pkg/"+runtime.GOOS+"_"+runtime.GOARCH+"/foo.a", "bad\n") + tg.tempFile("p1/src/bar/bar.go", ` + package bar + import _ "baz" + import _ "foo" + `) + + tg.run("install", "-x", "bar") +} + +func TestGoBuildGOPATHOrderBroken(t *testing.T) { + // This test is known not to work. + // See golang.org/issue/14271. + t.Skip("golang.org/issue/14271") + + tg := testgo(t) + defer tg.cleanup() + tg.makeTempdir() + + tg.tempFile("p1/src/foo/foo.go", "package foo\n") + tg.tempFile("p2/src/baz/baz.go", "package baz\n") + tg.tempFile("p1/pkg/"+runtime.GOOS+"_"+runtime.GOARCH+"/baz.a", "bad\n") + tg.tempFile("p2/pkg/"+runtime.GOOS+"_"+runtime.GOARCH+"/foo.a", "bad\n") + tg.tempFile("p1/src/bar/bar.go", ` + package bar + import _ "baz" + import _ "foo" + `) + + colon := string(filepath.ListSeparator) + tg.setenv("GOPATH", tg.path("p1")+colon+tg.path("p2")) + tg.run("install", "-x", "bar") + + tg.setenv("GOPATH", tg.path("p2")+colon+tg.path("p1")) + tg.run("install", "-x", "bar") +} + func TestIssue11709(t *testing.T) { tg := testgo(t) defer tg.cleanup() @@ -2682,3 +2739,49 @@ func TestIssue13655(t *testing.T) { tg.grepStdout("runtime/internal/sys", "did not find required dependency of "+pkg+" on runtime/internal/sys") } } + +// For issue 14337. +func TestParallelTest(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.makeTempdir() + const testSrc = `package package_test + import ( + "testing" + ) + func TestTest(t *testing.T) { + }` + tg.tempFile("src/p1/p1_test.go", strings.Replace(testSrc, "package_test", "p1_test", 1)) + tg.tempFile("src/p2/p2_test.go", strings.Replace(testSrc, "package_test", "p2_test", 1)) + tg.tempFile("src/p3/p3_test.go", strings.Replace(testSrc, "package_test", "p3_test", 1)) + tg.tempFile("src/p4/p4_test.go", strings.Replace(testSrc, "package_test", "p4_test", 1)) + tg.setenv("GOPATH", tg.path(".")) + tg.run("test", "-p=4", "p1", "p2", "p3", "p4") +} + +func TestCgoConsistentResults(t *testing.T) { + if !canCgo { + t.Skip("skipping because cgo not enabled") + } + + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.makeTempdir() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) + exe1 := tg.path("cgotest1" + exeSuffix) + exe2 := tg.path("cgotest2" + exeSuffix) + tg.run("build", "-o", exe1, "cgotest") + tg.run("build", "-x", "-o", exe2, "cgotest") + b1, err := ioutil.ReadFile(exe1) + tg.must(err) + b2, err := ioutil.ReadFile(exe2) + tg.must(err) + + if !tg.doGrepMatch(`-fdebug-prefix-map=\$WORK`, &tg.stderr) { + t.Skip("skipping because C compiler does not support -fdebug-prefix-map") + } + if !bytes.Equal(b1, b2) { + t.Error("building cgotest twice did not produce the same output") + } +} diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go index d8e7efedb3..de5e5ddeab 100644 --- a/src/cmd/go/help.go +++ b/src/cmd/go/help.go @@ -421,12 +421,6 @@ Vendor directories do not affect the placement of new repositories being checked out for the first time by 'go get': those are always placed in the main GOPATH, never in a vendor subtree. -In Go 1.5, as an experiment, setting the environment variable -GO15VENDOREXPERIMENT=1 enabled these features. -As of Go 1.6 they are on by default. To turn them off, set -GO15VENDOREXPERIMENT=0. In Go 1.7, the environment -variable will stop having any effect. - See https://golang.org/s/go15vendor for details. `, } @@ -497,8 +491,6 @@ Special-purpose environment variables: installed in a location other than where it is built. File names in stack traces are rewritten from GOROOT to GOROOT_FINAL. - GO15VENDOREXPERIMENT - Set to 0 to disable vendoring semantics. GO_EXTLINK_ENABLED Whether the linker should use external linking mode when using -linkmode=auto with code that uses cgo. diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index d384594722..f9b979da7f 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -454,7 +454,9 @@ func envForDir(dir string, base []string) []string { // mergeEnvLists merges the two environment lists such that // variables with the same name in "in" replace those in "out". +// This always returns a newly allocated slice. func mergeEnvLists(in, out []string) []string { + out = append([]string(nil), out...) NextVar: for _, inkv := range in { k := strings.SplitAfterN(inkv, "=", 2)[0] diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index a804ccd277..6b5ead2b8c 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -263,15 +263,6 @@ func reloadPackage(arg string, stk *importStack) *Package { return loadPackage(arg, stk) } -// The Go 1.5 vendoring experiment was enabled by setting GO15VENDOREXPERIMENT=1. -// In Go 1.6 this is on by default and is disabled by setting GO15VENDOREXPERIMENT=0. -// In Go 1.7 the variable will stop having any effect. -// The variable is obnoxiously long so that years from now when people find it in -// their profiles and wonder what it does, there is some chance that a web search -// might answer the question. -// There is a copy of this variable in src/go/build/build.go. Delete that one when this one goes away. -var go15VendorExperiment = os.Getenv("GO15VENDOREXPERIMENT") != "0" - // dirToImportPath returns the pseudo-import path we use for a package // outside the Go path. It begins with _/ and then contains the full path // to the directory. If the package lives in c:\home\gopher\my\pkg then @@ -361,7 +352,7 @@ func loadImport(path, srcDir string, parent *Package, stk *importStack, importPo // TODO: After Go 1, decide when to pass build.AllowBinary here. // See issue 3268 for mistakes to avoid. buildMode := build.ImportComment - if !go15VendorExperiment || mode&useVendor == 0 || path != origPath { + if mode&useVendor == 0 || path != origPath { // Not vendoring, or we already found the vendored path. buildMode |= build.IgnoreVendor } @@ -371,7 +362,7 @@ func loadImport(path, srcDir string, parent *Package, stk *importStack, importPo bp.BinDir = gobin } if err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path && - (!go15VendorExperiment || (!strings.Contains(path, "/vendor/") && !strings.HasPrefix(path, "vendor/"))) { + !strings.Contains(path, "/vendor/") && !strings.HasPrefix(path, "vendor/") { err = fmt.Errorf("code in directory %s expects import %q", bp.Dir, bp.ImportComment) } p.load(stk, bp, err) @@ -412,7 +403,7 @@ func isDir(path string) bool { // x/vendor/path, vendor/path, or else stay path if none of those exist. // vendoredImportPath returns the expanded path or, if no expansion is found, the original. func vendoredImportPath(parent *Package, path string) (found string) { - if parent == nil || parent.Root == "" || !go15VendorExperiment { + if parent == nil || parent.Root == "" { return path } @@ -580,10 +571,6 @@ func findInternal(path string) (index int, ok bool) { // If the import is allowed, disallowVendor returns the original package p. // If not, it returns a new package containing just an appropriate error. func disallowVendor(srcDir, path string, p *Package, stk *importStack) *Package { - if !go15VendorExperiment { - return p - } - // The stack includes p.ImportPath. // If that's the only thing on the stack, we started // with a name given on the command line, not an @@ -967,7 +954,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package } } } - if p.Standard && !p1.Standard && p.Error == nil { + if p.Standard && p.Error == nil && !p1.Standard && p1.Error == nil { p.Error = &PackageError{ ImportStack: stk.copy(), Err: fmt.Sprintf("non-standard import %q in standard package %q", path, p.ImportPath), diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go index 342edee50d..797d91fb96 100644 --- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -383,7 +383,7 @@ func (v *vcsCmd) ping(scheme, repo string) error { // The parent of dir must exist; dir must not. func (v *vcsCmd) create(dir, repo string) error { for _, cmd := range v.createCmd { - if !go15VendorExperiment && strings.Contains(cmd, "submodule") { + if strings.Contains(cmd, "submodule") { continue } if err := v.run(".", cmd, "dir", dir, "repo", repo); err != nil { @@ -396,7 +396,7 @@ func (v *vcsCmd) create(dir, repo string) error { // download downloads any new changes for the repo in dir. func (v *vcsCmd) download(dir string) error { for _, cmd := range v.downloadCmd { - if !go15VendorExperiment && strings.Contains(cmd, "submodule") { + if strings.Contains(cmd, "submodule") { continue } if err := v.run(dir, cmd); err != nil { @@ -445,7 +445,7 @@ func (v *vcsCmd) tagSync(dir, tag string) error { if tag == "" && v.tagSyncDefault != nil { for _, cmd := range v.tagSyncDefault { - if !go15VendorExperiment && strings.Contains(cmd, "submodule") { + if strings.Contains(cmd, "submodule") { continue } if err := v.run(dir, cmd); err != nil { @@ -456,7 +456,7 @@ func (v *vcsCmd) tagSync(dir, tag string) error { } for _, cmd := range v.tagSyncCmd { - if !go15VendorExperiment && strings.Contains(cmd, "submodule") { + if strings.Contains(cmd, "submodule") { continue } if err := v.run(dir, cmd, "tag", tag); err != nil { diff --git a/src/cmd/go/vendor_test.go b/src/cmd/go/vendor_test.go index 006a8c9d3f..40fe309b6d 100644 --- a/src/cmd/go/vendor_test.go +++ b/src/cmd/go/vendor_test.go @@ -20,7 +20,6 @@ func TestVendorImports(t *testing.T) { tg := testgo(t) defer tg.cleanup() tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) - tg.setenv("GO15VENDOREXPERIMENT", "1") tg.run("list", "-f", "{{.ImportPath}} {{.Imports}}", "vend/...") want := ` vend [vend/vendor/p r] @@ -51,7 +50,6 @@ func TestVendorBuild(t *testing.T) { tg := testgo(t) defer tg.cleanup() tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) - tg.setenv("GO15VENDOREXPERIMENT", "1") tg.run("build", "vend/x") } @@ -59,7 +57,6 @@ func TestVendorRun(t *testing.T) { tg := testgo(t) defer tg.cleanup() tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) - tg.setenv("GO15VENDOREXPERIMENT", "1") tg.cd(filepath.Join(tg.pwd(), "testdata/src/vend/hello")) tg.run("run", "hello.go") tg.grepStdout("hello, world", "missing hello world output") @@ -74,7 +71,6 @@ func TestVendorGOPATH(t *testing.T) { } gopath := changeVolume(filepath.Join(tg.pwd(), "testdata"), strings.ToLower) tg.setenv("GOPATH", gopath) - tg.setenv("GO15VENDOREXPERIMENT", "1") cd := changeVolume(filepath.Join(tg.pwd(), "testdata/src/vend/hello"), strings.ToUpper) tg.cd(cd) tg.run("run", "hello.go") @@ -85,7 +81,6 @@ func TestVendorTest(t *testing.T) { tg := testgo(t) defer tg.cleanup() tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) - tg.setenv("GO15VENDOREXPERIMENT", "1") tg.cd(filepath.Join(tg.pwd(), "testdata/src/vend/hello")) tg.run("test", "-v") tg.grepStdout("TestMsgInternal", "missing use in internal test") @@ -96,7 +91,6 @@ func TestVendorInvalid(t *testing.T) { tg := testgo(t) defer tg.cleanup() tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) - tg.setenv("GO15VENDOREXPERIMENT", "1") tg.runFail("build", "vend/x/invalid") tg.grepStderr("must be imported as foo", "missing vendor import error") @@ -106,7 +100,6 @@ func TestVendorImportError(t *testing.T) { tg := testgo(t) defer tg.cleanup() tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) - tg.setenv("GO15VENDOREXPERIMENT", "1") tg.runFail("build", "vend/x/vendor/p/p") @@ -173,7 +166,6 @@ func TestVendorGet(t *testing.T) { package p const C = 1`) tg.setenv("GOPATH", tg.path(".")) - tg.setenv("GO15VENDOREXPERIMENT", "1") tg.cd(tg.path("src/v")) tg.run("run", "m.go") tg.run("test") @@ -192,7 +184,6 @@ func TestVendorGetUpdate(t *testing.T) { defer tg.cleanup() tg.makeTempdir() tg.setenv("GOPATH", tg.path(".")) - tg.setenv("GO15VENDOREXPERIMENT", "1") tg.run("get", "github.com/rsc/go-get-issue-11864") tg.run("get", "-u", "github.com/rsc/go-get-issue-11864") } @@ -204,7 +195,6 @@ func TestGetSubmodules(t *testing.T) { defer tg.cleanup() tg.makeTempdir() tg.setenv("GOPATH", tg.path(".")) - tg.setenv("GO15VENDOREXPERIMENT", "1") tg.run("get", "-d", "github.com/rsc/go-get-issue-12612") tg.run("get", "-u", "-d", "github.com/rsc/go-get-issue-12612") } @@ -213,7 +203,6 @@ func TestVendorCache(t *testing.T) { tg := testgo(t) defer tg.cleanup() tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata/testvendor")) - tg.setenv("GO15VENDOREXPERIMENT", "1") tg.runFail("build", "p") tg.grepStderr("must be imported as x", "did not fail to build p") } @@ -225,7 +214,6 @@ func TestVendorTest2(t *testing.T) { defer tg.cleanup() tg.makeTempdir() tg.setenv("GOPATH", tg.path(".")) - tg.setenv("GO15VENDOREXPERIMENT", "1") tg.run("get", "github.com/rsc/go-get-issue-11864") // build -i should work @@ -251,7 +239,6 @@ func TestVendorList(t *testing.T) { defer tg.cleanup() tg.makeTempdir() tg.setenv("GOPATH", tg.path(".")) - tg.setenv("GO15VENDOREXPERIMENT", "1") tg.run("get", "github.com/rsc/go-get-issue-11864") tg.run("list", "-f", `{{join .TestImports "\n"}}`, "github.com/rsc/go-get-issue-11864/t") @@ -272,7 +259,6 @@ func TestVendor12156(t *testing.T) { tg := testgo(t) defer tg.cleanup() tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata/testvendor2")) - tg.setenv("GO15VENDOREXPERIMENT", "1") tg.cd(filepath.Join(tg.pwd(), "testdata/testvendor2/src/p")) tg.runFail("build", "p.go") tg.grepStderrNot("panic", "panicked") diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index cfebeffe4a..b10b804fd2 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -143,7 +143,9 @@ func visitFile(path string, f os.FileInfo, err error) error { if err == nil && isGoFile(f) { err = processFile(path, nil, os.Stdout, false) } - if err != nil { + // Don't complain if a file was deleted in the meantime (i.e. + // the directory changed concurrently while running gofmt). + if err != nil && !os.IsNotExist(err) { report(err) } return nil diff --git a/src/cmd/internal/obj/arm/obj5.go b/src/cmd/internal/obj/arm/obj5.go index 19a70e177e..1a51dc3b88 100644 --- a/src/cmd/internal/obj/arm/obj5.go +++ b/src/cmd/internal/obj/arm/obj5.go @@ -60,7 +60,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) { // Treat MRC 15, 0, , C13, C0, 3 specially. case AMRC: if p.To.Offset&0xffff0fff == 0xee1d0f70 { - // Because the instruction might be rewriten to a BL which returns in R0 + // Because the instruction might be rewritten to a BL which returns in R0 // the register must be zero. if p.To.Offset&0xf000 != 0 { ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line()) diff --git a/src/cmd/internal/obj/obj.go b/src/cmd/internal/obj/obj.go index e928121938..f38078fca8 100644 --- a/src/cmd/internal/obj/obj.go +++ b/src/cmd/internal/obj/obj.go @@ -290,3 +290,21 @@ func linkgetline(ctxt *Link, lineno int32, f **LSym, l *int32) { func Linkprfile(ctxt *Link, line int) { fmt.Printf("%s ", ctxt.LineHist.LineString(line)) } + +func fieldtrack(ctxt *Link, cursym *LSym) { + p := cursym.Text + if p == nil || p.Link == nil { // handle external functions and ELF section symbols + return + } + ctxt.Cursym = cursym + + for ; p != nil; p = p.Link { + if p.As == AUSEFIELD { + r := Addrel(ctxt.Cursym) + r.Off = 0 + r.Siz = 0 + r.Sym = p.From.Sym + r.Type = R_USEFIELD + } + } +} diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index 8d4a506843..bae64f4a29 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -298,6 +298,7 @@ func Flushplist(ctxt *Link) { ctxt.Arch.Follow(ctxt, s) ctxt.Arch.Preprocess(ctxt, s) ctxt.Arch.Assemble(ctxt, s) + fieldtrack(ctxt, s) linkpcln(ctxt, s) } diff --git a/src/cmd/internal/obj/x86/a.out.go b/src/cmd/internal/obj/x86/a.out.go index ca7b0cfdca..64bd865e42 100644 --- a/src/cmd/internal/obj/x86/a.out.go +++ b/src/cmd/internal/obj/x86/a.out.go @@ -295,8 +295,6 @@ const ( AFMOVX AFMOVXP - AFCOMB - AFCOMBP AFCOMD AFCOMDP AFCOMDPP @@ -626,14 +624,7 @@ const ( APADDUSW APADDW APAND - APANDB - APANDL APANDN - APANDSB - APANDSW - APANDUSB - APANDUSW - APANDW APAVGB APAVGW APCMPEQB @@ -650,23 +641,6 @@ const ( APEXTRD APEXTRQ APEXTRW - APFACC - APFADD - APFCMPEQ - APFCMPGE - APFCMPGT - APFMAX - APFMIN - APFMUL - APFNACC - APFPNACC - APFRCP - APFRCPI2T - APFRCPIT1 - APFRSQIT1 - APFRSQRT - APFSUB - APFSUBR APHADDD APHADDSW APHADDW @@ -697,7 +671,6 @@ const ( APMOVZXWD APMOVZXWQ APMULDQ - APMULHRW APMULHUW APMULHW APMULLD @@ -728,7 +701,6 @@ const ( APSUBUSB APSUBUSW APSUBW - APSWAPL APUNPCKHBW APUNPCKHLQ APUNPCKHQDQ @@ -767,11 +739,6 @@ const ( AUNPCKLPS AXORPD AXORPS - - APF2IW - APF2IL - API2FW - API2FL ARETFW ARETFL ARETFQ diff --git a/src/cmd/internal/obj/x86/anames.go b/src/cmd/internal/obj/x86/anames.go index 1875eae418..3b59e2f36f 100644 --- a/src/cmd/internal/obj/x86/anames.go +++ b/src/cmd/internal/obj/x86/anames.go @@ -255,8 +255,6 @@ var Anames = []string{ "FMOVWP", "FMOVX", "FMOVXP", - "FCOMB", - "FCOMBP", "FCOMD", "FCOMDP", "FCOMDPP", @@ -569,14 +567,7 @@ var Anames = []string{ "PADDUSW", "PADDW", "PAND", - "PANDB", - "PANDL", "PANDN", - "PANDSB", - "PANDSW", - "PANDUSB", - "PANDUSW", - "PANDW", "PAVGB", "PAVGW", "PCMPEQB", @@ -593,23 +584,6 @@ var Anames = []string{ "PEXTRD", "PEXTRQ", "PEXTRW", - "PFACC", - "PFADD", - "PFCMPEQ", - "PFCMPGE", - "PFCMPGT", - "PFMAX", - "PFMIN", - "PFMUL", - "PFNACC", - "PFPNACC", - "PFRCP", - "PFRCPI2T", - "PFRCPIT1", - "PFRSQIT1", - "PFRSQRT", - "PFSUB", - "PFSUBR", "PHADDD", "PHADDSW", "PHADDW", @@ -640,7 +614,6 @@ var Anames = []string{ "PMOVZXWD", "PMOVZXWQ", "PMULDQ", - "PMULHRW", "PMULHUW", "PMULHW", "PMULLD", @@ -671,7 +644,6 @@ var Anames = []string{ "PSUBUSB", "PSUBUSW", "PSUBW", - "PSWAPL", "PUNPCKHBW", "PUNPCKHLQ", "PUNPCKHQDQ", @@ -710,10 +682,6 @@ var Anames = []string{ "UNPCKLPS", "XORPD", "XORPS", - "PF2IW", - "PF2IL", - "PI2FW", - "PI2FL", "RETFW", "RETFL", "RETFQ", diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go index 4577ed7925..9ab6615e43 100644 --- a/src/cmd/internal/obj/x86/asm6.go +++ b/src/cmd/internal/obj/x86/asm6.go @@ -184,7 +184,6 @@ const ( Zm2_r Zm_r_xm Zm_r_i_xm - Zm_r_3d Zm_r_xm_nr Zr_m_xm_nr Zibm_r /* mmx1,mmx2/mem64,imm8 */ @@ -753,10 +752,6 @@ var yxrrl = []ytab{ {Yxr, Ynone, Yrl, Zm_r, 1}, } -var ymfp = []ytab{ - {Ymm, Ynone, Ymr, Zm_r_3d, 1}, -} - var ymrxr = []ytab{ {Ymr, Ynone, Yxr, Zm_r, 1}, {Yxm, Ynone, Yxr, Zm_r_xm, 1}, @@ -1085,7 +1080,6 @@ var optab = {ACVTPD2PS, yxm, Pe, [23]uint8{0x5a}}, {ACVTPS2PL, yxcvm1, Px, [23]uint8{Pe, 0x5b, Pm, 0x2d}}, {ACVTPS2PD, yxm, Pm, [23]uint8{0x5a}}, - {API2FW, ymfp, Px, [23]uint8{0x0c}}, {ACVTSD2SL, yxcvfl, Pf2, [23]uint8{0x2d}}, {ACVTSD2SQ, yxcvfq, Pw, [23]uint8{Pf2, 0x2d}}, {ACVTSD2SS, yxm, Pf2, [23]uint8{0x5a}}, @@ -1303,26 +1297,6 @@ var optab = {APEXTRB, yextr, Pq, [23]uint8{0x3a, 0x14, 00}}, {APEXTRD, yextr, Pq, [23]uint8{0x3a, 0x16, 00}}, {APEXTRQ, yextr, Pq3, [23]uint8{0x3a, 0x16, 00}}, - {APF2IL, ymfp, Px, [23]uint8{0x1d}}, - {APF2IW, ymfp, Px, [23]uint8{0x1c}}, - {API2FL, ymfp, Px, [23]uint8{0x0d}}, - {APFACC, ymfp, Px, [23]uint8{0xae}}, - {APFADD, ymfp, Px, [23]uint8{0x9e}}, - {APFCMPEQ, ymfp, Px, [23]uint8{0xb0}}, - {APFCMPGE, ymfp, Px, [23]uint8{0x90}}, - {APFCMPGT, ymfp, Px, [23]uint8{0xa0}}, - {APFMAX, ymfp, Px, [23]uint8{0xa4}}, - {APFMIN, ymfp, Px, [23]uint8{0x94}}, - {APFMUL, ymfp, Px, [23]uint8{0xb4}}, - {APFNACC, ymfp, Px, [23]uint8{0x8a}}, - {APFPNACC, ymfp, Px, [23]uint8{0x8e}}, - {APFRCP, ymfp, Px, [23]uint8{0x96}}, - {APFRCPIT1, ymfp, Px, [23]uint8{0xa6}}, - {APFRCPI2T, ymfp, Px, [23]uint8{0xb6}}, - {APFRSQIT1, ymfp, Px, [23]uint8{0xa7}}, - {APFRSQRT, ymfp, Px, [23]uint8{0x97}}, - {APFSUB, ymfp, Px, [23]uint8{0x9a}}, - {APFSUBR, ymfp, Px, [23]uint8{0xaa}}, {APHADDD, ymmxmm0f38, Px, [23]uint8{0x0F, 0x38, 0x02, 0, 0x66, 0x0F, 0x38, 0x02, 0}}, {APHADDSW, yxm_q4, Pq4, [23]uint8{0x03}}, {APHADDW, yxm_q4, Pq4, [23]uint8{0x01}}, @@ -1353,7 +1327,6 @@ var optab = {APMOVZXWD, yxm_q4, Pq4, [23]uint8{0x33}}, {APMOVZXWQ, yxm_q4, Pq4, [23]uint8{0x34}}, {APMULDQ, yxm_q4, Pq4, [23]uint8{0x28}}, - {APMULHRW, ymfp, Px, [23]uint8{0xb7}}, {APMULHUW, ymm, Py1, [23]uint8{0xe4, Pe, 0xe4}}, {APMULHW, ymm, Py1, [23]uint8{0xe5, Pe, 0xe5}}, {APMULLD, yxm_q4, Pq4, [23]uint8{0x40}}, @@ -1395,7 +1368,6 @@ var optab = {APSUBUSB, yxm, Pe, [23]uint8{0xd8}}, {APSUBUSW, yxm, Pe, [23]uint8{0xd9}}, {APSUBW, yxm, Pe, [23]uint8{0xf9}}, - {APSWAPL, ymfp, Px, [23]uint8{0xbb}}, {APUNPCKHBW, ymm, Py1, [23]uint8{0x68, Pe, 0x68}}, {APUNPCKHLQ, ymm, Py1, [23]uint8{0x6a, Pe, 0x6a}}, {APUNPCKHQDQ, yxm, Pe, [23]uint8{0x6d}}, @@ -1553,8 +1525,6 @@ var optab = {AFCMOVNE, yfcmv, Px, [23]uint8{0xdb, 01}}, {AFCMOVNU, yfcmv, Px, [23]uint8{0xdb, 03}}, {AFCMOVUN, yfcmv, Px, [23]uint8{0xda, 03}}, - {AFCOMB, nil, 0, [23]uint8{}}, - {AFCOMBP, nil, 0, [23]uint8{}}, {AFCOMD, yfadd, Px, [23]uint8{0xdc, 02, 0xd8, 02, 0xdc, 02}}, /* botch */ {AFCOMDP, yfadd, Px, [23]uint8{0xdc, 03, 0xd8, 03, 0xdc, 03}}, /* botch */ {AFCOMDPP, ycompp, Px, [23]uint8{0xde, 03}}, @@ -3556,15 +3526,6 @@ func doasm(ctxt *obj.Link, p *obj.Prog) { ctxt.Andptr[0] = byte(p.To.Offset) ctxt.Andptr = ctxt.Andptr[1:] - case Zm_r_3d: - ctxt.Andptr[0] = 0x0f - ctxt.Andptr = ctxt.Andptr[1:] - ctxt.Andptr[0] = 0x0f - ctxt.Andptr = ctxt.Andptr[1:] - asmand(ctxt, p, &p.From, &p.To) - ctxt.Andptr[0] = byte(op) - ctxt.Andptr = ctxt.Andptr[1:] - case Zibm_r, Zibr_m: for { tmp1 := z @@ -4618,15 +4579,6 @@ func asmins(ctxt *obj.Link, p *obj.Prog) { ctxt.Andptr = ctxt.And[:] ctxt.Asmode = int(p.Mode) - if p.As == obj.AUSEFIELD { - r := obj.Addrel(ctxt.Cursym) - r.Off = 0 - r.Siz = 0 - r.Sym = p.From.Sym - r.Type = obj.R_USEFIELD - return - } - if ctxt.Headtype == obj.Hnacl && p.Mode == 32 { switch p.As { case obj.ARET: diff --git a/src/cmd/internal/objfile/disasm.go b/src/cmd/internal/objfile/disasm.go index 6495dfb356..f038883dc0 100644 --- a/src/cmd/internal/objfile/disasm.go +++ b/src/cmd/internal/objfile/disasm.go @@ -15,8 +15,8 @@ import ( "strings" "text/tabwriter" - "golang.org/x/arch/arm/armasm" - "golang.org/x/arch/x86/x86asm" + "cmd/internal/unvendor/golang.org/x/arch/arm/armasm" + "cmd/internal/unvendor/golang.org/x/arch/x86/x86asm" ) // Disasm is a disassembler for a given File. diff --git a/src/cmd/vendor/golang.org/x/arch/arm/armasm/Makefile b/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/Makefile similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/arm/armasm/Makefile rename to src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/Makefile diff --git a/src/cmd/vendor/golang.org/x/arch/arm/armasm/decode.go b/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/decode.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/arm/armasm/decode.go rename to src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/decode.go diff --git a/src/cmd/vendor/golang.org/x/arch/arm/armasm/decode_test.go b/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/decode_test.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/arm/armasm/decode_test.go rename to src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/decode_test.go diff --git a/src/cmd/vendor/golang.org/x/arch/arm/armasm/ext_test.go b/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/ext_test.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/arm/armasm/ext_test.go rename to src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/ext_test.go diff --git a/src/cmd/vendor/golang.org/x/arch/arm/armasm/gnu.go b/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/gnu.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/arm/armasm/gnu.go rename to src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/gnu.go diff --git a/src/cmd/vendor/golang.org/x/arch/arm/armasm/inst.go b/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/inst.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/arm/armasm/inst.go rename to src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/inst.go diff --git a/src/cmd/vendor/golang.org/x/arch/arm/armasm/objdump_test.go b/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/objdump_test.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/arm/armasm/objdump_test.go rename to src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/objdump_test.go diff --git a/src/cmd/vendor/golang.org/x/arch/arm/armasm/objdumpext_test.go b/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/objdumpext_test.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/arm/armasm/objdumpext_test.go rename to src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/objdumpext_test.go diff --git a/src/cmd/vendor/golang.org/x/arch/arm/armasm/plan9x.go b/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/plan9x.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/arm/armasm/plan9x.go rename to src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/plan9x.go diff --git a/src/cmd/vendor/golang.org/x/arch/arm/armasm/tables.go b/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/tables.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/arm/armasm/tables.go rename to src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/tables.go diff --git a/src/cmd/vendor/golang.org/x/arch/arm/armasm/testdata/Makefile b/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/testdata/Makefile similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/arm/armasm/testdata/Makefile rename to src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/testdata/Makefile diff --git a/src/cmd/vendor/golang.org/x/arch/arm/armasm/testdata/decode.txt b/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/testdata/decode.txt similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/arm/armasm/testdata/decode.txt rename to src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/testdata/decode.txt diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/Makefile b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/Makefile similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/Makefile rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/Makefile diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/decode.go b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/decode.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/decode.go rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/decode.go diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/decode_test.go b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/decode_test.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/decode_test.go rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/decode_test.go diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/ext_test.go b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/ext_test.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/ext_test.go rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/ext_test.go diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/gnu.go b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/gnu.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/gnu.go rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/gnu.go diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/inst.go b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/inst.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/inst.go rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/inst.go diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/inst_test.go b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/inst_test.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/inst_test.go rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/inst_test.go diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/intel.go b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/intel.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/intel.go rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/intel.go diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/objdump_test.go b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/objdump_test.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/objdump_test.go rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/objdump_test.go diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/objdumpext_test.go b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/objdumpext_test.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/objdumpext_test.go rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/objdumpext_test.go diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/plan9ext_test.go b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/plan9ext_test.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/plan9ext_test.go rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/plan9ext_test.go diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/plan9x.go b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/plan9x.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/plan9x.go rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/plan9x.go diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/plan9x_test.go b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/plan9x_test.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/plan9x_test.go rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/plan9x_test.go diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/tables.go b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/tables.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/tables.go rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/tables.go diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/testdata/Makefile b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/testdata/Makefile similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/testdata/Makefile rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/testdata/Makefile diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/testdata/decode.txt b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/testdata/decode.txt similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/testdata/decode.txt rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/testdata/decode.txt diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/testdata/libmach8db.c b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/testdata/libmach8db.c similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/testdata/libmach8db.c rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/testdata/libmach8db.c diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/xed_test.go b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/xed_test.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/xed_test.go rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/xed_test.go diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/xedext_test.go b/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/xedext_test.go similarity index 100% rename from src/cmd/vendor/golang.org/x/arch/x86/x86asm/xedext_test.go rename to src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/xedext_test.go diff --git a/src/cmd/vendor/vendor.json b/src/cmd/internal/unvendor/vendor.json similarity index 100% rename from src/cmd/vendor/vendor.json rename to src/cmd/internal/unvendor/vendor.json diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index 563600d9a2..fb3d8fb2cd 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -951,7 +951,7 @@ func defgotype(gotype *LSym) *DWDie { } if !strings.HasPrefix(gotype.Name, "type.") { - Diag("dwarf: type name doesn't start with \".type\": %s", gotype.Name) + Diag("dwarf: type name doesn't start with \"type.\": %s", gotype.Name) return mustFind(&dwtypes, "") } @@ -1946,7 +1946,9 @@ func writepub(ispub func(*DWDie) bool) int64 { */ func writearanges() int64 { sectionstart := Cpos() - headersize := int(Rnd(4+2+4+1+1, int64(Thearch.Ptrsize))) // don't count unit_length field itself + // The first tuple is aligned to a multiple of the size of a single tuple + // (twice the size of an address) + headersize := int(Rnd(4+2+4+1+1, int64(Thearch.Ptrsize*2))) // don't count unit_length field itself for compunit := dwroot.child; compunit != nil; compunit = compunit.link { b := getattr(compunit, DW_AT_low_pc) diff --git a/src/cmd/link/internal/ld/pobj.go b/src/cmd/link/internal/ld/pobj.go index 808d377f8a..9ec14c24ed 100644 --- a/src/cmd/link/internal/ld/pobj.go +++ b/src/cmd/link/internal/ld/pobj.go @@ -119,33 +119,6 @@ func Ldmain() { obj.Flagstr("memprofile", "write memory profile to `file`", &memprofile) obj.Flagint64("memprofilerate", "set runtime.MemProfileRate to `rate`", &memprofilerate) - // Clumsy hack to preserve old two-argument -X name val syntax for old scripts. - // Rewrite that syntax into new syntax -X name=val. - // TODO(rsc): Delete this hack in Go 1.6 or later. - var args []string - for i := 0; i < len(os.Args); i++ { - arg := os.Args[i] - if (arg == "-X" || arg == "--X") && i+2 < len(os.Args) && !strings.Contains(os.Args[i+1], "=") { - fmt.Fprintf(os.Stderr, "link: warning: option %s %s %s may not work in future releases; use %s %s=%s\n", - arg, os.Args[i+1], os.Args[i+2], - arg, os.Args[i+1], os.Args[i+2]) - args = append(args, arg) - args = append(args, os.Args[i+1]+"="+os.Args[i+2]) - i += 2 - continue - } - if (strings.HasPrefix(arg, "-X=") || strings.HasPrefix(arg, "--X=")) && i+1 < len(os.Args) && strings.Count(arg, "=") == 1 { - fmt.Fprintf(os.Stderr, "link: warning: option %s %s may not work in future releases; use %s=%s\n", - arg, os.Args[i+1], - arg, os.Args[i+1]) - args = append(args, arg+"="+os.Args[i+1]) - i++ - continue - } - args = append(args, arg) - } - os.Args = args - obj.Flagparse(usage) startProfile() diff --git a/src/cmd/link/internal/ld/sym.go b/src/cmd/link/internal/ld/sym.go index 731f3ede94..6122b85298 100644 --- a/src/cmd/link/internal/ld/sym.go +++ b/src/cmd/link/internal/ld/sym.go @@ -34,8 +34,6 @@ package ld import ( "cmd/internal/obj" "log" - "os" - "path/filepath" "strconv" ) @@ -70,13 +68,6 @@ func linknew(arch *LinkArch) *Link { log.Fatalf("invalid goarch %s (want %s)", p, arch.Name) } - var buf string - buf, _ = os.Getwd() - if buf == "" { - buf = "/???" - } - buf = filepath.ToSlash(buf) - ctxt.Headtype = headtype(obj.Getgoos()) if ctxt.Headtype < 0 { log.Fatalf("unknown goos %s", obj.Getgoos()) diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index 3e6169e453..b87ca81007 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -215,6 +215,11 @@ func Asmelfsym() { dwarfaddelfsectionsyms() + // Some linkers will add a FILE sym if one is not present. + // Avoid having the working directory inserted into the symbol table. + putelfsyment(0, 0, 0, STB_LOCAL<<4|STT_FILE, SHN_ABS, 0) + numelfsym++ + elfbind = STB_LOCAL genasmsym(putelfsym) diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go index ee022b803a..dc57a6335a 100644 --- a/src/crypto/rsa/rsa.go +++ b/src/crypto/rsa/rsa.go @@ -14,7 +14,7 @@ // possible. // // Two sets of interfaces are included in this package. When a more abstract -// interface isn't neccessary, there are functions for encrypting/decrypting +// interface isn't necessary, there are functions for encrypting/decrypting // with v1.5/OAEP and signing/verifying with v1.5/PSS. If one needs to abstract // over the public-key primitive, the PrivateKey struct implements the // Decrypter and Signer interfaces from the crypto package. diff --git a/src/crypto/tls/cipher_suites.go b/src/crypto/tls/cipher_suites.go index 869ffa50bd..224ed1bc86 100644 --- a/src/crypto/tls/cipher_suites.go +++ b/src/crypto/tls/cipher_suites.go @@ -74,7 +74,7 @@ type cipherSuite struct { var cipherSuites = []*cipherSuite{ // Ciphersuite order is chosen so that ECDHE comes before plain RSA - // and RC4 comes before AES (because of the Lucky13 attack). + // and RC4 comes before AES-CBC (because of the Lucky13 attack). {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM}, {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM}, {TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go index d8e7cb77af..28c36160b5 100644 --- a/src/database/sql/sql.go +++ b/src/database/sql/sql.go @@ -199,7 +199,7 @@ type Scanner interface { // time.Time // nil - for NULL values // - // An error should be returned if the value can not be stored + // An error should be returned if the value cannot be stored // without loss of information. Scan(src interface{}) error } diff --git a/src/encoding/csv/writer.go b/src/encoding/csv/writer.go index 353d91f238..a6056285b4 100644 --- a/src/encoding/csv/writer.go +++ b/src/encoding/csv/writer.go @@ -130,7 +130,7 @@ func (w *Writer) fieldNeedsQuotes(field string) bool { if field == "" { return false } - if field == `\.` || strings.IndexRune(field, w.Comma) >= 0 || strings.IndexAny(field, "\"\r\n") >= 0 { + if field == `\.` || strings.ContainsRune(field, w.Comma) || strings.ContainsAny(field, "\"\r\n") { return true } diff --git a/src/encoding/gob/codec_test.go b/src/encoding/gob/codec_test.go index 8efcdc78ff..b772171f93 100644 --- a/src/encoding/gob/codec_test.go +++ b/src/encoding/gob/codec_test.go @@ -970,7 +970,7 @@ func TestBadRecursiveType(t *testing.T) { err := NewEncoder(b).Encode(&rec) if err == nil { t.Error("expected error; got none") - } else if strings.Index(err.Error(), "recursive") < 0 { + } else if !strings.Contains(err.Error(), "recursive") { t.Error("expected recursive type error; got", err) } // Can't test decode easily because we can't encode one, so we can't pass one to a Decoder. diff --git a/src/encoding/gob/encoder_test.go b/src/encoding/gob/encoder_test.go index 570d79696b..811dd2b18c 100644 --- a/src/encoding/gob/encoder_test.go +++ b/src/encoding/gob/encoder_test.go @@ -280,7 +280,7 @@ func TestValueError(t *testing.T) { } t4p := &Type4{3} var t4 Type4 // note: not a pointer. - if err := encAndDec(t4p, t4); err == nil || strings.Index(err.Error(), "pointer") < 0 { + if err := encAndDec(t4p, t4); err == nil || !strings.Contains(err.Error(), "pointer") { t.Error("expected error about pointer; got", err) } } @@ -388,7 +388,7 @@ func TestSingletons(t *testing.T) { t.Errorf("expected error decoding %v: %s", test.in, test.err) continue case err != nil && test.err != "": - if strings.Index(err.Error(), test.err) < 0 { + if !strings.Contains(err.Error(), test.err) { t.Errorf("wrong error decoding %v: wanted %s, got %v", test.in, test.err, err) } continue @@ -414,7 +414,7 @@ func TestStructNonStruct(t *testing.T) { var ns NonStruct if err := encAndDec(s, &ns); err == nil { t.Error("should get error for struct/non-struct") - } else if strings.Index(err.Error(), "type") < 0 { + } else if !strings.Contains(err.Error(), "type") { t.Error("for struct/non-struct expected type error; got", err) } // Now try the other way @@ -424,7 +424,7 @@ func TestStructNonStruct(t *testing.T) { } if err := encAndDec(ns, &s); err == nil { t.Error("should get error for non-struct/struct") - } else if strings.Index(err.Error(), "type") < 0 { + } else if !strings.Contains(err.Error(), "type") { t.Error("for non-struct/struct expected type error; got", err) } } diff --git a/src/encoding/gob/gobencdec_test.go b/src/encoding/gob/gobencdec_test.go index eb76b481d1..d674f0c784 100644 --- a/src/encoding/gob/gobencdec_test.go +++ b/src/encoding/gob/gobencdec_test.go @@ -548,7 +548,7 @@ func TestGobEncoderFieldTypeError(t *testing.T) { if err == nil { t.Fatal("expected decode error for mismatched fields (encoder to non-decoder)") } - if strings.Index(err.Error(), "type") < 0 { + if !strings.Contains(err.Error(), "type") { t.Fatal("expected type error; got", err) } // Non-encoder to GobDecoder: error @@ -562,7 +562,7 @@ func TestGobEncoderFieldTypeError(t *testing.T) { if err == nil { t.Fatal("expected decode error for mismatched fields (non-encoder to decoder)") } - if strings.Index(err.Error(), "type") < 0 { + if !strings.Contains(err.Error(), "type") { t.Fatal("expected type error; got", err) } } diff --git a/src/encoding/hex/hex.go b/src/encoding/hex/hex.go index d1fc7024a9..a51b1db61c 100644 --- a/src/encoding/hex/hex.go +++ b/src/encoding/hex/hex.go @@ -105,7 +105,7 @@ func Dump(data []byte) string { dumper := Dumper(&buf) dumper.Write(data) dumper.Close() - return string(buf.Bytes()) + return buf.String() } // Dumper returns a WriteCloser that writes a hex dump of all written data to diff --git a/src/encoding/xml/marshal.go b/src/encoding/xml/marshal.go index 8ebd693030..9fcd5d7695 100644 --- a/src/encoding/xml/marshal.go +++ b/src/encoding/xml/marshal.go @@ -850,7 +850,7 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error { switch k { case reflect.String: s := vf.String() - dashDash = strings.Index(s, "--") >= 0 + dashDash = strings.Contains(s, "--") dashLast = s[len(s)-1] == '-' if !dashDash { p.WriteString(s) diff --git a/src/fmt/fmt_test.go b/src/fmt/fmt_test.go index 793f709a79..1d9d015f4a 100644 --- a/src/fmt/fmt_test.go +++ b/src/fmt/fmt_test.go @@ -259,6 +259,12 @@ var fmtTests = []struct { {"%+.3F", float32(-1.0), "-1.000"}, {"%+07.2f", 1.0, "+001.00"}, {"%+07.2f", -1.0, "-001.00"}, + {"%-07.2f", 1.0, "1.00 "}, + {"%-07.2f", -1.0, "-1.00 "}, + {"%+-07.2f", 1.0, "+1.00 "}, + {"%+-07.2f", -1.0, "-1.00 "}, + {"%-+07.2f", 1.0, "+1.00 "}, + {"%-+07.2f", -1.0, "-1.00 "}, {"%+10.2f", +1.0, " +1.00"}, {"%+10.2f", -1.0, " -1.00"}, {"% .3E", -1.0, "-1.000E+00"}, @@ -380,6 +386,9 @@ var fmtTests = []struct { {"%20e", math.Inf(1), " +Inf"}, {"%-20f", math.Inf(-1), "-Inf "}, {"%20g", math.NaN(), " NaN"}, + {"%+20f", math.NaN(), " +NaN"}, + {"% -20f", math.NaN(), " NaN "}, + {"%+-20f", math.NaN(), "+NaN "}, // arrays {"%v", array, "[1 2 3 4 5]"}, @@ -648,13 +657,19 @@ var fmtTests = []struct { // Complex numbers: exhaustively tested in TestComplexFormatting. {"%7.2f", 1 + 2i, "( 1.00 +2.00i)"}, {"%+07.2f", -1 - 2i, "(-001.00-002.00i)"}, - // Zero padding does not apply to infinities. + // Zero padding does not apply to infinities and NaN. {"%020f", math.Inf(-1), " -Inf"}, {"%020f", math.Inf(+1), " +Inf"}, + {"%020f", math.NaN(), " NaN"}, {"% 020f", math.Inf(-1), " -Inf"}, {"% 020f", math.Inf(+1), " Inf"}, + {"% 020f", math.NaN(), " NaN"}, {"%+020f", math.Inf(-1), " -Inf"}, {"%+020f", math.Inf(+1), " +Inf"}, + {"%+020f", math.NaN(), " +NaN"}, + {"%-020f", math.Inf(-1), "-Inf "}, + {"%-020f", math.Inf(+1), "+Inf "}, + {"%-020f", math.NaN(), "NaN "}, {"%20f", -1.0, " -1.000000"}, // Make sure we can handle very large widths. {"%0100f", -1.0, zeroFill("-", 99, "1.000000")}, diff --git a/src/fmt/format.go b/src/fmt/format.go index 517b18f7d4..c811cc6a3d 100644 --- a/src/fmt/format.go +++ b/src/fmt/format.go @@ -5,7 +5,6 @@ package fmt import ( - "math" "strconv" "unicode/utf8" ) @@ -405,42 +404,38 @@ func doPrec(f *fmt, def int) int { // formatFloat formats a float64; it is an efficient equivalent to f.pad(strconv.FormatFloat()...). func (f *fmt) formatFloat(v float64, verb byte, prec, n int) { // Format number, reserving space for leading + sign if needed. - num := strconv.AppendFloat(f.intbuf[0:1], v, verb, prec, n) + num := strconv.AppendFloat(f.intbuf[:1], v, verb, prec, n) if num[1] == '-' || num[1] == '+' { num = num[1:] } else { num[0] = '+' } - // Special handling for infinity, which doesn't look like a number so shouldn't be padded with zeros. - if math.IsInf(v, 0) { - if f.zero { - defer func() { f.zero = true }() - f.zero = false - } - } - // num is now a signed version of the number. - // If we're zero padding, want the sign before the leading zeros. - // Achieve this by writing the sign out and then padding the unsigned number. - if f.zero && f.widPresent && f.wid > len(num) { - if f.space && v >= 0 { - f.buf.WriteByte(' ') // This is what C does: even with zero, f.space means space. - f.wid-- - } else if f.plus || v < 0 { - f.buf.WriteByte(num[0]) - f.wid-- - } - f.pad(num[1:]) - return - } // f.space says to replace a leading + with a space. if f.space && num[0] == '+' { num[0] = ' ' + } + // Special handling for infinities and NaN, + // which don't look like a number so shouldn't be padded with zeros. + if num[1] == 'I' || num[1] == 'N' { + oldZero := f.zero + f.zero = false + // Remove sign before NaN if not asked for. + if num[1] == 'N' && !f.space && !f.plus { + num = num[1:] + } f.pad(num) + f.zero = oldZero return } - // Now we know the sign is attached directly to the number, if present at all. - // We want a sign if asked for, if it's negative, or if it's infinity (+Inf vs. -Inf). - if f.plus || num[0] == '-' || math.IsInf(v, 0) { + // We want a sign if asked for and if the sign is not positive. + if f.plus || num[0] != '+' { + // If we're zero padding we want the sign before the leading zeros. + // Achieve this by writing the sign out and then padding the unsigned number. + if f.zero && f.widPresent && f.wid > len(num) { + f.buf.WriteByte(num[0]) + f.wid-- + num = num[1:] + } f.pad(num) return } diff --git a/src/fmt/scan_test.go b/src/fmt/scan_test.go index 7ac74dcb4b..ce6f08659a 100644 --- a/src/fmt/scan_test.go +++ b/src/fmt/scan_test.go @@ -519,7 +519,7 @@ func testScanfMulti(name string, t *testing.T) { if err != nil { if test.err == "" { t.Errorf("got error scanning (%q, %q): %q", test.format, test.text, err) - } else if strings.Index(err.Error(), test.err) < 0 { + } else if !strings.Contains(err.Error(), test.err) { t.Errorf("got wrong error scanning (%q, %q): %q; expected %q", test.format, test.text, err, test.err) } continue @@ -613,7 +613,7 @@ func TestScanNotPointer(t *testing.T) { _, err := Fscan(r, a) if err == nil { t.Error("expected error scanning non-pointer") - } else if strings.Index(err.Error(), "pointer") < 0 { + } else if !strings.Contains(err.Error(), "pointer") { t.Errorf("expected pointer error scanning non-pointer, got: %s", err) } } @@ -623,7 +623,7 @@ func TestScanlnNoNewline(t *testing.T) { _, err := Sscanln("1 x\n", &a) if err == nil { t.Error("expected error scanning string missing newline") - } else if strings.Index(err.Error(), "newline") < 0 { + } else if !strings.Contains(err.Error(), "newline") { t.Errorf("expected newline error scanning string missing newline, got: %s", err) } } @@ -634,7 +634,7 @@ func TestScanlnWithMiddleNewline(t *testing.T) { _, err := Fscanln(r, &a, &b) if err == nil { t.Error("expected error scanning string with extra newline") - } else if strings.Index(err.Error(), "newline") < 0 { + } else if !strings.Contains(err.Error(), "newline") { t.Errorf("expected newline error scanning string with extra newline, got: %s", err) } } diff --git a/src/go/constant/value.go b/src/go/constant/value.go index 310814df71..1b0938dda4 100644 --- a/src/go/constant/value.go +++ b/src/go/constant/value.go @@ -276,10 +276,10 @@ func smallRat(x *big.Float) bool { // MakeUnknown returns the Unknown value. func MakeUnknown() Value { return unknownVal{} } -// MakeBool returns the Bool value for x. +// MakeBool returns the Bool value for b. func MakeBool(b bool) Value { return boolVal(b) } -// MakeString returns the String value for x. +// MakeString returns the String value for s. func MakeString(s string) Value { return stringVal(s) } // MakeInt64 returns the Int value for x. diff --git a/src/go/doc/comment.go b/src/go/doc/comment.go index f414ca4090..5631539abc 100644 --- a/src/go/doc/comment.go +++ b/src/go/doc/comment.go @@ -225,7 +225,7 @@ func heading(line string) string { } // exclude lines with illegal characters - if strings.IndexAny(line, ",.;:!?+*/=()[]{}_^°&§~%#@<\">\\") >= 0 { + if strings.ContainsAny(line, ",.;:!?+*/=()[]{}_^°&§~%#@<\">\\") { return "" } diff --git a/src/go/internal/gcimporter/bimport.go b/src/go/internal/gcimporter/bimport.go index 68690424a1..ad1c4cd02a 100644 --- a/src/go/internal/gcimporter/bimport.go +++ b/src/go/internal/gcimporter/bimport.go @@ -678,4 +678,8 @@ var predeclared = []types.Type{ // package unsafe types.Typ[types.UnsafePointer], + + // any type, for builtin export data + // TODO(mdempsky): Provide an actual Type value to represent "any"? + types.Typ[types.Invalid], } diff --git a/src/go/internal/gcimporter/gcimporter.go b/src/go/internal/gcimporter/gcimporter.go index 0ef8eb4fc6..052277f4fe 100644 --- a/src/go/internal/gcimporter/gcimporter.go +++ b/src/go/internal/gcimporter/gcimporter.go @@ -31,7 +31,8 @@ var pkgExts = [...]string{".a", ".o"} // FindPkg returns the filename and unique package id for an import // path based on package information provided by build.Import (using -// the build.Default build.Context). +// the build.Default build.Context). A relative srcDir is interpreted +// relative to the current working directory. // If no file was found, an empty filename is returned. // func FindPkg(path, srcDir string) (filename, id string) { @@ -44,6 +45,9 @@ func FindPkg(path, srcDir string) (filename, id string) { default: // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x" // Don't require the source files to be present. + if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282 + srcDir = abs + } bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary) if bp.PkgObj == "" { return @@ -381,7 +385,7 @@ func (p *parser) getPkg(id, name string) *types.Package { if pname := pkg.Name(); pname == "" { pkg.SetName(name) } else if pname != name { - p.errorf("%s package name mismatch: %s (given) vs %s (expected)", pname, name) + p.errorf("%s package name mismatch: %s (given) vs %s (expected)", id, pname, name) } } return pkg diff --git a/src/go/types/eval.go b/src/go/types/eval.go index 7b42ff1a9d..f928ee6923 100644 --- a/src/go/types/eval.go +++ b/src/go/types/eval.go @@ -44,7 +44,7 @@ func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (tv Typ scope = pkg.scope } else { // The package scope extent (position information) may be - // incorrect (files spread accross a wide range of fset + // incorrect (files spread across a wide range of fset // positions) - ignore it and just consider its children // (file scopes). for _, fscope := range pkg.scope.children { diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go index 14148a585b..1536df5bf1 100644 --- a/src/go/types/resolver.go +++ b/src/go/types/resolver.go @@ -483,11 +483,9 @@ func pkgName(path string) string { // (Per the go/build package dependency tests, we cannot import // path/filepath and simply use filepath.Dir.) func dir(path string) string { - if i := strings.LastIndexAny(path, "/\\"); i >= 0 { - path = path[:i] + if i := strings.LastIndexAny(path, `/\`); i > 0 { + return path[:i] } - if path == "" { - path = "." - } - return path + // i <= 0 + return "." } diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go index 09f2585bcf..97e6a69521 100644 --- a/src/go/types/stdlib_test.go +++ b/src/go/types/stdlib_test.go @@ -266,13 +266,16 @@ func walkDirs(t *testing.T, dir string) { } // typecheck package in directory - files, err := pkgFilenames(dir) - if err != nil { - t.Error(err) - return - } - if files != nil { - typecheck(t, dir, files) + // but ignore files directly under $GOROOT/src (might be temporary test files). + if dir != filepath.Join(runtime.GOROOT(), "src") { + files, err := pkgFilenames(dir) + if err != nil { + t.Error(err) + return + } + if files != nil { + typecheck(t, dir, files) + } } // traverse subdirectories, but don't walk into testdata diff --git a/src/go/types/universe.go b/src/go/types/universe.go index 40185c1ad4..cc3bd5a370 100644 --- a/src/go/types/universe.go +++ b/src/go/types/universe.go @@ -196,7 +196,7 @@ func init() { // func def(obj Object) { name := obj.Name() - if strings.Index(name, " ") >= 0 { + if strings.Contains(name, " ") { return // nothing to do } // fix Obj link for named types diff --git a/src/log/syslog/syslog.go b/src/log/syslog/syslog.go index 4bf447626f..0e342242ec 100644 --- a/src/log/syslog/syslog.go +++ b/src/log/syslog/syslog.go @@ -85,7 +85,7 @@ type Writer struct { } // This interface and the separate syslog_unix.go file exist for -// Solaris support as implemented by gccgo. On Solaris you can not +// Solaris support as implemented by gccgo. On Solaris you cannot // simply open a TCP connection to the syslog daemon. The gccgo // sources have a syslog_solaris.go file that implements unixSyslog to // return a type that satisfies this interface and simply calls the C diff --git a/src/math/big/ratconv.go b/src/math/big/ratconv.go index 4566ff4e39..57df124e88 100644 --- a/src/math/big/ratconv.go +++ b/src/math/big/ratconv.go @@ -15,7 +15,7 @@ import ( ) func ratTok(ch rune) bool { - return strings.IndexRune("+-/0123456789.eE", ch) >= 0 + return strings.ContainsRune("+-/0123456789.eE", ch) } // Scan is a support routine for fmt.Scanner. It accepts the formats @@ -25,7 +25,7 @@ func (z *Rat) Scan(s fmt.ScanState, ch rune) error { if err != nil { return err } - if strings.IndexRune("efgEFGv", ch) < 0 { + if !strings.ContainsRune("efgEFGv", ch) { return errors.New("Rat.Scan: invalid verb") } if _, ok := z.SetString(string(tok)); !ok { diff --git a/src/mime/encodedword.go b/src/mime/encodedword.go index db4b5f4510..d219bbd393 100644 --- a/src/mime/encodedword.go +++ b/src/mime/encodedword.go @@ -89,7 +89,7 @@ func (e WordEncoder) bEncode(buf *bytes.Buffer, charset, s string) { var currentLen, last, runeLen int for i := 0; i < len(s); i += runeLen { - // Multi-byte characters must not be split accross encoded-words. + // Multi-byte characters must not be split across encoded-words. // See RFC 2047, section 5.3. _, runeLen = utf8.DecodeRuneInString(s[i:]) @@ -119,7 +119,7 @@ func (e WordEncoder) qEncode(buf *bytes.Buffer, charset, s string) { var currentLen, runeLen int for i := 0; i < len(s); i += runeLen { b := s[i] - // Multi-byte characters must not be split accross encoded-words. + // Multi-byte characters must not be split across encoded-words. // See RFC 2047, section 5.3. var encLen int if b >= ' ' && b <= '~' && b != '=' && b != '?' && b != '_' { diff --git a/src/mime/grammar.go b/src/mime/grammar.go index 31b66e8f03..6a6f71dbd4 100644 --- a/src/mime/grammar.go +++ b/src/mime/grammar.go @@ -11,7 +11,7 @@ import ( // isTSpecial reports whether rune is in 'tspecials' as defined by RFC // 1521 and RFC 2045. func isTSpecial(r rune) bool { - return strings.IndexRune(`()<>@,;:\"/[]?=`, r) != -1 + return strings.ContainsRune(`()<>@,;:\"/[]?=`, r) } // isTokenChar reports whether rune is in 'token' as defined by RFC diff --git a/src/net/dial.go b/src/net/dial.go index 193776fe41..0661c3ecdf 100644 --- a/src/net/dial.go +++ b/src/net/dial.go @@ -6,6 +6,7 @@ package net import ( "errors" + "runtime" "time" ) @@ -225,8 +226,10 @@ func (d *Dialer) Dial(network, address string) (Conn, error) { finalDeadline: finalDeadline, } + // DualStack mode requires that dialTCP support cancelation. This is + // not available on plan9 (golang.org/issue/11225), so we ignore it. var primaries, fallbacks addrList - if d.DualStack && network == "tcp" { + if d.DualStack && network == "tcp" && runtime.GOOS != "plan9" { primaries, fallbacks = addrs.partition(isIPv4) } else { primaries = addrs @@ -236,9 +239,9 @@ func (d *Dialer) Dial(network, address string) (Conn, error) { if len(fallbacks) == 0 { // dialParallel can accept an empty fallbacks list, // but this shortcut avoids the goroutine/channel overhead. - c, err = dialSerial(ctx, primaries, nil) + c, err = dialSerial(ctx, primaries, ctx.Cancel) } else { - c, err = dialParallel(ctx, primaries, fallbacks) + c, err = dialParallel(ctx, primaries, fallbacks, ctx.Cancel) } if d.KeepAlive > 0 && err == nil { @@ -255,10 +258,9 @@ func (d *Dialer) Dial(network, address string) (Conn, error) { // head start. It returns the first established connection and // closes the others. Otherwise it returns an error from the first // primary address. -func dialParallel(ctx *dialContext, primaries, fallbacks addrList) (Conn, error) { - results := make(chan dialResult) // unbuffered, so dialSerialAsync can detect race loss & cleanup +func dialParallel(ctx *dialContext, primaries, fallbacks addrList, userCancel <-chan struct{}) (Conn, error) { + results := make(chan dialResult, 2) cancel := make(chan struct{}) - defer close(cancel) // Spawn the primary racer. go dialSerialAsync(ctx, primaries, nil, cancel, results) @@ -267,28 +269,59 @@ func dialParallel(ctx *dialContext, primaries, fallbacks addrList) (Conn, error) fallbackTimer := time.NewTimer(ctx.fallbackDelay()) go dialSerialAsync(ctx, fallbacks, fallbackTimer, cancel, results) - var primaryErr error - for nracers := 2; nracers > 0; nracers-- { - res := <-results - // If we're still waiting for a connection, then hasten the delay. - // Otherwise, disable the Timer and let cancel take over. - if fallbackTimer.Stop() && res.error != nil { - fallbackTimer.Reset(0) - } - if res.error == nil { - return res.Conn, nil - } - if res.primary { - primaryErr = res.error + // Wait for both racers to succeed or fail. + var primaryResult, fallbackResult dialResult + for !primaryResult.done || !fallbackResult.done { + select { + case <-userCancel: + // Forward an external cancelation request. + if cancel != nil { + close(cancel) + cancel = nil + } + userCancel = nil + case res := <-results: + // Drop the result into its assigned bucket. + if res.primary { + primaryResult = res + } else { + fallbackResult = res + } + // On success, cancel the other racer (if one exists.) + if res.error == nil && cancel != nil { + close(cancel) + cancel = nil + } + // If the fallbackTimer was pending, then either we've canceled the + // fallback because we no longer want it, or we haven't canceled yet + // and therefore want it to wake up immediately. + if fallbackTimer.Stop() && cancel != nil { + fallbackTimer.Reset(0) + } } } - return nil, primaryErr + + // Return, in order of preference: + // 1. The primary connection (but close the other if we got both.) + // 2. The fallback connection. + // 3. The primary error. + if primaryResult.error == nil { + if fallbackResult.error == nil { + fallbackResult.Conn.Close() + } + return primaryResult.Conn, nil + } else if fallbackResult.error == nil { + return fallbackResult.Conn, nil + } else { + return nil, primaryResult.error + } } type dialResult struct { Conn error primary bool + done bool } // dialSerialAsync runs dialSerial after some delay, and returns the @@ -300,19 +333,11 @@ func dialSerialAsync(ctx *dialContext, ras addrList, timer *time.Timer, cancel < select { case <-timer.C: case <-cancel: - return + // dialSerial will immediately return errCanceled in this case. } } c, err := dialSerial(ctx, ras, cancel) - select { - case results <- dialResult{c, err, timer == nil}: - // We won the race. - case <-cancel: - // The other goroutine won the race. - if c != nil { - c.Close() - } - } + results <- dialResult{Conn: c, error: err, primary: timer == nil, done: true} } // dialSerial connects to a list of addresses in sequence, returning @@ -336,11 +361,11 @@ func dialSerial(ctx *dialContext, ras addrList, cancel <-chan struct{}) (Conn, e break } - // dialTCP does not support cancelation (see golang.org/issue/11225), - // so if cancel fires, we'll continue trying to connect until the next - // timeout, or return a spurious connection for the caller to close. + // If this dial is canceled, the implementation is expected to complete + // quickly, but it's still possible that we could return a spurious Conn, + // which the caller must Close. dialer := func(d time.Time) (Conn, error) { - return dialSingle(ctx, ra, d) + return dialSingle(ctx, ra, d, cancel) } c, err := dial(ctx.network, ra, dialer, partialDeadline) if err == nil { @@ -360,7 +385,7 @@ func dialSerial(ctx *dialContext, ras addrList, cancel <-chan struct{}) (Conn, e // dialSingle attempts to establish and returns a single connection to // the destination address. This must be called through the OS-specific // dial function, because some OSes don't implement the deadline feature. -func dialSingle(ctx *dialContext, ra Addr, deadline time.Time) (c Conn, err error) { +func dialSingle(ctx *dialContext, ra Addr, deadline time.Time, cancel <-chan struct{}) (c Conn, err error) { la := ctx.LocalAddr if la != nil && la.Network() != ra.Network() { return nil, &OpError{Op: "dial", Net: ctx.network, Source: la, Addr: ra, Err: errors.New("mismatched local address type " + la.Network())} @@ -368,7 +393,7 @@ func dialSingle(ctx *dialContext, ra Addr, deadline time.Time) (c Conn, err erro switch ra := ra.(type) { case *TCPAddr: la, _ := la.(*TCPAddr) - c, err = testHookDialTCP(ctx.network, la, ra, deadline, ctx.Cancel) + c, err = testHookDialTCP(ctx.network, la, ra, deadline, cancel) case *UDPAddr: la, _ := la.(*UDPAddr) c, err = dialUDP(ctx.network, la, ra, deadline) diff --git a/src/net/dial_test.go b/src/net/dial_test.go index 2311b10824..1df923f14b 100644 --- a/src/net/dial_test.go +++ b/src/net/dial_test.go @@ -176,6 +176,7 @@ func TestDialerDualStackFDLeak(t *testing.T) { t.Skip("both IPv4 and IPv6 are required") } + before := sw.Sockets() origTestHookLookupIP := testHookLookupIP defer func() { testHookLookupIP = origTestHookLookupIP }() testHookLookupIP = lookupLocalhost @@ -195,17 +196,15 @@ func TestDialerDualStackFDLeak(t *testing.T) { if err != nil { t.Fatal(err) } - defer dss.teardown() if err := dss.buildup(handler); err != nil { + dss.teardown() t.Fatal(err) } - before := sw.Sockets() - const T = 100 * time.Millisecond const N = 10 var wg sync.WaitGroup wg.Add(N) - d := &Dialer{DualStack: true, Timeout: T} + d := &Dialer{DualStack: true, Timeout: 100 * time.Millisecond} for i := 0; i < N; i++ { go func() { defer wg.Done() @@ -218,7 +217,7 @@ func TestDialerDualStackFDLeak(t *testing.T) { }() } wg.Wait() - time.Sleep(2 * T) // wait for the dial racers to stop + dss.teardown() after := sw.Sockets() if len(after) != len(before) { t.Errorf("got %d; want %d", len(after), len(before)) @@ -229,9 +228,8 @@ func TestDialerDualStackFDLeak(t *testing.T) { // expected to hang until the timeout elapses. These addresses are reserved // for benchmarking by RFC 6890. const ( - slowDst4 = "192.18.0.254" - slowDst6 = "2001:2::254" - slowTimeout = 1 * time.Second + slowDst4 = "198.18.0.254" + slowDst6 = "2001:2::254" ) // In some environments, the slow IPs may be explicitly unreachable, and fail @@ -240,7 +238,10 @@ const ( func slowDialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time, cancel <-chan struct{}) (*TCPConn, error) { c, err := dialTCP(net, laddr, raddr, deadline, cancel) if ParseIP(slowDst4).Equal(raddr.IP) || ParseIP(slowDst6).Equal(raddr.IP) { - time.Sleep(deadline.Sub(time.Now())) + select { + case <-cancel: + case <-time.After(deadline.Sub(time.Now())): + } } return c, err } @@ -284,6 +285,9 @@ func TestDialParallel(t *testing.T) { if !supportsIPv4 || !supportsIPv6 { t.Skip("both IPv4 and IPv6 are required") } + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; cannot cancel dialTCP, golang.org/issue/11225") + } closedPortDelay, expectClosedPortDelay := dialClosedPort() if closedPortDelay > expectClosedPortDelay { @@ -389,7 +393,6 @@ func TestDialParallel(t *testing.T) { fallbacks := makeAddrs(tt.fallbacks, dss.port) d := Dialer{ FallbackDelay: fallbackDelay, - Timeout: slowTimeout, } ctx := &dialContext{ Dialer: d, @@ -398,7 +401,7 @@ func TestDialParallel(t *testing.T) { finalDeadline: d.deadline(time.Now()), } startTime := time.Now() - c, err := dialParallel(ctx, primaries, fallbacks) + c, err := dialParallel(ctx, primaries, fallbacks, nil) elapsed := time.Now().Sub(startTime) if c != nil { @@ -418,9 +421,27 @@ func TestDialParallel(t *testing.T) { } else if !(elapsed <= expectElapsedMax) { t.Errorf("#%d: got %v; want <= %v", i, elapsed, expectElapsedMax) } + + // Repeat each case, ensuring that it can be canceled quickly. + cancel := make(chan struct{}) + var wg sync.WaitGroup + wg.Add(1) + go func() { + time.Sleep(5 * time.Millisecond) + close(cancel) + wg.Done() + }() + startTime = time.Now() + c, err = dialParallel(ctx, primaries, fallbacks, cancel) + if c != nil { + c.Close() + } + elapsed = time.Now().Sub(startTime) + if elapsed > 100*time.Millisecond { + t.Errorf("#%d (cancel): got %v; want <= 100ms", i, elapsed) + } + wg.Wait() } - // Wait for any slowDst4/slowDst6 connections to timeout. - time.Sleep(slowTimeout * 3 / 2) } func lookupSlowFast(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) { @@ -463,8 +484,6 @@ func TestDialerFallbackDelay(t *testing.T) { {true, 200 * time.Millisecond, 200 * time.Millisecond}, // The default is 300ms. {true, 0, 300 * time.Millisecond}, - // This case is last, in order to wait for hanging slowDst6 connections. - {false, 0, slowTimeout}, } handler := func(dss *dualStackServer, ln Listener) { @@ -488,7 +507,7 @@ func TestDialerFallbackDelay(t *testing.T) { } for i, tt := range testCases { - d := &Dialer{DualStack: tt.dualstack, FallbackDelay: tt.delay, Timeout: slowTimeout} + d := &Dialer{DualStack: tt.dualstack, FallbackDelay: tt.delay} startTime := time.Now() c, err := d.Dial("tcp", JoinHostPort("slow6loopback4", dss.port)) @@ -509,17 +528,58 @@ func TestDialerFallbackDelay(t *testing.T) { } } -func TestDialSerialAsyncSpuriousConnection(t *testing.T) { - if runtime.GOOS == "plan9" { - t.Skip("skipping on plan9; no deadline support, golang.org/issue/11932") +func TestDialParallelSpuriousConnection(t *testing.T) { + if !supportsIPv4 || !supportsIPv6 { + t.Skip("both IPv4 and IPv6 are required") } - ln, err := newLocalListener("tcp") + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; cannot cancel dialTCP, golang.org/issue/11225") + } + + var wg sync.WaitGroup + wg.Add(2) + handler := func(dss *dualStackServer, ln Listener) { + // Accept one connection per address. + c, err := ln.Accept() + if err != nil { + t.Fatal(err) + } + // The client should close itself, without sending data. + c.SetReadDeadline(time.Now().Add(1 * time.Second)) + var b [1]byte + if _, err := c.Read(b[:]); err != io.EOF { + t.Errorf("got %v; want %v", err, io.EOF) + } + c.Close() + wg.Done() + } + dss, err := newDualStackServer([]streamListener{ + {network: "tcp4", address: "127.0.0.1"}, + {network: "tcp6", address: "::1"}, + }) if err != nil { t.Fatal(err) } - defer ln.Close() + defer dss.teardown() + if err := dss.buildup(handler); err != nil { + t.Fatal(err) + } - d := Dialer{} + const fallbackDelay = 100 * time.Millisecond + + origTestHookDialTCP := testHookDialTCP + defer func() { testHookDialTCP = origTestHookDialTCP }() + testHookDialTCP = func(net string, laddr, raddr *TCPAddr, deadline time.Time, cancel <-chan struct{}) (*TCPConn, error) { + // Sleep long enough for Happy Eyeballs to kick in, and inhibit cancelation. + // This forces dialParallel to juggle two successful connections. + time.Sleep(fallbackDelay * 2) + cancel = nil + return dialTCP(net, laddr, raddr, deadline, cancel) + } + + d := Dialer{ + FallbackDelay: fallbackDelay, + } ctx := &dialContext{ Dialer: d, network: "tcp", @@ -527,28 +587,23 @@ func TestDialSerialAsyncSpuriousConnection(t *testing.T) { finalDeadline: d.deadline(time.Now()), } - results := make(chan dialResult) - cancel := make(chan struct{}) + makeAddr := func(ip string) addrList { + addr, err := ResolveTCPAddr("tcp", JoinHostPort(ip, dss.port)) + if err != nil { + t.Fatal(err) + } + return addrList{addr} + } - // Spawn a connection in the background. - go dialSerialAsync(ctx, addrList{ln.Addr()}, nil, cancel, results) - - // Receive it at the server. - c, err := ln.Accept() + // dialParallel returns one connection (and closes the other.) + c, err := dialParallel(ctx, makeAddr("127.0.0.1"), makeAddr("::1"), nil) if err != nil { t.Fatal(err) } - defer c.Close() + c.Close() - // Tell dialSerialAsync that someone else won the race. - close(cancel) - - // The connection should close itself, without sending data. - c.SetReadDeadline(time.Now().Add(1 * time.Second)) - var b [1]byte - if _, err := c.Read(b[:]); err != io.EOF { - t.Errorf("got %v; want %v", err, io.EOF) - } + // The server should've seen both connections. + wg.Wait() } func TestDialerPartialDeadline(t *testing.T) { @@ -677,7 +732,6 @@ func TestDialerDualStack(t *testing.T) { c.Close() } } - time.Sleep(timeout * 3 / 2) // wait for the dial racers to stop } func TestDialerKeepAlive(t *testing.T) { diff --git a/src/net/dnsclient_unix.go b/src/net/dnsclient_unix.go index 17188f0024..736e57322c 100644 --- a/src/net/dnsclient_unix.go +++ b/src/net/dnsclient_unix.go @@ -229,7 +229,6 @@ type resolverConfig struct { // time to recheck resolv.conf. ch chan struct{} // guards lastChecked and modTime lastChecked time.Time // last time resolv.conf was checked - modTime time.Time // time of resolv.conf modification mu sync.RWMutex // protects dnsConfig dnsConfig *dnsConfig // parsed resolv.conf structure used in lookups @@ -239,16 +238,12 @@ var resolvConf resolverConfig // init initializes conf and is only called via conf.initOnce. func (conf *resolverConfig) init() { - // Set dnsConfig, modTime, and lastChecked so we don't parse + // Set dnsConfig and lastChecked so we don't parse // resolv.conf twice the first time. conf.dnsConfig = systemConf().resolv if conf.dnsConfig == nil { conf.dnsConfig = dnsReadConfig("/etc/resolv.conf") } - - if fi, err := os.Stat("/etc/resolv.conf"); err == nil { - conf.modTime = fi.ModTime() - } conf.lastChecked = time.Now() // Prepare ch so that only one update of resolverConfig may @@ -274,17 +269,12 @@ func (conf *resolverConfig) tryUpdate(name string) { } conf.lastChecked = now + var mtime time.Time if fi, err := os.Stat(name); err == nil { - if fi.ModTime().Equal(conf.modTime) { - return - } - conf.modTime = fi.ModTime() - } else { - // If modTime wasn't set prior, assume nothing has changed. - if conf.modTime.IsZero() { - return - } - conf.modTime = time.Time{} + mtime = fi.ModTime() + } + if mtime.Equal(conf.dnsConfig.mtime) { + return } dnsConf := dnsReadConfig(name) diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go index 934f25b2c9..d7f00c784d 100644 --- a/src/net/dnsclient_unix_test.go +++ b/src/net/dnsclient_unix_test.go @@ -124,20 +124,20 @@ func (conf *resolvConfTest) writeAndUpdate(lines []string) error { return err } f.Close() - if err := conf.forceUpdate(conf.path); err != nil { + if err := conf.forceUpdate(conf.path, time.Now().Add(time.Hour)); err != nil { return err } return nil } -func (conf *resolvConfTest) forceUpdate(name string) error { +func (conf *resolvConfTest) forceUpdate(name string, lastChecked time.Time) error { dnsConf := dnsReadConfig(name) conf.mu.Lock() conf.dnsConfig = dnsConf conf.mu.Unlock() for i := 0; i < 5; i++ { if conf.tryAcquireSema() { - conf.lastChecked = time.Time{} + conf.lastChecked = lastChecked conf.releaseSema() return nil } @@ -153,7 +153,7 @@ func (conf *resolvConfTest) servers() []string { } func (conf *resolvConfTest) teardown() error { - err := conf.forceUpdate("/etc/resolv.conf") + err := conf.forceUpdate("/etc/resolv.conf", time.Time{}) os.RemoveAll(conf.dir) return err } @@ -353,7 +353,6 @@ func TestGoLookupIPWithResolverConfig(t *testing.T) { t.Error(err) continue } - conf.tryUpdate(conf.path) addrs, err := goLookupIP(tt.name) if err != nil { if err, ok := err.(*DNSError); !ok || (err.Name != tt.error.(*DNSError).Name || err.Server != tt.error.(*DNSError).Server || err.IsTimeout != tt.error.(*DNSError).IsTimeout) { @@ -392,7 +391,6 @@ func TestGoLookupIPOrderFallbackToFile(t *testing.T) { if err := conf.writeAndUpdate([]string{}); err != nil { t.Fatal(err) } - conf.tryUpdate(conf.path) // Redirect host file lookups. defer func(orig string) { testHookHostsPath = orig }(testHookHostsPath) testHookHostsPath = "testdata/hosts" diff --git a/src/net/dnsconfig_unix.go b/src/net/dnsconfig_unix.go index 6073fdb6d8..0515ca90de 100644 --- a/src/net/dnsconfig_unix.go +++ b/src/net/dnsconfig_unix.go @@ -8,18 +8,21 @@ package net +import "time" + var defaultNS = []string{"127.0.0.1", "::1"} type dnsConfig struct { - servers []string // servers to use - search []string // suffixes to append to local name - ndots int // number of dots in name to trigger absolute lookup - timeout int // seconds before giving up on packet - attempts int // lost packets before giving up on server - rotate bool // round robin among servers - unknownOpt bool // anything unknown was encountered - lookup []string // OpenBSD top-level database "lookup" order - err error // any error that occurs during open of resolv.conf + servers []string // servers to use + search []string // suffixes to append to local name + ndots int // number of dots in name to trigger absolute lookup + timeout int // seconds before giving up on packet + attempts int // lost packets before giving up on server + rotate bool // round robin among servers + unknownOpt bool // anything unknown was encountered + lookup []string // OpenBSD top-level database "lookup" order + err error // any error that occurs during open of resolv.conf + mtime time.Time // time of resolv.conf modification } // See resolv.conf(5) on a Linux machine. @@ -38,6 +41,13 @@ func dnsReadConfig(filename string) *dnsConfig { return conf } defer file.close() + if fi, err := file.file.Stat(); err == nil { + conf.mtime = fi.ModTime() + } else { + conf.servers = defaultNS + conf.err = err + return conf + } for line, ok := file.readLine(); ok; line, ok = file.readLine() { if len(line) > 0 && (line[0] == ';' || line[0] == '#') { // comment. diff --git a/src/net/dnsconfig_unix_test.go b/src/net/dnsconfig_unix_test.go index c8eed61890..849c0da93b 100644 --- a/src/net/dnsconfig_unix_test.go +++ b/src/net/dnsconfig_unix_test.go @@ -10,6 +10,7 @@ import ( "os" "reflect" "testing" + "time" ) var dnsReadConfigTests = []struct { @@ -76,6 +77,7 @@ func TestDNSReadConfig(t *testing.T) { if conf.err != nil { t.Fatal(conf.err) } + conf.mtime = time.Time{} if !reflect.DeepEqual(conf, tt.want) { t.Errorf("%s:\ngot: %+v\nwant: %+v", tt.name, conf, tt.want) } diff --git a/src/net/dnsmsg.go b/src/net/dnsmsg.go index 93078fe849..5747fd232b 100644 --- a/src/net/dnsmsg.go +++ b/src/net/dnsmsg.go @@ -315,7 +315,7 @@ func (rr *dnsRR_TXT) Walk(f func(v interface{}, name, tag string) bool) bool { if !f(&txt, "Txt", "") { return false } - // more bytes than rr.Hdr.Rdlength said there woudld be + // more bytes than rr.Hdr.Rdlength said there would be if rr.Hdr.Rdlength-n < uint16(len(txt))+1 { return false } @@ -406,6 +406,13 @@ func packDomainName(s string, msg []byte, off int) (off1 int, ok bool) { s += "." } + // Allow root domain. + if s == "." { + msg[off] = 0 + off++ + return off, true + } + // Each dot ends a segment of the name. // We trade each dot byte for a length byte. // There is also a trailing zero. @@ -422,8 +429,13 @@ func packDomainName(s string, msg []byte, off int) (off1 int, ok bool) { if i-begin >= 1<<6 { // top two bits of length must be clear return len(msg), false } + if i-begin == 0 { + return len(msg), false + } + msg[off] = byte(i - begin) off++ + for j := begin; j < i; j++ { msg[off] = s[j] off++ @@ -494,6 +506,9 @@ Loop: return "", len(msg), false } } + if len(s) == 0 { + s = "." + } if ptr == 0 { off1 = off } @@ -803,20 +818,32 @@ func (dns *dnsMsg) Pack() (msg []byte, ok bool) { // Pack it in: header and then the pieces. off := 0 off, ok = packStruct(&dh, msg, off) + if !ok { + return nil, false + } for i := 0; i < len(question); i++ { off, ok = packStruct(&question[i], msg, off) + if !ok { + return nil, false + } } for i := 0; i < len(answer); i++ { off, ok = packRR(answer[i], msg, off) + if !ok { + return nil, false + } } for i := 0; i < len(ns); i++ { off, ok = packRR(ns[i], msg, off) + if !ok { + return nil, false + } } for i := 0; i < len(extra); i++ { off, ok = packRR(extra[i], msg, off) - } - if !ok { - return nil, false + if !ok { + return nil, false + } } return msg[0:off], true } @@ -848,6 +875,9 @@ func (dns *dnsMsg) Unpack(msg []byte) bool { for i := 0; i < len(dns.question); i++ { off, ok = unpackStruct(&dns.question[i], msg, off) + if !ok { + return false + } } for i := 0; i < int(dh.Ancount); i++ { rec, off, ok = unpackRR(msg, off) diff --git a/src/net/dnsmsg_test.go b/src/net/dnsmsg_test.go index 1078d77ceb..339fb83c62 100644 --- a/src/net/dnsmsg_test.go +++ b/src/net/dnsmsg_test.go @@ -10,6 +10,103 @@ import ( "testing" ) +func TestStructPackUnpack(t *testing.T) { + want := dnsQuestion{ + Name: ".", + Qtype: dnsTypeA, + Qclass: dnsClassINET, + } + buf := make([]byte, 50) + n, ok := packStruct(&want, buf, 0) + if !ok { + t.Fatal("packing failed") + } + buf = buf[:n] + got := dnsQuestion{} + n, ok = unpackStruct(&got, buf, 0) + if !ok { + t.Fatal("unpacking failed") + } + if n != len(buf) { + t.Error("unpacked different amount than packed: got n = %d, want = %d", n, len(buf)) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("got = %+v, want = %+v", got, want) + } +} + +func TestDomainNamePackUnpack(t *testing.T) { + tests := []struct { + in string + want string + ok bool + }{ + {"", ".", true}, + {".", ".", true}, + {"google..com", "", false}, + {"google.com", "google.com.", true}, + {"google..com.", "", false}, + {"google.com.", "google.com.", true}, + {".google.com.", "", false}, + {"www..google.com.", "", false}, + {"www.google.com.", "www.google.com.", true}, + } + + for _, test := range tests { + buf := make([]byte, 30) + n, ok := packDomainName(test.in, buf, 0) + if ok != test.ok { + t.Errorf("packing of %s: got ok = %t, want = %t", test.in, ok, test.ok) + continue + } + if !test.ok { + continue + } + buf = buf[:n] + got, n, ok := unpackDomainName(buf, 0) + if !ok { + t.Errorf("unpacking for %s failed", test.in) + continue + } + if n != len(buf) { + t.Error( + "unpacked different amount than packed for %s: got n = %d, want = %d", + test.in, + n, + len(buf), + ) + } + if got != test.want { + t.Errorf("unpacking packing of %s: got = %s, want = %s", test.in, got, test.want) + } + } +} + +func TestDNSPackUnpack(t *testing.T) { + want := dnsMsg{ + question: []dnsQuestion{{ + Name: ".", + Qtype: dnsTypeAAAA, + Qclass: dnsClassINET, + }}, + answer: []dnsRR{}, + ns: []dnsRR{}, + extra: []dnsRR{}, + } + b, ok := want.Pack() + if !ok { + t.Fatal("packing failed") + } + var got dnsMsg + ok = got.Unpack(b) + if !ok { + t.Fatal("unpacking failed") + } + if !reflect.DeepEqual(got, want) { + t.Errorf("got = %+v, want = %+v", got, want) + } +} + func TestDNSParseSRVReply(t *testing.T) { data, err := hex.DecodeString(dnsSRVReply) if err != nil { diff --git a/src/net/error_plan9_test.go b/src/net/error_plan9_test.go index 495ea96534..d7c7f1487f 100644 --- a/src/net/error_plan9_test.go +++ b/src/net/error_plan9_test.go @@ -9,6 +9,8 @@ import "syscall" var ( errTimedout = syscall.ETIMEDOUT errOpNotSupported = syscall.EPLAN9 + + abortedConnRequestErrors []error ) func isPlatformError(err error) bool { diff --git a/src/net/error_posix_test.go b/src/net/error_posix_test.go index 981cc837ba..b411a378df 100644 --- a/src/net/error_posix_test.go +++ b/src/net/error_posix_test.go @@ -12,16 +12,6 @@ import ( "testing" ) -var ( - errTimedout = syscall.ETIMEDOUT - errOpNotSupported = syscall.EOPNOTSUPP -) - -func isPlatformError(err error) bool { - _, ok := err.(syscall.Errno) - return ok -} - func TestSpuriousENOTAVAIL(t *testing.T) { for _, tt := range []struct { error diff --git a/src/net/error_unix_test.go b/src/net/error_unix_test.go new file mode 100644 index 0000000000..db66d0acf1 --- /dev/null +++ b/src/net/error_unix_test.go @@ -0,0 +1,21 @@ +// Copyright 2015 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. + +// +build !plan9,!windows + +package net + +import "syscall" + +var ( + errTimedout = syscall.ETIMEDOUT + errOpNotSupported = syscall.EOPNOTSUPP + + abortedConnRequestErrors = []error{syscall.ECONNABORTED} // see accept in fd_unix.go +) + +func isPlatformError(err error) bool { + _, ok := err.(syscall.Errno) + return ok +} diff --git a/src/net/error_windows_test.go b/src/net/error_windows_test.go new file mode 100644 index 0000000000..834a9de441 --- /dev/null +++ b/src/net/error_windows_test.go @@ -0,0 +1,19 @@ +// Copyright 2015 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 net + +import "syscall" + +var ( + errTimedout = syscall.ETIMEDOUT + errOpNotSupported = syscall.EOPNOTSUPP + + abortedConnRequestErrors = []error{syscall.ERROR_NETNAME_DELETED, syscall.WSAECONNRESET} // see accept in fd_windows.go +) + +func isPlatformError(err error) bool { + _, ok := err.(syscall.Errno) + return ok +} diff --git a/src/net/fd_windows.go b/src/net/fd_windows.go index fd50d772d6..abdee9d02c 100644 --- a/src/net/fd_windows.go +++ b/src/net/fd_windows.go @@ -579,7 +579,7 @@ func (fd *netFD) acceptOne(rawsa []syscall.RawSockaddrAny, o *operation) (*netFD o.handle = s o.rsan = int32(unsafe.Sizeof(rawsa[0])) _, err = rsrv.ExecIO(o, "AcceptEx", func(o *operation) error { - return syscall.AcceptEx(o.fd.sysfd, o.handle, (*byte)(unsafe.Pointer(&rawsa[0])), 0, uint32(o.rsan), uint32(o.rsan), &o.qty, &o.o) + return acceptFunc(o.fd.sysfd, o.handle, (*byte)(unsafe.Pointer(&rawsa[0])), 0, uint32(o.rsan), uint32(o.rsan), &o.qty, &o.o) }) if err != nil { netfd.Close() diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go index 126b0ebdd1..63ea35ab8c 100644 --- a/src/net/hook_windows.go +++ b/src/net/hook_windows.go @@ -13,9 +13,10 @@ var ( testHookDialChannel = func() { time.Sleep(time.Millisecond) } // see golang.org/issue/5349 // Placeholders for socket system calls. - socketFunc func(int, int, int) (syscall.Handle, error) = syscall.Socket - closeFunc func(syscall.Handle) error = syscall.Closesocket - connectFunc func(syscall.Handle, syscall.Sockaddr) error = syscall.Connect - connectExFunc func(syscall.Handle, syscall.Sockaddr, *byte, uint32, *uint32, *syscall.Overlapped) error = syscall.ConnectEx - listenFunc func(syscall.Handle, int) error = syscall.Listen + socketFunc func(int, int, int) (syscall.Handle, error) = syscall.Socket + closeFunc func(syscall.Handle) error = syscall.Closesocket + connectFunc func(syscall.Handle, syscall.Sockaddr) error = syscall.Connect + connectExFunc func(syscall.Handle, syscall.Sockaddr, *byte, uint32, *uint32, *syscall.Overlapped) error = syscall.ConnectEx + listenFunc func(syscall.Handle, int) error = syscall.Listen + acceptFunc func(syscall.Handle, syscall.Handle, *byte, uint32, uint32, uint32, *uint32, *syscall.Overlapped) error = syscall.AcceptEx ) diff --git a/src/net/hosts.go b/src/net/hosts.go index c4de1b6a97..9c101c6ef5 100644 --- a/src/net/hosts.go +++ b/src/net/hosts.go @@ -110,7 +110,9 @@ func lookupStaticHost(host string) []string { lowerHost := []byte(host) lowerASCIIBytes(lowerHost) if ips, ok := hosts.byName[absDomainName(lowerHost)]; ok { - return ips + ipsCp := make([]string, len(ips)) + copy(ipsCp, ips) + return ipsCp } } return nil @@ -127,7 +129,9 @@ func lookupStaticAddr(addr string) []string { } if len(hosts.byAddr) != 0 { if hosts, ok := hosts.byAddr[addr]; ok { - return hosts + hostsCp := make([]string, len(hosts)) + copy(hostsCp, hosts) + return hostsCp } } return nil diff --git a/src/net/hosts_test.go b/src/net/hosts_test.go index 4c67bfa982..5d6c9cfe19 100644 --- a/src/net/hosts_test.go +++ b/src/net/hosts_test.go @@ -64,13 +64,17 @@ func TestLookupStaticHost(t *testing.T) { for _, tt := range lookupStaticHostTests { testHookHostsPath = tt.name for _, ent := range tt.ents { - ins := []string{ent.in, absDomainName([]byte(ent.in)), strings.ToLower(ent.in), strings.ToUpper(ent.in)} - for _, in := range ins { - addrs := lookupStaticHost(in) - if !reflect.DeepEqual(addrs, ent.out) { - t.Errorf("%s, lookupStaticHost(%s) = %v; want %v", tt.name, in, addrs, ent.out) - } - } + testStaticHost(t, tt.name, ent) + } + } +} + +func testStaticHost(t *testing.T, hostsPath string, ent staticHostEntry) { + ins := []string{ent.in, absDomainName([]byte(ent.in)), strings.ToLower(ent.in), strings.ToUpper(ent.in)} + for _, in := range ins { + addrs := lookupStaticHost(in) + if !reflect.DeepEqual(addrs, ent.out) { + t.Errorf("%s, lookupStaticHost(%s) = %v; want %v", hostsPath, in, addrs, ent.out) } } } @@ -129,13 +133,43 @@ func TestLookupStaticAddr(t *testing.T) { for _, tt := range lookupStaticAddrTests { testHookHostsPath = tt.name for _, ent := range tt.ents { - hosts := lookupStaticAddr(ent.in) - for i := range ent.out { - ent.out[i] = absDomainName([]byte(ent.out[i])) - } - if !reflect.DeepEqual(hosts, ent.out) { - t.Errorf("%s, lookupStaticAddr(%s) = %v; want %v", tt.name, ent.in, hosts, ent.out) - } + testStaticAddr(t, tt.name, ent) } } } + +func testStaticAddr(t *testing.T, hostsPath string, ent staticHostEntry) { + hosts := lookupStaticAddr(ent.in) + for i := range ent.out { + ent.out[i] = absDomainName([]byte(ent.out[i])) + } + if !reflect.DeepEqual(hosts, ent.out) { + t.Errorf("%s, lookupStaticAddr(%s) = %v; want %v", hostsPath, ent.in, hosts, ent.out) + } +} + +func TestHostCacheModification(t *testing.T) { + // Ensure that programs can't modify the internals of the host cache. + // See https://github.com/golang/go/issues/14212. + defer func(orig string) { testHookHostsPath = orig }(testHookHostsPath) + + testHookHostsPath = "testdata/ipv4-hosts" + ent := staticHostEntry{"localhost", []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}} + testStaticHost(t, testHookHostsPath, ent) + // Modify the addresses return by lookupStaticHost. + addrs := lookupStaticHost(ent.in) + for i := range addrs { + addrs[i] += "junk" + } + testStaticHost(t, testHookHostsPath, ent) + + testHookHostsPath = "testdata/ipv6-hosts" + ent = staticHostEntry{"::1", []string{"localhost"}} + testStaticAddr(t, testHookHostsPath, ent) + // Modify the hosts return by lookupStaticAddr. + hosts := lookupStaticAddr(ent.in) + for i := range hosts { + hosts[i] += "junk" + } + testStaticAddr(t, testHookHostsPath, ent) +} diff --git a/src/net/http/fs.go b/src/net/http/fs.go index f61c138c1d..520a5477a7 100644 --- a/src/net/http/fs.go +++ b/src/net/http/fs.go @@ -34,7 +34,7 @@ import ( type Dir string func (d Dir) Open(name string) (File, error) { - if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 || + if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) || strings.Contains(name, "\x00") { return nil, errors.New("http: invalid character in file path") } @@ -451,7 +451,7 @@ func localRedirect(w ResponseWriter, r *Request, newPath string) { // ServeFile replies to the request with the contents of the named // file or directory. // -// If the provided file or direcory name is a relative path, it is +// If the provided file or directory name is a relative path, it is // interpreted relative to the current directory and may ascend to parent // directories. If the provided name is constructed from user input, it // should be sanitized before calling ServeFile. As a precaution, ServeFile diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go index c4faccc7a8..4e19b3e71f 100644 --- a/src/net/http/h2_bundle.go +++ b/src/net/http/h2_bundle.go @@ -1,5 +1,5 @@ -// Code generated by golang.org/x/tools/cmd/bundle command: -// $ bundle golang.org/x/net/http2 net/http http2 +// Code generated by golang.org/x/tools/cmd/bundle. +//go:generate bundle -o h2_bundle.go -prefix http2 -import golang.org/x/net/http2/hpack=internal/golang.org/x/net/http2/hpack golang.org/x/net/http2 // Package http2 implements the HTTP/2 protocol. // @@ -6141,13 +6141,18 @@ func (rt http2erringRoundTripper) RoundTrip(*Request) (*Response, error) { retur // call gzip.NewReader on the first call to Read type http2gzipReader struct { body io.ReadCloser // underlying Response.Body - zr io.Reader // lazily-initialized gzip reader + zr *gzip.Reader // lazily-initialized gzip reader + zerr error // sticky error } func (gz *http2gzipReader) Read(p []byte) (n int, err error) { + if gz.zerr != nil { + return 0, gz.zerr + } if gz.zr == nil { gz.zr, err = gzip.NewReader(gz.body) if err != nil { + gz.zerr = err return 0, err } } diff --git a/src/net/http/httptest/server.go b/src/net/http/httptest/server.go index fabfeca943..7e52adb607 100644 --- a/src/net/http/httptest/server.go +++ b/src/net/http/httptest/server.go @@ -167,7 +167,7 @@ func (s *Server) Close() { // few milliseconds wasn't liked (early versions of // https://golang.org/cl/15151) so now we just // forcefully close StateNew. The docs for Server.Close say - // we wait for "oustanding requests", so we don't close things + // we wait for "outstanding requests", so we don't close things // in StateActive. if st == http.StateIdle || st == http.StateNew { s.closeConn(c) @@ -202,10 +202,31 @@ func (s *Server) logCloseHangDebugInfo() { // CloseClientConnections closes any open HTTP connections to the test Server. func (s *Server) CloseClientConnections() { + var conns int + ch := make(chan bool) + s.mu.Lock() - defer s.mu.Unlock() for c := range s.conns { - s.closeConn(c) + conns++ + s.closeConnChan(c, ch) + } + s.mu.Unlock() + + // Wait for outstanding closes to finish. + // + // Out of paranoia for making a late change in Go 1.6, we + // bound how long this can wait, since golang.org/issue/14291 + // isn't fully understood yet. At least this should only be used + // in tests. + timer := time.NewTimer(5 * time.Second) + defer timer.Stop() + for i := 0; i < conns; i++ { + select { + case <-ch: + case <-timer.C: + // Too slow. Give up. + return + } } } @@ -267,9 +288,13 @@ func (s *Server) wrap() { } } -// closeConn closes c. Except on plan9, which is special. See comment below. +// closeConn closes c. // s.mu must be held. -func (s *Server) closeConn(c net.Conn) { +func (s *Server) closeConn(c net.Conn) { s.closeConnChan(c, nil) } + +// closeConnChan is like closeConn, but takes an optional channel to receive a value +// when the goroutine closing c is done. +func (s *Server) closeConnChan(c net.Conn, done chan<- bool) { if runtime.GOOS == "plan9" { // Go's Plan 9 net package isn't great at unblocking reads when // their underlying TCP connections are closed. Don't trust @@ -278,7 +303,21 @@ func (s *Server) closeConn(c net.Conn) { // resources if the syscall doesn't end up returning. Oh well. s.forgetConn(c) } - go c.Close() + + // Somewhere in the chaos of https://golang.org/cl/15151 we found that + // some types of conns were blocking in Close too long (or deadlocking?) + // and we had to call Close in a goroutine. I (bradfitz) forget what + // that was at this point, but I suspect it was *tls.Conns, which + // were later fixed in https://golang.org/cl/18572, so this goroutine + // is _probably_ unnecessary now. But it's too late in Go 1.6 too remove + // it with confidence. + // TODO(bradfitz): try to remove it for Go 1.7. (golang.org/issue/14291) + go func() { + c.Close() + if done != nil { + done <- true + } + }() } // forgetConn removes c from the set of tracked conns and decrements it from the diff --git a/src/net/http/httptest/server_test.go b/src/net/http/httptest/server_test.go index 6ffc671e57..c9606f2419 100644 --- a/src/net/http/httptest/server_test.go +++ b/src/net/http/httptest/server_test.go @@ -84,3 +84,17 @@ func TestServerCloseBlocking(t *testing.T) { ts.Close() // test we don't hang here forever. } + +// Issue 14290 +func TestServerCloseClientConnections(t *testing.T) { + var s *Server + s = NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + s.CloseClientConnections() + })) + defer s.Close() + res, err := http.Get(s.URL) + if err == nil { + res.Body.Close() + t.Fatal("Unexpected response: %#v", res) + } +} diff --git a/src/net/http/httputil/dump.go b/src/net/http/httputil/dump.go index e22cc66dbf..245eed0b21 100644 --- a/src/net/http/httputil/dump.go +++ b/src/net/http/httputil/dump.go @@ -264,19 +264,20 @@ func DumpRequest(req *http.Request, body bool) (dump []byte, err error) { return } -// errNoBody is a sentinel error value used by failureToReadBody so we can detect -// that the lack of body was intentional. +// errNoBody is a sentinel error value used by failureToReadBody so we +// can detect that the lack of body was intentional. var errNoBody = errors.New("sentinel error value") // failureToReadBody is a io.ReadCloser that just returns errNoBody on -// Read. It's swapped in when we don't actually want to consume the -// body, but need a non-nil one, and want to distinguish the error -// from reading the dummy body. +// Read. It's swapped in when we don't actually want to consume +// the body, but need a non-nil one, and want to distinguish the +// error from reading the dummy body. type failureToReadBody struct{} func (failureToReadBody) Read([]byte) (int, error) { return 0, errNoBody } func (failureToReadBody) Close() error { return nil } +// emptyBody is an instance of empty reader. var emptyBody = ioutil.NopCloser(strings.NewReader("")) // DumpResponse is like DumpRequest but dumps a response. @@ -286,7 +287,13 @@ func DumpResponse(resp *http.Response, body bool) (dump []byte, err error) { savecl := resp.ContentLength if !body { - resp.Body = failureToReadBody{} + // For content length of zero. Make sure the body is an empty + // reader, instead of returning error through failureToReadBody{}. + if resp.ContentLength == 0 { + resp.Body = emptyBody + } else { + resp.Body = failureToReadBody{} + } } else if resp.Body == nil { resp.Body = emptyBody } else { diff --git a/src/net/http/httputil/dump_test.go b/src/net/http/httputil/dump_test.go index 46bf521723..fc884347a6 100644 --- a/src/net/http/httputil/dump_test.go +++ b/src/net/http/httputil/dump_test.go @@ -288,6 +288,27 @@ Transfer-Encoding: chunked foo 0`, }, + { + res: &http.Response{ + Status: "200 OK", + StatusCode: 200, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + ContentLength: 0, + Header: http.Header{ + // To verify if headers are not filtered out. + "Foo1": []string{"Bar1"}, + "Foo2": []string{"Bar2"}, + }, + Body: nil, + }, + body: false, // to verify we see 0, not empty. + want: `HTTP/1.1 200 OK +Foo1: Bar1 +Foo2: Bar2 +Content-Length: 0`, + }, } func TestDumpResponse(t *testing.T) { diff --git a/src/net/http/server.go b/src/net/http/server.go index 5e3b6084ae..e2d8d277e0 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -1009,7 +1009,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { w.closeAfterReply = true } default: - // Some other kind of error occured, like a read timeout, or + // Some other kind of error occurred, like a read timeout, or // corrupt chunked encoding. In any case, whatever remains // on the wire must not be parsed as another HTTP request. w.closeAfterReply = true @@ -2032,7 +2032,7 @@ const ( // For HTTP/2, StateActive fires on the transition from zero // to one active request, and only transitions away once all // active requests are complete. That means that ConnState - // can not be used to do per-request work; ConnState only notes + // cannot be used to do per-request work; ConnState only notes // the overall state of the connection. StateActive diff --git a/src/net/http/transport.go b/src/net/http/transport.go index 41df906cf2..03e9162b14 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -163,6 +163,26 @@ func (t *Transport) onceSetNextProtoDefaults() { return } if t.TLSNextProto != nil { + // This is the documented way to disable http2 on a + // Transport. + return + } + if t.TLSClientConfig != nil { + // Be conservative for now (for Go 1.6) at least and + // don't automatically enable http2 if they've + // specified a custom TLS config. Let them opt-in + // themselves via http2.ConfigureTransport so we don't + // surprise them by modifying their tls.Config. + // Issue 14275. + return + } + if t.ExpectContinueTimeout != 0 && t != DefaultTransport { + // ExpectContinueTimeout is unsupported in http2, so + // if they explicitly asked for it (as opposed to just + // using the DefaultTransport, which sets it), then + // disable http2 for now. + // + // Issue 13851. (and changed in Issue 14391) return } t2, err := http2configureTransport(t) @@ -398,7 +418,7 @@ func (t *Transport) CloseIdleConnections() { // CancelRequest cancels an in-flight request by closing its connection. // CancelRequest should only be called after RoundTrip has returned. // -// Deprecated: Use Request.Cancel instead. CancelRequest can not cancel +// Deprecated: Use Request.Cancel instead. CancelRequest cannot cancel // HTTP/2 requests. func (t *Transport) CancelRequest(req *Request) { t.reqMu.Lock() @@ -1337,7 +1357,7 @@ type writeRequest struct { req *transportRequest ch chan<- error - // Optional blocking chan for Expect: 100-continue (for recieve). + // Optional blocking chan for Expect: 100-continue (for receive). // If not nil, writeLoop blocks sending request body until // it receives from this chan. continueCh <-chan struct{} diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index 8cb89a4220..d9da078fa0 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -2885,23 +2885,39 @@ func TestTransportPrefersResponseOverWriteError(t *testing.T) { } func TestTransportAutomaticHTTP2(t *testing.T) { - tr := &Transport{} + testTransportAutoHTTP(t, &Transport{}, true) +} + +// golang.org/issue/14391: also check DefaultTransport +func TestTransportAutomaticHTTP2_DefaultTransport(t *testing.T) { + testTransportAutoHTTP(t, DefaultTransport.(*Transport), true) +} + +func TestTransportAutomaticHTTP2_TLSNextProto(t *testing.T) { + testTransportAutoHTTP(t, &Transport{ + TLSNextProto: make(map[string]func(string, *tls.Conn) RoundTripper), + }, false) +} + +func TestTransportAutomaticHTTP2_TLSConfig(t *testing.T) { + testTransportAutoHTTP(t, &Transport{ + TLSClientConfig: new(tls.Config), + }, false) +} + +func TestTransportAutomaticHTTP2_ExpectContinueTimeout(t *testing.T) { + testTransportAutoHTTP(t, &Transport{ + ExpectContinueTimeout: 1 * time.Second, + }, false) +} + +func testTransportAutoHTTP(t *testing.T, tr *Transport, wantH2 bool) { _, err := tr.RoundTrip(new(Request)) if err == nil { t.Error("expected error from RoundTrip") } - if tr.TLSNextProto["h2"] == nil { - t.Errorf("HTTP/2 not registered.") - } - - // Now with TLSNextProto set: - tr = &Transport{TLSNextProto: make(map[string]func(string, *tls.Conn) RoundTripper)} - _, err = tr.RoundTrip(new(Request)) - if err == nil { - t.Error("expected error from RoundTrip") - } - if tr.TLSNextProto["h2"] != nil { - t.Errorf("HTTP/2 registered, despite non-nil TLSNextProto field") + if reg := tr.TLSNextProto["h2"] != nil; reg != wantH2 { + t.Errorf("HTTP/2 registered = %v; want %v", reg, wantH2) } } diff --git a/src/net/internal/socktest/switch.go b/src/net/internal/socktest/switch.go index 8bef06b97c..3c37b6ff80 100644 --- a/src/net/internal/socktest/switch.go +++ b/src/net/internal/socktest/switch.go @@ -121,7 +121,7 @@ const ( FilterSocket FilterType = iota // for Socket FilterConnect // for Connect or ConnectEx FilterListen // for Listen - FilterAccept // for Accept or Accept4 + FilterAccept // for Accept, Accept4 or AcceptEx FilterGetsockoptInt // for GetsockoptInt FilterClose // for Close or Closesocket ) diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go index e61bf2be60..2e3d2bc7fc 100644 --- a/src/net/internal/socktest/sys_windows.go +++ b/src/net/internal/socktest/sys_windows.go @@ -154,3 +154,33 @@ func (sw *Switch) Listen(s syscall.Handle, backlog int) (err error) { sw.stats.getLocked(so.Cookie).Listened++ return nil } + +// AcceptEx wraps syscall.AcceptEx. +func (sw *Switch) AcceptEx(ls syscall.Handle, as syscall.Handle, b *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, rcvd *uint32, overlapped *syscall.Overlapped) error { + so := sw.sockso(ls) + if so == nil { + return syscall.AcceptEx(ls, as, b, rxdatalen, laddrlen, raddrlen, rcvd, overlapped) + } + sw.fmu.RLock() + f, _ := sw.fltab[FilterAccept] + sw.fmu.RUnlock() + + af, err := f.apply(so) + if err != nil { + return err + } + so.Err = syscall.AcceptEx(ls, as, b, rxdatalen, laddrlen, raddrlen, rcvd, overlapped) + if err = af.apply(so); err != nil { + return err + } + + sw.smu.Lock() + defer sw.smu.Unlock() + if so.Err != nil { + sw.stats.getLocked(so.Cookie).AcceptFailed++ + return so.Err + } + nso := sw.addLocked(as, so.Cookie.Family(), so.Cookie.Type(), so.Cookie.Protocol()) + sw.stats.getLocked(nso.Cookie).Accepted++ + return nil +} diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go index 2d829743ec..b879717425 100644 --- a/src/net/main_windows_test.go +++ b/src/net/main_windows_test.go @@ -11,6 +11,7 @@ var ( origConnect = connectFunc origConnectEx = connectExFunc origListen = listenFunc + origAccept = acceptFunc ) func installTestHooks() { @@ -19,6 +20,7 @@ func installTestHooks() { connectFunc = sw.Connect connectExFunc = sw.ConnectEx listenFunc = sw.Listen + acceptFunc = sw.AcceptEx } func uninstallTestHooks() { @@ -27,6 +29,7 @@ func uninstallTestHooks() { connectFunc = origConnect connectExFunc = origConnectEx listenFunc = origListen + acceptFunc = origAccept } func forceCloseSockets() { diff --git a/src/net/mockserver_test.go b/src/net/mockserver_test.go index dd6f4df3b9..38b317af7d 100644 --- a/src/net/mockserver_test.go +++ b/src/net/mockserver_test.go @@ -30,10 +30,20 @@ func testUnixAddr() string { func newLocalListener(network string) (Listener, error) { switch network { - case "tcp", "tcp4", "tcp6": + case "tcp": + if supportsIPv4 { + if ln, err := Listen("tcp4", "127.0.0.1:0"); err == nil { + return ln, nil + } + } + if supportsIPv6 { + return Listen("tcp6", "[::1]:0") + } + case "tcp4": if supportsIPv4 { return Listen("tcp4", "127.0.0.1:0") } + case "tcp6": if supportsIPv6 { return Listen("tcp6", "[::1]:0") } diff --git a/src/net/net_test.go b/src/net/net_test.go index cd62b4373e..94392928c2 100644 --- a/src/net/net_test.go +++ b/src/net/net_test.go @@ -6,6 +6,7 @@ package net import ( "io" + "net/internal/socktest" "os" "runtime" "testing" @@ -304,3 +305,58 @@ func TestListenCloseListen(t *testing.T) { } t.Fatalf("failed to listen/close/listen on same address after %d tries", maxTries) } + +// See golang.org/issue/6163, golang.org/issue/6987. +func TestAcceptIgnoreAbortedConnRequest(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("%s does not have full support of socktest", runtime.GOOS) + } + + syserr := make(chan error) + go func() { + defer close(syserr) + for _, err := range abortedConnRequestErrors { + syserr <- err + } + }() + sw.Set(socktest.FilterAccept, func(so *socktest.Status) (socktest.AfterFilter, error) { + if err, ok := <-syserr; ok { + return nil, err + } + return nil, nil + }) + defer sw.Set(socktest.FilterAccept, nil) + + operr := make(chan error, 1) + handler := func(ls *localServer, ln Listener) { + defer close(operr) + c, err := ln.Accept() + if err != nil { + if perr := parseAcceptError(err); perr != nil { + operr <- perr + } + operr <- err + return + } + c.Close() + } + ls, err := newLocalServer("tcp") + if err != nil { + t.Fatal(err) + } + defer ls.teardown() + if err := ls.buildup(handler); err != nil { + t.Fatal(err) + } + + c, err := Dial(ls.Listener.Addr().Network(), ls.Listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + c.Close() + + for err := range operr { + t.Error(err) + } +} diff --git a/src/net/net_windows_test.go b/src/net/net_windows_test.go index 095a339e02..df39032721 100644 --- a/src/net/net_windows_test.go +++ b/src/net/net_windows_test.go @@ -314,20 +314,43 @@ func TestInterfacesWithNetsh(t *testing.T) { } } -func netshInterfaceIPv4ShowAddress(name string) ([]string, error) { - out, err := runCmd("netsh", "interface", "ipv4", "show", "address", "name=\""+name+"\"") - if err != nil { - return nil, err - } +func netshInterfaceIPv4ShowAddress(name string, netshOutput []byte) []string { // adress information is listed like: + // + //Configuration for interface "Local Area Connection" + // DHCP enabled: Yes // IP Address: 10.0.0.2 // Subnet Prefix: 10.0.0.0/24 (mask 255.255.255.0) // IP Address: 10.0.0.3 // Subnet Prefix: 10.0.0.0/24 (mask 255.255.255.0) + // Default Gateway: 10.0.0.254 + // Gateway Metric: 0 + // InterfaceMetric: 10 + // + //Configuration for interface "Loopback Pseudo-Interface 1" + // DHCP enabled: No + // IP Address: 127.0.0.1 + // Subnet Prefix: 127.0.0.0/8 (mask 255.0.0.0) + // InterfaceMetric: 50 + // addrs := make([]string, 0) var addr, subnetprefix string - lines := bytes.Split(out, []byte{'\r', '\n'}) + var processingOurInterface bool + lines := bytes.Split(netshOutput, []byte{'\r', '\n'}) for _, line := range lines { + if !processingOurInterface { + if !bytes.HasPrefix(line, []byte("Configuration for interface")) { + continue + } + if !bytes.Contains(line, []byte(`"`+name+`"`)) { + continue + } + processingOurInterface = true + continue + } + if len(line) == 0 { + break + } if bytes.Contains(line, []byte("Subnet Prefix:")) { f := bytes.Split(line, []byte{':'}) if len(f) == 2 { @@ -351,18 +374,50 @@ func netshInterfaceIPv4ShowAddress(name string) ([]string, error) { } } } - return addrs, nil + return addrs } -func netshInterfaceIPv6ShowAddress(name string) ([]string, error) { +func netshInterfaceIPv6ShowAddress(name string, netshOutput []byte) []string { + // adress information is listed like: + // + //Address ::1 Parameters + //--------------------------------------------------------- + //Interface Luid : Loopback Pseudo-Interface 1 + //Scope Id : 0.0 + //Valid Lifetime : infinite + //Preferred Lifetime : infinite + //DAD State : Preferred + //Address Type : Other + //Skip as Source : false + // + //Address XXXX::XXXX:XXXX:XXXX:XXXX%11 Parameters + //--------------------------------------------------------- + //Interface Luid : Local Area Connection + //Scope Id : 0.11 + //Valid Lifetime : infinite + //Preferred Lifetime : infinite + //DAD State : Preferred + //Address Type : Other + //Skip as Source : false + // + // TODO: need to test ipv6 netmask too, but netsh does not outputs it - out, err := runCmd("netsh", "interface", "ipv6", "show", "address", "interface=\""+name+"\"") - if err != nil { - return nil, err - } + var addr string addrs := make([]string, 0) - lines := bytes.Split(out, []byte{'\r', '\n'}) + lines := bytes.Split(netshOutput, []byte{'\r', '\n'}) for _, line := range lines { + if addr != "" { + if len(line) == 0 { + addr = "" + continue + } + if string(line) != "Interface Luid : "+name { + continue + } + addrs = append(addrs, addr) + addr = "" + continue + } if !bytes.HasPrefix(line, []byte("Address")) { continue } @@ -383,9 +438,9 @@ func netshInterfaceIPv6ShowAddress(name string) ([]string, error) { f[0] = []byte(ParseIP(string(f[0])).String()) } - addrs = append(addrs, string(bytes.ToLower(bytes.TrimSpace(f[0])))) + addr = string(bytes.ToLower(bytes.TrimSpace(f[0]))) } - return addrs, nil + return addrs } func TestInterfaceAddrsWithNetsh(t *testing.T) { @@ -395,6 +450,16 @@ func TestInterfaceAddrsWithNetsh(t *testing.T) { if !isEnglishOS(t) { t.Skip("English version of OS required for this test") } + + outIPV4, err := runCmd("netsh", "interface", "ipv4", "show", "address") + if err != nil { + t.Fatal(err) + } + outIPV6, err := runCmd("netsh", "interface", "ipv6", "show", "address", "level=verbose") + if err != nil { + t.Fatal(err) + } + ift, err := Interfaces() if err != nil { t.Fatal(err) @@ -431,14 +496,8 @@ func TestInterfaceAddrsWithNetsh(t *testing.T) { } sort.Strings(have) - want, err := netshInterfaceIPv4ShowAddress(ifi.Name) - if err != nil { - t.Fatal(err) - } - wantIPv6, err := netshInterfaceIPv6ShowAddress(ifi.Name) - if err != nil { - t.Fatal(err) - } + want := netshInterfaceIPv4ShowAddress(ifi.Name, outIPV4) + wantIPv6 := netshInterfaceIPv6ShowAddress(ifi.Name, outIPV6) want = append(want, wantIPv6...) sort.Strings(want) @@ -487,8 +546,13 @@ func TestInterfaceHardwareAddrWithGetmac(t *testing.T) { // //Connection Name: Bluetooth Network Connection //Network Adapter: Bluetooth Device (Personal Area Network) - //Physical Address: XX-XX-XX-XX-XX-XX - //Transport Name: Media disconnected + //Physical Address: N/A + //Transport Name: Hardware not present + // + //Connection Name: VMware Network Adapter VMnet8 + //Network Adapter: VMware Virtual Ethernet Adapter for VMnet8 + //Physical Address: Disabled + //Transport Name: Disconnected // want := make(map[string]string) var name string @@ -516,6 +580,9 @@ func TestInterfaceHardwareAddrWithGetmac(t *testing.T) { if addr == "" { t.Fatal("empty address on \"Physical Address\" line: %q", line) } + if addr == "disabled" || addr == "n/a" { + continue + } addr = strings.Replace(addr, "-", ":", -1) want[name] = addr name = "" diff --git a/src/net/rpc/server_test.go b/src/net/rpc/server_test.go index 8871c88133..cf171ac4fb 100644 --- a/src/net/rpc/server_test.go +++ b/src/net/rpc/server_test.go @@ -183,7 +183,7 @@ func testRPC(t *testing.T, addr string) { err = client.Call("Arith.Unknown", args, reply) if err == nil { t.Error("expected error calling unknown service") - } else if strings.Index(err.Error(), "method") < 0 { + } else if !strings.Contains(err.Error(), "method") { t.Error("expected error about method; got", err) } @@ -226,7 +226,7 @@ func testRPC(t *testing.T, addr string) { err = client.Call("Arith.Add", reply, reply) // args, reply would be the correct thing to use if err == nil { t.Error("expected error calling Arith.Add with wrong arg type") - } else if strings.Index(err.Error(), "type") < 0 { + } else if !strings.Contains(err.Error(), "type") { t.Error("expected error about type; got", err) } diff --git a/src/net/url/url.go b/src/net/url/url.go index 1a93e3496e..b3513a85a3 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -511,7 +511,7 @@ func parseAuthority(authority string) (user *Userinfo, host string, err error) { return nil, host, nil } userinfo := authority[:i] - if strings.Index(userinfo, ":") < 0 { + if !strings.Contains(userinfo, ":") { if userinfo, err = unescape(userinfo, encodeUserPassword); err != nil { return nil, "", err } @@ -709,8 +709,8 @@ func (v Values) Get(key string) string { if v == nil { return "" } - vs, ok := v[key] - if !ok || len(vs) == 0 { + vs := v[key] + if len(vs) == 0 { return "" } return vs[0] diff --git a/src/os/exec/lp_windows.go b/src/os/exec/lp_windows.go index c3efd67e9e..0b0712dcad 100644 --- a/src/os/exec/lp_windows.go +++ b/src/os/exec/lp_windows.go @@ -70,7 +70,7 @@ func LookPath(file string) (f string, err error) { } exts = append(exts, e) } - if strings.IndexAny(file, `:\/`) != -1 { + if strings.ContainsAny(file, `:\/`) { if f, err = findExecutable(file, exts); err == nil { return } diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go index 5c073da991..2f7d48d5bd 100644 --- a/src/os/os_windows_test.go +++ b/src/os/os_windows_test.go @@ -177,7 +177,7 @@ func TestStatDir(t *testing.T) { } if !os.SameFile(fi, fi2) { - t.Fatal("race condition occured") + t.Fatal("race condition occurred") } } diff --git a/src/os/signal/doc.go b/src/os/signal/doc.go index 80e66cffe5..9ee547b15d 100644 --- a/src/os/signal/doc.go +++ b/src/os/signal/doc.go @@ -11,7 +11,7 @@ package on Windows and Plan 9, see below. Types of signals The signals SIGKILL and SIGSTOP may not be caught by a program, and -therefore can not be affected by this package. +therefore cannot be affected by this package. Synchronous signals are signals triggered by errors in program execution: SIGBUS, SIGFPE, and SIGSEGV. These are only considered diff --git a/src/path/filepath/match.go b/src/path/filepath/match.go index 89f16de355..d64bf84fc0 100644 --- a/src/path/filepath/match.go +++ b/src/path/filepath/match.go @@ -49,7 +49,7 @@ Pattern: star, chunk, pattern = scanChunk(pattern) if star && chunk == "" { // Trailing * matches rest of string unless it has a /. - return strings.Index(name, string(Separator)) < 0, nil + return !strings.Contains(name, string(Separator)), nil } // Look for match at current position. t, ok, err := matchChunk(chunk, name) @@ -305,5 +305,5 @@ func glob(dir, pattern string, matches []string) (m []string, e error) { // recognized by Match. func hasMeta(path string) bool { // TODO(niemeyer): Should other magic characters be added here? - return strings.IndexAny(path, "*?[") >= 0 + return strings.ContainsAny(path, "*?[") } diff --git a/src/path/filepath/match_test.go b/src/path/filepath/match_test.go index 0edbfc70c4..d8bab7f4da 100644 --- a/src/path/filepath/match_test.go +++ b/src/path/filepath/match_test.go @@ -88,7 +88,7 @@ func TestMatch(t *testing.T) { pattern := tt.pattern s := tt.s if runtime.GOOS == "windows" { - if strings.Index(pattern, "\\") >= 0 { + if strings.Contains(pattern, "\\") { // no escape allowed on windows. continue } diff --git a/src/path/filepath/symlink.go b/src/path/filepath/symlink.go index bc287c5ecb..f627a94ddb 100644 --- a/src/path/filepath/symlink.go +++ b/src/path/filepath/symlink.go @@ -100,7 +100,7 @@ func walkSymlinks(path string) (string, error) { return "", err } if runtime.GOOS == "windows" { - // walkLinks(".", ...) always retuns "." on unix. + // walkLinks(".", ...) always returns "." on unix. // But on windows it returns symlink target, if current // directory is a symlink. Stop the walk, if symlink // target is not absolute path, and return "." diff --git a/src/path/match.go b/src/path/match.go index 75dd3b38e7..8d9aa513b1 100644 --- a/src/path/match.go +++ b/src/path/match.go @@ -43,7 +43,7 @@ Pattern: star, chunk, pattern = scanChunk(pattern) if star && chunk == "" { // Trailing * matches rest of string unless it has a /. - return strings.Index(name, "/") < 0, nil + return !strings.Contains(name, "/"), nil } // Look for match at current position. t, ok, err := matchChunk(chunk, name) diff --git a/src/regexp/backtrack.go b/src/regexp/backtrack.go index fd95604fe4..3a1100dde8 100644 --- a/src/regexp/backtrack.go +++ b/src/regexp/backtrack.go @@ -254,7 +254,6 @@ func (m *machine) tryBacktrack(b *bitState, i input, pc uint32, pos int) bool { } panic("bad arg in InstCapture") - continue case syntax.InstEmptyWidth: if syntax.EmptyOp(inst.Arg)&^i.context(pos) != 0 { @@ -299,7 +298,6 @@ func (m *machine) tryBacktrack(b *bitState, i input, pc uint32, pos int) bool { // Otherwise, continue on in hope of a longer match. continue } - panic("unreachable") } return m.matched diff --git a/src/regexp/regexp.go b/src/regexp/regexp.go index d7d0edb993..42ae6e1d7a 100644 --- a/src/regexp/regexp.go +++ b/src/regexp/regexp.go @@ -454,7 +454,7 @@ func Match(pattern string, b []byte) (matched bool, err error) { // in Expand, so for instance $1 represents the text of the first submatch. func (re *Regexp) ReplaceAllString(src, repl string) string { n := 2 - if strings.Index(repl, "$") >= 0 { + if strings.Contains(repl, "$") { n = 2 * (re.numSubexp + 1) } b := re.replaceAll(nil, src, n, func(dst []byte, match []int) []byte { diff --git a/src/regexp/syntax/regexp.go b/src/regexp/syntax/regexp.go index 75822cf981..ca5724063b 100644 --- a/src/regexp/syntax/regexp.go +++ b/src/regexp/syntax/regexp.go @@ -252,7 +252,7 @@ const meta = `\.+*?()|[]{}^$` func escape(b *bytes.Buffer, r rune, force bool) { if unicode.IsPrint(r) { - if strings.IndexRune(meta, r) >= 0 || force { + if strings.ContainsRune(meta, r) || force { b.WriteRune('\\') } b.WriteRune(r) diff --git a/src/runtime/alg.go b/src/runtime/alg.go index 9ea0eb0187..9e19119f4a 100644 --- a/src/runtime/alg.go +++ b/src/runtime/alg.go @@ -16,24 +16,16 @@ const ( // type algorithms - known to compiler const ( - alg_MEM = iota + alg_NOEQ = iota alg_MEM0 alg_MEM8 alg_MEM16 alg_MEM32 alg_MEM64 alg_MEM128 - alg_NOEQ - alg_NOEQ0 - alg_NOEQ8 - alg_NOEQ16 - alg_NOEQ32 - alg_NOEQ64 - alg_NOEQ128 alg_STRING alg_INTER alg_NILINTER - alg_SLICE alg_FLOAT32 alg_FLOAT64 alg_CPLX64 @@ -77,24 +69,16 @@ func memhash128(p unsafe.Pointer, h uintptr) uintptr { func memhash_varlen(p unsafe.Pointer, h uintptr) uintptr var algarray = [alg_max]typeAlg{ - alg_MEM: {nil, nil}, // not used + alg_NOEQ: {nil, nil}, alg_MEM0: {memhash0, memequal0}, alg_MEM8: {memhash8, memequal8}, alg_MEM16: {memhash16, memequal16}, alg_MEM32: {memhash32, memequal32}, alg_MEM64: {memhash64, memequal64}, alg_MEM128: {memhash128, memequal128}, - alg_NOEQ: {nil, nil}, - alg_NOEQ0: {nil, nil}, - alg_NOEQ8: {nil, nil}, - alg_NOEQ16: {nil, nil}, - alg_NOEQ32: {nil, nil}, - alg_NOEQ64: {nil, nil}, - alg_NOEQ128: {nil, nil}, alg_STRING: {strhash, strequal}, alg_INTER: {interhash, interequal}, alg_NILINTER: {nilinterhash, nilinterequal}, - alg_SLICE: {nil, nil}, alg_FLOAT32: {f32hash, f32equal}, alg_FLOAT64: {f64hash, f64equal}, alg_CPLX64: {c64hash, c64equal}, @@ -188,13 +172,6 @@ func nilinterhash(p unsafe.Pointer, h uintptr) uintptr { } } -func memequal(p, q unsafe.Pointer, size uintptr) bool { - if p == q { - return true - } - return memeq(p, q, size) -} - func memequal0(p, q unsafe.Pointer) bool { return true } diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s index 4181859724..9237d57f24 100644 --- a/src/runtime/asm_386.s +++ b/src/runtime/asm_386.s @@ -1239,12 +1239,18 @@ TEXT ·checkASM(SB),NOSPLIT,$0-1 SETEQ ret+0(FP) RET -TEXT runtime·memeq(SB),NOSPLIT,$0-13 +// memequal(p, q unsafe.Pointer, size uintptr) bool +TEXT runtime·memequal(SB),NOSPLIT,$0-13 MOVL a+0(FP), SI MOVL b+4(FP), DI + CMPL SI, DI + JEQ eq MOVL size+8(FP), BX LEAL ret+12(FP), AX JMP runtime·memeqbody(SB) +eq: + MOVB $1, ret+12(FP) + RET // memequal_varlen(a, b unsafe.Pointer) bool TEXT runtime·memequal_varlen(SB),NOSPLIT,$0-9 diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index 5094812a05..98a8e839ed 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -1269,12 +1269,18 @@ DATA shifts<>+0xf0(SB)/8, $0x0807060504030201 DATA shifts<>+0xf8(SB)/8, $0xff0f0e0d0c0b0a09 GLOBL shifts<>(SB),RODATA,$256 -TEXT runtime·memeq(SB),NOSPLIT,$0-25 +// memequal(p, q unsafe.Pointer, size uintptr) bool +TEXT runtime·memequal(SB),NOSPLIT,$0-25 MOVQ a+0(FP), SI MOVQ b+8(FP), DI + CMPQ SI, DI + JEQ eq MOVQ size+16(FP), BX LEAQ ret+24(FP), AX JMP runtime·memeqbody(SB) +eq: + MOVB $1, ret+24(FP) + RET // memequal_varlen(a, b unsafe.Pointer) bool TEXT runtime·memequal_varlen(SB),NOSPLIT,$0-17 diff --git a/src/runtime/asm_amd64p32.s b/src/runtime/asm_amd64p32.s index ecbc5975bb..ae7a53821b 100644 --- a/src/runtime/asm_amd64p32.s +++ b/src/runtime/asm_amd64p32.s @@ -573,13 +573,19 @@ TEXT runtime·aeshash64(SB),NOSPLIT,$0-20 MOVL AX, ret+16(FP) RET -TEXT runtime·memeq(SB),NOSPLIT,$0-17 +// memequal(p, q unsafe.Pointer, size uintptr) bool +TEXT runtime·memequal(SB),NOSPLIT,$0-13 MOVL a+0(FP), SI MOVL b+4(FP), DI + CMPL SI, DI + JEQ eq MOVL size+8(FP), BX CALL runtime·memeqbody(SB) MOVB AX, ret+16(FP) RET +eq: + MOVB $1, ret+16(FP) + RET // memequal_varlen(a, b unsafe.Pointer) bool TEXT runtime·memequal_varlen(SB),NOSPLIT,$0-9 diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s index 07894a3a72..5d0206d1c9 100644 --- a/src/runtime/asm_arm.s +++ b/src/runtime/asm_arm.s @@ -750,13 +750,16 @@ TEXT runtime·memhash_varlen(SB),NOSPLIT,$16-12 MOVW R0, ret+8(FP) RET -TEXT runtime·memeq(SB),NOSPLIT,$-4-13 +// memequal(p, q unsafe.Pointer, size uintptr) bool +TEXT runtime·memequal(SB),NOSPLIT,$-4-13 MOVW a+0(FP), R1 MOVW b+4(FP), R2 MOVW size+8(FP), R3 ADD R1, R3, R6 MOVW $1, R0 MOVB R0, ret+12(FP) + CMP R1, R2 + RET.EQ loop: CMP R1, R6 RET.EQ @@ -779,7 +782,7 @@ TEXT runtime·memequal_varlen(SB),NOSPLIT,$16-9 MOVW R0, 4(R13) MOVW R1, 8(R13) MOVW R2, 12(R13) - BL runtime·memeq(SB) + BL runtime·memequal(SB) MOVB 16(R13), R0 MOVB R0, ret+8(FP) RET @@ -866,7 +869,7 @@ loop: MOVB R8, v+16(FP) RET -// TODO: share code with memeq? +// TODO: share code with memequal? TEXT bytes·Equal(SB),NOSPLIT,$0-25 MOVW a_len+4(FP), R1 MOVW b_len+16(FP), R3 diff --git a/src/runtime/asm_arm64.s b/src/runtime/asm_arm64.s index ab5d5b5e5f..5a5c64c270 100644 --- a/src/runtime/asm_arm64.s +++ b/src/runtime/asm_arm64.s @@ -765,13 +765,16 @@ TEXT runtime·memhash_varlen(SB),NOSPLIT,$40-24 MOVD R3, ret+16(FP) RET -TEXT runtime·memeq(SB),NOSPLIT,$-8-25 +// memequal(p, q unsafe.Pointer, size uintptr) bool +TEXT runtime·memequal(SB),NOSPLIT,$-8-25 MOVD a+0(FP), R1 MOVD b+8(FP), R2 MOVD size+16(FP), R3 ADD R1, R3, R6 MOVD $1, R0 MOVB R0, ret+24(FP) + CMP R1, R2 + BEQ done loop: CMP R1, R6 BEQ done @@ -794,7 +797,7 @@ TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17 MOVD R3, 8(RSP) MOVD R4, 16(RSP) MOVD R5, 24(RSP) - BL runtime·memeq(SB) + BL runtime·memequal(SB) MOVBU 32(RSP), R3 MOVB R3, ret+16(FP) RET @@ -929,7 +932,7 @@ notfound: MOVD R0, ret+24(FP) RET -// TODO: share code with memeq? +// TODO: share code with memequal? TEXT bytes·Equal(SB),NOSPLIT,$0-49 MOVD a_len+8(FP), R1 MOVD b_len+32(FP), R3 diff --git a/src/runtime/asm_mips64x.s b/src/runtime/asm_mips64x.s index 08482fed23..80cea8587a 100644 --- a/src/runtime/asm_mips64x.s +++ b/src/runtime/asm_mips64x.s @@ -647,9 +647,11 @@ TEXT runtime·aeshash64(SB),NOSPLIT,$-8-0 TEXT runtime·aeshashstr(SB),NOSPLIT,$-8-0 MOVW (R0), R1 -TEXT runtime·memeq(SB),NOSPLIT,$-8-25 +// memequal(p, q unsafe.Pointer, size uintptr) bool +TEXT runtime·memequal(SB),NOSPLIT,$-8-25 MOVV a+0(FP), R1 MOVV b+8(FP), R2 + BEQ R1, R2, eq MOVV size+16(FP), R3 ADDV R1, R3, R4 loop: @@ -666,6 +668,10 @@ test: MOVB R0, ret+24(FP) RET +eq: + MOVV $1, R1 + MOVB R1, ret+24(FP) + RET // memequal_varlen(a, b unsafe.Pointer) bool TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17 @@ -676,7 +682,7 @@ TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17 MOVV R1, 8(R29) MOVV R2, 16(R29) MOVV R3, 24(R29) - JAL runtime·memeq(SB) + JAL runtime·memequal(SB) MOVBU 32(R29), R1 MOVB R1, ret+16(FP) RET @@ -710,7 +716,7 @@ loop: MOVB R0, ret+32(FP) RET -// TODO: share code with memeq? +// TODO: share code with memequal? TEXT bytes·Equal(SB),NOSPLIT,$0-49 MOVV a_len+8(FP), R3 MOVV b_len+32(FP), R4 diff --git a/src/runtime/asm_ppc64x.s b/src/runtime/asm_ppc64x.s index 50c4f2623c..f067b4a9b9 100644 --- a/src/runtime/asm_ppc64x.s +++ b/src/runtime/asm_ppc64x.s @@ -795,9 +795,12 @@ TEXT runtime·aeshash64(SB),NOSPLIT|NOFRAME,$0-0 TEXT runtime·aeshashstr(SB),NOSPLIT|NOFRAME,$0-0 MOVW (R0), R1 -TEXT runtime·memeq(SB),NOSPLIT|NOFRAME,$0-25 +// memequal(p, q unsafe.Pointer, size uintptr) bool +TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25 MOVD a+0(FP), R3 MOVD b+8(FP), R4 + CMP R3, R4 + BEQ eq MOVD size+16(FP), R5 SUB $1, R3 SUB $1, R4 @@ -816,6 +819,10 @@ test: MOVB R0, ret+24(FP) RET +eq: + MOVD $1, R1 + MOVB R1, ret+24(FP) + RET // memequal_varlen(a, b unsafe.Pointer) bool TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17 @@ -827,7 +834,7 @@ TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17 MOVD R3, FIXED_FRAME+0(R1) MOVD R4, FIXED_FRAME+8(R1) MOVD R5, FIXED_FRAME+16(R1) - BL runtime·memeq(SB) + BL runtime·memequal(SB) MOVBZ FIXED_FRAME+24(R1), R3 MOVB R3, ret+16(FP) RET @@ -864,7 +871,7 @@ loop: MOVB R0, ret+32(FP) RET -// TODO: share code with memeq? +// TODO: share code with memequal? TEXT bytes·Equal(SB),NOSPLIT,$0-49 MOVD a_len+8(FP), R3 MOVD b_len+32(FP), R4 diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go index 66115fd8b4..f632f7ab5a 100644 --- a/src/runtime/cgocall.go +++ b/src/runtime/cgocall.go @@ -340,7 +340,7 @@ var racecgosync uint64 // represents possible synchronization in C code // When and if we implement a moving garbage collector, // cgoCheckPointer will pin the pointer for the duration of the cgo // call. (This is necessary but not sufficient; the cgo program will -// also have to change to pin Go pointers that can not point to Go +// also have to change to pin Go pointers that cannot point to Go // pointers.) // cgoCheckPointer checks if the argument contains a Go pointer that @@ -463,6 +463,9 @@ func cgoCheckArg(t *_type, p unsafe.Pointer, indir, top bool, msg string) { if !top { panic(errorString(msg)) } + if st.elem.kind&kindNoPointers != 0 { + return + } for i := 0; i < s.cap; i++ { cgoCheckArg(st.elem, p, true, false, msg) p = add(p, st.elem.size) diff --git a/src/runtime/cgocheck.go b/src/runtime/cgocheck.go index 0077e22332..aebce1506d 100644 --- a/src/runtime/cgocheck.go +++ b/src/runtime/cgocheck.go @@ -135,9 +135,6 @@ func cgoCheckTypedBlock(typ *_type, src unsafe.Pointer, off, size uintptr) { hbits := heapBitsForAddr(uintptr(src)) for i := uintptr(0); i < off+size; i += sys.PtrSize { bits := hbits.bits() - if bits != 0 { - println(i, bits) - } if i >= off && bits&bitPointer != 0 { v := *(*unsafe.Pointer)(add(src, i)) if cgoIsGoPointer(v) { diff --git a/src/runtime/chan.go b/src/runtime/chan.go index 5be18beb23..063c5ce391 100644 --- a/src/runtime/chan.go +++ b/src/runtime/chan.go @@ -421,7 +421,7 @@ func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, r if sg := c.sendq.dequeue(); sg != nil { // Found a waiting sender. If buffer is size 0, receive value - // directly from sender. Otherwise, recieve from head of queue + // directly from sender. Otherwise, receive from head of queue // and add sender's value to the tail of the queue (both map to // the same buffer slot because the queue is full). recv(c, sg, ep, func() { unlock(&c.lock) }) diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go index d7b367f941..63769e801c 100644 --- a/src/runtime/crash_cgo_test.go +++ b/src/runtime/crash_cgo_test.go @@ -7,10 +7,12 @@ package runtime_test import ( + "internal/testenv" "os/exec" "runtime" "strings" "testing" + "time" ) func TestCgoCrashHandler(t *testing.T) { @@ -147,3 +149,41 @@ func TestEnsureDropM(t *testing.T) { t.Errorf("expected %q, got %v", want, got) } } + +// Test for issue 14387. +// Test that the program that doesn't need any cgo pointer checking +// takes about the same amount of time with it as without it. +func TestCgoCheckBytes(t *testing.T) { + // Make sure we don't count the build time as part of the run time. + testenv.MustHaveGoBuild(t) + exe, err := buildTestProg(t, "testprogcgo") + if err != nil { + t.Fatal(err) + } + + cmd := testEnv(exec.Command(exe, "CgoCheckBytes")) + + start := time.Now() + cmd.Run() + d1 := time.Since(start) + + cmd = testEnv(exec.Command(exe, "CgoCheckBytes")) + cmd.Env = append(cmd.Env, "GODEBUG=cgocheck=0") + + start = time.Now() + cmd.Run() + d2 := time.Since(start) + + if d2*10 < d1 { + t.Errorf("cgo check too slow: got %v, expected at most %v", d1, d2*10) + } +} + +func TestCgoPanicDeadlock(t *testing.T) { + // test issue 14432 + got := runTestProg(t, "testprogcgo", "CgoPanicDeadlock") + want := "panic: cgo error\n\n" + if !strings.HasPrefix(got, want) { + t.Fatalf("output does not start with %q:\n%s", want, got) + } +} diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index b622eb4526..de45e832f8 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -317,3 +317,38 @@ func TestNetpollDeadlock(t *testing.T) { t.Fatalf("output does not start with %q:\n%s", want, output) } } + +func TestPanicTraceback(t *testing.T) { + output := runTestProg(t, "testprog", "PanicTraceback") + want := "panic: hello" + if !strings.HasPrefix(output, want) { + t.Fatalf("output does not start with %q:\n%s", want, output) + } + + // Check functions in the traceback. + fns := []string{"panic", "main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"} + for _, fn := range fns { + re := regexp.MustCompile(`(?m)^` + regexp.QuoteMeta(fn) + `\(.*\n`) + idx := re.FindStringIndex(output) + if idx == nil { + t.Fatalf("expected %q function in traceback:\n%s", fn, output) + } + output = output[idx[1]:] + } +} + +func testPanicDeadlock(t *testing.T, name string, want string) { + // test issue 14432 + output := runTestProg(t, "testprog", name) + if !strings.HasPrefix(output, want) { + t.Fatalf("output does not start with %q:\n%s", want, output) + } +} + +func TestPanicDeadlockGosched(t *testing.T) { + testPanicDeadlock(t, "GoschedInPanic", "panic: errorThatGosched\n\n") +} + +func TestPanicDeadlockSyscall(t *testing.T) { + testPanicDeadlock(t, "SyscallInPanic", "1\n2\npanic: 3\n\n") +} diff --git a/src/runtime/crash_unix_test.go b/src/runtime/crash_unix_test.go index 1a012eb6ef..771b303f6e 100644 --- a/src/runtime/crash_unix_test.go +++ b/src/runtime/crash_unix_test.go @@ -14,6 +14,7 @@ import ( "os/exec" "path/filepath" "runtime" + "strings" "syscall" "testing" ) @@ -52,6 +53,18 @@ func TestCrashDumpsAllThreads(t *testing.T) { cmd = exec.Command(filepath.Join(dir, "a.exe")) cmd = testEnv(cmd) cmd.Env = append(cmd.Env, "GOTRACEBACK=crash") + + // Set GOGC=off. Because of golang.org/issue/10958, the tight + // loops in the test program are not preemptible. If GC kicks + // in, it may lock up and prevent main from saying it's ready. + newEnv := []string{} + for _, s := range cmd.Env { + if !strings.HasPrefix(s, "GOGC=") { + newEnv = append(newEnv, s) + } + } + cmd.Env = append(newEnv, "GOGC=off") + var outbuf bytes.Buffer cmd.Stdout = &outbuf cmd.Stderr = &outbuf diff --git a/src/runtime/debug/stack_test.go b/src/runtime/debug/stack_test.go index f54437231b..9376e82b84 100644 --- a/src/runtime/debug/stack_test.go +++ b/src/runtime/debug/stack_test.go @@ -59,7 +59,7 @@ func TestStack(t *testing.T) { } func check(t *testing.T, line, has string) { - if strings.Index(line, has) < 0 { + if !strings.Contains(line, has) { t.Errorf("expected %q in %q", has, line) } } diff --git a/src/runtime/export_linux_test.go b/src/runtime/export_linux_test.go index c8b9746676..61d6ae4bf2 100644 --- a/src/runtime/export_linux_test.go +++ b/src/runtime/export_linux_test.go @@ -7,3 +7,4 @@ package runtime var NewOSProc0 = newosproc0 +var Mincore = mincore diff --git a/src/runtime/export_mmap_test.go b/src/runtime/export_mmap_test.go new file mode 100644 index 0000000000..11ea076f0b --- /dev/null +++ b/src/runtime/export_mmap_test.go @@ -0,0 +1,15 @@ +// Copyright 2016 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. + +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris + +// Export guts for testing. + +package runtime + +var Mmap = mmap + +const ENOMEM = _ENOMEM +const MAP_ANON = _MAP_ANON +const MAP_PRIVATE = _MAP_PRIVATE diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index 5400c1d14e..245cc88aae 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -45,42 +45,6 @@ func LFStackPop(head *uint64) *LFNode { return (*LFNode)(unsafe.Pointer(lfstackpop(head))) } -type ParFor struct { - body func(*ParFor, uint32) - done uint32 - Nthr uint32 - thrseq uint32 - Cnt uint32 - wait bool -} - -func NewParFor(nthrmax uint32) *ParFor { - var desc *ParFor - systemstack(func() { - desc = (*ParFor)(unsafe.Pointer(parforalloc(nthrmax))) - }) - return desc -} - -func ParForSetup(desc *ParFor, nthr, n uint32, wait bool, body func(*ParFor, uint32)) { - systemstack(func() { - parforsetup((*parfor)(unsafe.Pointer(desc)), nthr, n, wait, - *(*func(*parfor, uint32))(unsafe.Pointer(&body))) - }) -} - -func ParForDo(desc *ParFor) { - systemstack(func() { - parfordo((*parfor)(unsafe.Pointer(desc))) - }) -} - -func ParForIters(desc *ParFor, tid uint32) (uint32, uint32) { - desc1 := (*parfor)(unsafe.Pointer(desc)) - pos := desc1.thr[tid].pos - return uint32(pos), uint32(pos >> 32) -} - func GCMask(x interface{}) (ret []byte) { systemstack(func() { ret = getgcmask(x) diff --git a/src/runtime/hashmap_fast.go b/src/runtime/hashmap_fast.go index 519dc77f71..f95ea3e1b7 100644 --- a/src/runtime/hashmap_fast.go +++ b/src/runtime/hashmap_fast.go @@ -216,7 +216,7 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer { if k.len != key.len { continue } - if k.str == key.str || memeq(k.str, key.str, uintptr(key.len)) { + if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)) } } @@ -254,7 +254,7 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer { } if keymaybe != bucketCnt { k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*sys.PtrSize)) - if memeq(k.str, key.str, uintptr(key.len)) { + if memequal(k.str, key.str, uintptr(key.len)) { return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+keymaybe*uintptr(t.valuesize)) } } @@ -284,7 +284,7 @@ dohash: if k.len != key.len { continue } - if k.str == key.str || memeq(k.str, key.str, uintptr(key.len)) { + if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)) } } @@ -321,7 +321,7 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) { if k.len != key.len { continue } - if k.str == key.str || memeq(k.str, key.str, uintptr(key.len)) { + if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)), true } } @@ -357,7 +357,7 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) { } if keymaybe != bucketCnt { k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*sys.PtrSize)) - if memeq(k.str, key.str, uintptr(key.len)) { + if memequal(k.str, key.str, uintptr(key.len)) { return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+keymaybe*uintptr(t.valuesize)), true } } @@ -387,7 +387,7 @@ dohash: if k.len != key.len { continue } - if k.str == key.str || memeq(k.str, key.str, uintptr(key.len)) { + if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.valuesize)), true } } diff --git a/src/runtime/iface.go b/src/runtime/iface.go index 71dc865e07..50dff77e42 100644 --- a/src/runtime/iface.go +++ b/src/runtime/iface.go @@ -398,22 +398,6 @@ func assertE2E2(inter *interfacetype, e eface, r *eface) bool { return true } -func ifacethash(i iface) uint32 { - tab := i.tab - if tab == nil { - return 0 - } - return tab._type.hash -} - -func efacethash(e eface) uint32 { - t := e._type - if t == nil { - return 0 - } - return t.hash -} - func iterate_itabs(fn func(*itab)) { for _, h := range &hash { for ; h != nil; h = h.link { diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go index 45086c43cd..1204e8143e 100644 --- a/src/runtime/mbarrier.go +++ b/src/runtime/mbarrier.go @@ -100,7 +100,7 @@ func gcmarkwb_m(slot *uintptr, ptr uintptr) { // related operations. In particular there are times when the GC assumes // that the world is stopped but scheduler related code is still being // executed, dealing with syscalls, dealing with putting gs on runnable -// queues and so forth. This code can not execute write barriers because +// queues and so forth. This code cannot execute write barriers because // the GC might drop them on the floor. Stopping the world involves removing // the p associated with an m. We use the fact that m.p == nil to indicate // that we are in one these critical section and throw if the write is of diff --git a/src/runtime/mem_bsd.go b/src/runtime/mem_bsd.go index 1e388ec728..c3fe6106d7 100644 --- a/src/runtime/mem_bsd.go +++ b/src/runtime/mem_bsd.go @@ -59,9 +59,9 @@ func sysReserve(v unsafe.Pointer, n uintptr, reserved *bool) unsafe.Pointer { return p } -func sysMap(v unsafe.Pointer, n uintptr, reserved bool, sysStat *uint64) { - const _ENOMEM = 12 +const _ENOMEM = 12 +func sysMap(v unsafe.Pointer, n uintptr, reserved bool, sysStat *uint64) { mSysStatInc(sysStat, n) // On 64-bit, we don't actually have v reserved, so tread carefully. diff --git a/src/runtime/mfixalloc.go b/src/runtime/mfixalloc.go index 8653a6a99f..569a304cf4 100644 --- a/src/runtime/mfixalloc.go +++ b/src/runtime/mfixalloc.go @@ -30,8 +30,8 @@ type fixalloc struct { } // A generic linked list of blocks. (Typically the block is bigger than sizeof(MLink).) -// Since assignments to mlink.next will result in a write barrier being preformed -// this can not be used by some of the internal GC structures. For example when +// Since assignments to mlink.next will result in a write barrier being performed +// this cannot be used by some of the internal GC structures. For example when // the sweeper is placing an unmarked object on the free list it does not want the // write barrier to be called since that could result in the object being reachable. type mlink struct { diff --git a/src/runtime/mprof.go b/src/runtime/mprof.go index fc73bbfbe1..3efa375d6a 100644 --- a/src/runtime/mprof.go +++ b/src/runtime/mprof.go @@ -506,9 +506,7 @@ func ThreadCreateProfile(p []StackRecord) (n int, ok bool) { ok = true i := 0 for mp := first; mp != nil; mp = mp.alllink { - for s := range mp.createstack { - p[i].Stack0[s] = uintptr(mp.createstack[s]) - } + p[i].Stack0 = mp.createstack i++ } } diff --git a/src/runtime/mstats.go b/src/runtime/mstats.go index 368687d006..8ae636077b 100644 --- a/src/runtime/mstats.go +++ b/src/runtime/mstats.go @@ -165,7 +165,7 @@ type MemStats struct { // Size of the trailing by_size array differs between Go and C, // and all data after by_size is local to runtime, not exported. -// NumSizeClasses was changed, but we can not change Go struct because of backward compatibility. +// NumSizeClasses was changed, but we cannot change Go struct because of backward compatibility. // sizeof_C_MStats is what C thinks about size of Go struct. var sizeof_C_MStats = unsafe.Offsetof(memstats.by_size) + 61*unsafe.Sizeof(memstats.by_size[0]) @@ -192,7 +192,7 @@ func readmemstats_m(stats *MemStats) { updatememstats(nil) // Size of the trailing by_size array differs between Go and C, - // NumSizeClasses was changed, but we can not change Go struct because of backward compatibility. + // NumSizeClasses was changed, but we cannot change Go struct because of backward compatibility. memmove(unsafe.Pointer(stats), unsafe.Pointer(&memstats), sizeof_C_MStats) // Stack numbers are part of the heap numbers, separate those out for user consumption diff --git a/src/runtime/norace_linux_test.go b/src/runtime/norace_linux_test.go index bbf9d0b413..049801d3fc 100644 --- a/src/runtime/norace_linux_test.go +++ b/src/runtime/norace_linux_test.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. -// The file contains tests that can not run under race detector for some reason. +// The file contains tests that cannot run under race detector for some reason. // +build !race package runtime_test diff --git a/src/runtime/norace_test.go b/src/runtime/norace_test.go index 3681bf190d..e9b39b2f45 100644 --- a/src/runtime/norace_test.go +++ b/src/runtime/norace_test.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. -// The file contains tests that can not run under race detector for some reason. +// The file contains tests that cannot run under race detector for some reason. // +build !race package runtime_test diff --git a/src/runtime/os1_darwin.go b/src/runtime/os1_darwin.go index 5c00407b2f..19bb0f16e0 100644 --- a/src/runtime/os1_darwin.go +++ b/src/runtime/os1_darwin.go @@ -157,7 +157,7 @@ func sigblock() { } // Called to initialize a new m (including the bootstrap m). -// Called on the new thread, can not allocate memory. +// Called on the new thread, cannot allocate memory. func minit() { // Initialize signal handling. _g_ := getg() diff --git a/src/runtime/os1_dragonfly.go b/src/runtime/os1_dragonfly.go index bf3e1ccb83..7e4f84e6a3 100644 --- a/src/runtime/os1_dragonfly.go +++ b/src/runtime/os1_dragonfly.go @@ -133,7 +133,7 @@ func sigblock() { } // Called to initialize a new m (including the bootstrap m). -// Called on the new thread, can not allocate memory. +// Called on the new thread, cannot allocate memory. func minit() { _g_ := getg() diff --git a/src/runtime/os1_freebsd.go b/src/runtime/os1_freebsd.go index 79d995476e..f00fdf4389 100644 --- a/src/runtime/os1_freebsd.go +++ b/src/runtime/os1_freebsd.go @@ -136,7 +136,7 @@ func sigblock() { } // Called to initialize a new m (including the bootstrap m). -// Called on the new thread, can not allocate memory. +// Called on the new thread, cannot allocate memory. func minit() { _g_ := getg() diff --git a/src/runtime/os1_linux.go b/src/runtime/os1_linux.go index b38cfc14f9..2d53b934f5 100644 --- a/src/runtime/os1_linux.go +++ b/src/runtime/os1_linux.go @@ -225,7 +225,7 @@ func sigblock() { func gettid() uint32 // Called to initialize a new m (including the bootstrap m). -// Called on the new thread, can not allocate memory. +// Called on the new thread, cannot allocate memory. func minit() { // Initialize signal handling. _g_ := getg() diff --git a/src/runtime/os1_nacl.go b/src/runtime/os1_nacl.go index dab205de6a..5526d906d8 100644 --- a/src/runtime/os1_nacl.go +++ b/src/runtime/os1_nacl.go @@ -30,7 +30,7 @@ func sigblock() { } // Called to initialize a new m (including the bootstrap m). -// Called on the new thread, can not allocate memory. +// Called on the new thread, cannot allocate memory. func minit() { _g_ := getg() diff --git a/src/runtime/os1_netbsd.go b/src/runtime/os1_netbsd.go index eab8eb8702..e32df9585c 100644 --- a/src/runtime/os1_netbsd.go +++ b/src/runtime/os1_netbsd.go @@ -167,7 +167,7 @@ func sigblock() { } // Called to initialize a new m (including the bootstrap m). -// Called on the new thread, can not allocate memory. +// Called on the new thread, cannot allocate memory. func minit() { _g_ := getg() _g_.m.procid = uint64(lwp_self()) diff --git a/src/runtime/os1_plan9.go b/src/runtime/os1_plan9.go index 7506d591df..9911077911 100644 --- a/src/runtime/os1_plan9.go +++ b/src/runtime/os1_plan9.go @@ -33,7 +33,7 @@ func sigblock() { } // Called to initialize a new m (including the bootstrap m). -// Called on the new thread, can not allocate memory. +// Called on the new thread, cannot allocate memory. func minit() { // Mask all SSE floating-point exceptions // when running on the 64-bit kernel. diff --git a/src/runtime/os1_windows.go b/src/runtime/os1_windows.go index a28e11e088..8d46bca36d 100644 --- a/src/runtime/os1_windows.go +++ b/src/runtime/os1_windows.go @@ -399,7 +399,7 @@ func sigblock() { } // Called to initialize a new m (including the bootstrap m). -// Called on the new thread, can not allocate memory. +// Called on the new thread, cannot allocate memory. func minit() { var thandle uintptr stdcall7(_DuplicateHandle, currentProcess, currentThread, currentProcess, uintptr(unsafe.Pointer(&thandle)), 0, 0, _DUPLICATE_SAME_ACCESS) diff --git a/src/runtime/os2_nacl.go b/src/runtime/os2_nacl.go index 0c91e0f737..d8c88db0ad 100644 --- a/src/runtime/os2_nacl.go +++ b/src/runtime/os2_nacl.go @@ -10,18 +10,19 @@ const ( // native_client/src/trusted/service_runtime/include/sys/errno.h // The errors are mainly copied from Linux. - _EPERM = 1 /* Operation not permitted */ - _ENOENT = 2 /* No such file or directory */ - _ESRCH = 3 /* No such process */ - _EINTR = 4 /* Interrupted system call */ - _EIO = 5 /* I/O error */ - _ENXIO = 6 /* No such device or address */ - _E2BIG = 7 /* Argument list too long */ - _ENOEXEC = 8 /* Exec format error */ - _EBADF = 9 /* Bad file number */ - _ECHILD = 10 /* No child processes */ - _EAGAIN = 11 /* Try again */ - _ENOMEM = 12 /* Out of memory */ + _EPERM = 1 /* Operation not permitted */ + _ENOENT = 2 /* No such file or directory */ + _ESRCH = 3 /* No such process */ + _EINTR = 4 /* Interrupted system call */ + _EIO = 5 /* I/O error */ + _ENXIO = 6 /* No such device or address */ + _E2BIG = 7 /* Argument list too long */ + _ENOEXEC = 8 /* Exec format error */ + _EBADF = 9 /* Bad file number */ + _ECHILD = 10 /* No child processes */ + _EAGAIN = 11 /* Try again */ + // _ENOMEM is defined in mem_bsd.go for nacl. + // _ENOMEM = 12 /* Out of memory */ _EACCES = 13 /* Permission denied */ _EFAULT = 14 /* Bad address */ _EBUSY = 16 /* Device or resource busy */ diff --git a/src/runtime/os3_solaris.go b/src/runtime/os3_solaris.go index 7bda07bd4a..fdc817d3f7 100644 --- a/src/runtime/os3_solaris.go +++ b/src/runtime/os3_solaris.go @@ -208,7 +208,7 @@ func sigblock() { } // Called to initialize a new m (including the bootstrap m). -// Called on the new thread, can not allocate memory. +// Called on the new thread, cannot allocate memory. func minit() { _g_ := getg() asmcgocall(unsafe.Pointer(funcPC(miniterrno)), unsafe.Pointer(&libc____errno)) @@ -442,7 +442,21 @@ func madvise(addr unsafe.Pointer, n uintptr, flags int32) { //go:nosplit func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) unsafe.Pointer { - return unsafe.Pointer(sysvicall6(&libc_mmap, uintptr(addr), uintptr(n), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(off))) + p, err := doMmap(uintptr(addr), n, uintptr(prot), uintptr(flags), uintptr(fd), uintptr(off)) + if p == ^uintptr(0) { + return unsafe.Pointer(err) + } + return unsafe.Pointer(p) +} + +//go:nosplit +func doMmap(addr, n, prot, flags, fd, off uintptr) (uintptr, uintptr) { + var libcall libcall + libcall.fn = uintptr(unsafe.Pointer(&libc_mmap)) + libcall.n = 6 + libcall.args = uintptr(noescape(unsafe.Pointer(&addr))) + asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&libcall)) + return libcall.r1, libcall.err } //go:nosplit diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go index b8257768ac..78557759cc 100644 --- a/src/runtime/os_darwin.go +++ b/src/runtime/os_darwin.go @@ -7,6 +7,7 @@ package runtime import "unsafe" type mOS struct { + machport uint32 // return address for mach ipc waitsema uint32 // semaphore for parking on locks } diff --git a/src/runtime/panic.go b/src/runtime/panic.go index ba07330e35..349e997395 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -333,6 +333,21 @@ func Goexit() { goexit1() } +// Call all Error and String methods before freezing the world. +// Used when crashing with panicking. +// This must match types handled by printany. +func preprintpanics(p *_panic) { + for p != nil { + switch v := p.arg.(type) { + case error: + p.arg = v.Error() + case stringer: + p.arg = v.String() + } + p = p.link + } +} + // Print all currently active panics. Used when crashing. func printpanics(p *_panic) { if p.link != nil { @@ -459,6 +474,10 @@ func gopanic(e interface{}) { } // ran out of deferred calls - old-school panic now + // Because it is unsafe to call arbitrary user code after freezing + // the world, we call preprintpanics to invoke all necessary Error + // and String methods to prepare the panic strings before startpanic. + preprintpanics(gp._panic) startpanic() printpanics(gp._panic) dopanic(0) // should not return diff --git a/src/runtime/parfor.go b/src/runtime/parfor.go deleted file mode 100644 index 9e11cb3e12..0000000000 --- a/src/runtime/parfor.go +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2012 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. - -// Parallel for algorithm. - -package runtime - -import ( - "runtime/internal/atomic" - "runtime/internal/sys" -) - -// A parfor holds state for the parallel for operation. -type parfor struct { - body func(*parfor, uint32) // executed for each element - done uint32 // number of idle threads - nthr uint32 // total number of threads - thrseq uint32 // thread id sequencer - cnt uint32 // iteration space [0, cnt) - wait bool // if true, wait while all threads finish processing, - // otherwise parfor may return while other threads are still working - - thr []parforthread // thread descriptors - - // stats - nsteal uint64 - nstealcnt uint64 - nprocyield uint64 - nosyield uint64 - nsleep uint64 -} - -// A parforthread holds state for a single thread in the parallel for. -type parforthread struct { - // the thread's iteration space [32lsb, 32msb) - pos uint64 - // stats - nsteal uint64 - nstealcnt uint64 - nprocyield uint64 - nosyield uint64 - nsleep uint64 - pad [sys.CacheLineSize]byte -} - -func parforalloc(nthrmax uint32) *parfor { - return &parfor{ - thr: make([]parforthread, nthrmax), - } -} - -// Parforsetup initializes desc for a parallel for operation with nthr -// threads executing n jobs. -// -// On return the nthr threads are each expected to call parfordo(desc) -// to run the operation. During those calls, for each i in [0, n), one -// thread will be used invoke body(desc, i). -// If wait is true, no parfordo will return until all work has been completed. -// If wait is false, parfordo may return when there is a small amount -// of work left, under the assumption that another thread has that -// work well in hand. -func parforsetup(desc *parfor, nthr, n uint32, wait bool, body func(*parfor, uint32)) { - if desc == nil || nthr == 0 || nthr > uint32(len(desc.thr)) || body == nil { - print("desc=", desc, " nthr=", nthr, " count=", n, " body=", body, "\n") - throw("parfor: invalid args") - } - - desc.body = body - desc.done = 0 - desc.nthr = nthr - desc.thrseq = 0 - desc.cnt = n - desc.wait = wait - desc.nsteal = 0 - desc.nstealcnt = 0 - desc.nprocyield = 0 - desc.nosyield = 0 - desc.nsleep = 0 - - for i := range desc.thr { - begin := uint32(uint64(n) * uint64(i) / uint64(nthr)) - end := uint32(uint64(n) * uint64(i+1) / uint64(nthr)) - desc.thr[i].pos = uint64(begin) | uint64(end)<<32 - } -} - -func parfordo(desc *parfor) { - // Obtain 0-based thread index. - tid := atomic.Xadd(&desc.thrseq, 1) - 1 - if tid >= desc.nthr { - print("tid=", tid, " nthr=", desc.nthr, "\n") - throw("parfor: invalid tid") - } - - // If single-threaded, just execute the for serially. - body := desc.body - if desc.nthr == 1 { - for i := uint32(0); i < desc.cnt; i++ { - body(desc, i) - } - return - } - - me := &desc.thr[tid] - mypos := &me.pos - for { - for { - // While there is local work, - // bump low index and execute the iteration. - pos := atomic.Xadd64(mypos, 1) - begin := uint32(pos) - 1 - end := uint32(pos >> 32) - if begin < end { - body(desc, begin) - continue - } - break - } - - // Out of work, need to steal something. - idle := false - for try := uint32(0); ; try++ { - // If we don't see any work for long enough, - // increment the done counter... - if try > desc.nthr*4 && !idle { - idle = true - atomic.Xadd(&desc.done, 1) - } - - // ...if all threads have incremented the counter, - // we are done. - extra := uint32(0) - if !idle { - extra = 1 - } - if desc.done+extra == desc.nthr { - if !idle { - atomic.Xadd(&desc.done, 1) - } - goto exit - } - - // Choose a random victim for stealing. - var begin, end uint32 - victim := fastrand1() % (desc.nthr - 1) - if victim >= tid { - victim++ - } - victimpos := &desc.thr[victim].pos - for { - // See if it has any work. - pos := atomic.Load64(victimpos) - begin = uint32(pos) - end = uint32(pos >> 32) - if begin+1 >= end { - end = 0 - begin = end - break - } - if idle { - atomic.Xadd(&desc.done, -1) - idle = false - } - begin2 := begin + (end-begin)/2 - newpos := uint64(begin) | uint64(begin2)<<32 - if atomic.Cas64(victimpos, pos, newpos) { - begin = begin2 - break - } - } - if begin < end { - // Has successfully stolen some work. - if idle { - throw("parfor: should not be idle") - } - atomic.Store64(mypos, uint64(begin)|uint64(end)<<32) - me.nsteal++ - me.nstealcnt += uint64(end) - uint64(begin) - break - } - - // Backoff. - if try < desc.nthr { - // nothing - } else if try < 4*desc.nthr { - me.nprocyield++ - procyield(20) - } else if !desc.wait { - // If a caller asked not to wait for the others, exit now - // (assume that most work is already done at this point). - if !idle { - atomic.Xadd(&desc.done, 1) - } - goto exit - } else if try < 6*desc.nthr { - me.nosyield++ - osyield() - } else { - me.nsleep++ - usleep(1) - } - } - } - -exit: - atomic.Xadd64(&desc.nsteal, int64(me.nsteal)) - atomic.Xadd64(&desc.nstealcnt, int64(me.nstealcnt)) - atomic.Xadd64(&desc.nprocyield, int64(me.nprocyield)) - atomic.Xadd64(&desc.nosyield, int64(me.nosyield)) - atomic.Xadd64(&desc.nsleep, int64(me.nsleep)) - me.nsteal = 0 - me.nstealcnt = 0 - me.nprocyield = 0 - me.nosyield = 0 - me.nsleep = 0 -} diff --git a/src/runtime/parfor_test.go b/src/runtime/parfor_test.go deleted file mode 100644 index 5d22aecc9b..0000000000 --- a/src/runtime/parfor_test.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// The race detector does not understand ParFor synchronization. -// +build !race - -package runtime_test - -import ( - . "runtime" - "testing" -) - -// Simple serial sanity test for parallelfor. -func TestParFor(t *testing.T) { - const P = 1 - const N = 20 - data := make([]uint64, N) - for i := uint64(0); i < N; i++ { - data[i] = i - } - desc := NewParFor(P) - ParForSetup(desc, P, N, true, func(desc *ParFor, i uint32) { - data[i] = data[i]*data[i] + 1 - }) - ParForDo(desc) - for i := uint64(0); i < N; i++ { - if data[i] != i*i+1 { - t.Fatalf("Wrong element %d: %d", i, data[i]) - } - } -} - -// Test that nonblocking parallelfor does not block. -func TestParFor2(t *testing.T) { - const P = 7 - const N = 1003 - data := make([]uint64, N) - for i := uint64(0); i < N; i++ { - data[i] = i - } - desc := NewParFor(P) - ParForSetup(desc, P, N, false, func(desc *ParFor, i uint32) { - data[i] = data[i]*data[i] + 1 - }) - for p := 0; p < P; p++ { - ParForDo(desc) - } - for i := uint64(0); i < N; i++ { - if data[i] != i*i+1 { - t.Fatalf("Wrong element %d: %d", i, data[i]) - } - } -} - -// Test that iterations are properly distributed. -func TestParForSetup(t *testing.T) { - const P = 11 - const N = 101 - desc := NewParFor(P) - for n := uint32(0); n < N; n++ { - for p := uint32(1); p <= P; p++ { - ParForSetup(desc, p, n, true, func(desc *ParFor, i uint32) {}) - sum := uint32(0) - size0 := uint32(0) - end0 := uint32(0) - for i := uint32(0); i < p; i++ { - begin, end := ParForIters(desc, i) - size := end - begin - sum += size - if i == 0 { - size0 = size - if begin != 0 { - t.Fatalf("incorrect begin: %d (n=%d, p=%d)", begin, n, p) - } - } else { - if size != size0 && size != size0+1 { - t.Fatalf("incorrect size: %d/%d (n=%d, p=%d)", size, size0, n, p) - } - if begin != end0 { - t.Fatalf("incorrect begin/end: %d/%d (n=%d, p=%d)", begin, end0, n, p) - } - } - end0 = end - } - if sum != n { - t.Fatalf("incorrect sum: %d/%d (p=%d)", sum, n, p) - } - } - } -} - -// Test parallel parallelfor. -func TestParForParallel(t *testing.T) { - N := uint64(1e7) - if testing.Short() { - N /= 10 - } - data := make([]uint64, N) - for i := uint64(0); i < N; i++ { - data[i] = i - } - P := GOMAXPROCS(-1) - c := make(chan bool, P) - desc := NewParFor(uint32(P)) - ParForSetup(desc, uint32(P), uint32(N), false, func(desc *ParFor, i uint32) { - data[i] = data[i]*data[i] + 1 - }) - for p := 1; p < P; p++ { - go func() { - ParForDo(desc) - c <- true - }() - } - ParForDo(desc) - for p := 1; p < P; p++ { - <-c - } - for i := uint64(0); i < N; i++ { - if data[i] != i*i+1 { - t.Fatalf("Wrong element %d: %d", i, data[i]) - } - } - - data, desc = nil, nil - GC() -} diff --git a/src/runtime/pprof/pprof.go b/src/runtime/pprof/pprof.go index 7d677cb64e..e09a33d5d9 100644 --- a/src/runtime/pprof/pprof.go +++ b/src/runtime/pprof/pprof.go @@ -346,7 +346,7 @@ func printStackRecord(w io.Writer, stk []uintptr, allFrames bool) { name := f.Name() // Hide runtime.goexit and any runtime functions at the beginning. // This is useful mainly for allocation traces. - wasPanic = name == "runtime.panic" + wasPanic = name == "runtime.gopanic" if name == "runtime.goexit" || !show && strings.HasPrefix(name, "runtime.") { continue } diff --git a/src/runtime/proc.go b/src/runtime/proc.go index d1f5088b50..389917916f 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -2406,7 +2406,7 @@ func entersyscallblock_handoff() { // The goroutine g exited its system call. // Arrange for it to run on a cpu again. // This is called only from the go syscall library, not -// from the low-level system calls used by the +// from the low-level system calls used by the runtime. //go:nosplit func exitsyscall(dummy int32) { _g_ := getg() diff --git a/src/runtime/runtime-lldb_test.go b/src/runtime/runtime-lldb_test.go new file mode 100644 index 0000000000..2bd91c1ec0 --- /dev/null +++ b/src/runtime/runtime-lldb_test.go @@ -0,0 +1,262 @@ +// Copyright 2016 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 runtime_test + +import ( + "debug/elf" + "debug/macho" + "encoding/binary" + "internal/testenv" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +var lldbPath string + +func checkLldbPython(t *testing.T) { + cmd := exec.Command("lldb", "-P") + out, err := cmd.CombinedOutput() + if err != nil { + t.Skipf("skipping due to issue running lldb: %v\n%s", err, out) + } + lldbPath = strings.TrimSpace(string(out)) + + cmd = exec.Command("/usr/bin/python2.7", "-c", "import sys;sys.path.append(sys.argv[1]);import lldb; print('go lldb python support')", lldbPath) + out, err = cmd.CombinedOutput() + + if err != nil { + t.Skipf("skipping due to issue running python: %v\n%s", err, out) + } + if string(out) != "go lldb python support\n" { + t.Skipf("skipping due to lack of python lldb support: %s", out) + } + + if runtime.GOOS == "darwin" { + // Try to see if we have debugging permissions. + cmd = exec.Command("/usr/sbin/DevToolsSecurity", "-status") + out, err = cmd.CombinedOutput() + if err != nil { + t.Skipf("DevToolsSecurity failed: %v", err) + } else if !strings.Contains(string(out), "enabled") { + t.Skip(string(out)) + } + cmd = exec.Command("/usr/bin/groups") + out, err = cmd.CombinedOutput() + if err != nil { + t.Skipf("groups failed: %v", err) + } else if !strings.Contains(string(out), "_developer") { + t.Skip("Not in _developer group") + } + } +} + +const lldbHelloSource = ` +package main +import "fmt" +func main() { + mapvar := make(map[string]string,5) + mapvar["abc"] = "def" + mapvar["ghi"] = "jkl" + intvar := 42 + ptrvar := &intvar + fmt.Println("hi") // line 10 + _ = ptrvar +} +` + +const lldbScriptSource = ` +import sys +sys.path.append(sys.argv[1]) +import lldb +import os + +TIMEOUT_SECS = 5 + +debugger = lldb.SBDebugger.Create() +debugger.SetAsync(True) +target = debugger.CreateTargetWithFileAndArch("a.exe", None) +if target: + print "Created target" + main_bp = target.BreakpointCreateByLocation("main.go", 10) + if main_bp: + print "Created breakpoint" + process = target.LaunchSimple(None, None, os.getcwd()) + if process: + print "Process launched" + listener = debugger.GetListener() + process.broadcaster.AddListener(listener, lldb.SBProcess.eBroadcastBitStateChanged) + while True: + event = lldb.SBEvent() + if listener.WaitForEvent(TIMEOUT_SECS, event): + if lldb.SBProcess.GetRestartedFromEvent(event): + continue + state = process.GetState() + if state in [lldb.eStateUnloaded, lldb.eStateLaunching, lldb.eStateRunning]: + continue + else: + print "Timeout launching" + break + if state == lldb.eStateStopped: + for t in process.threads: + if t.GetStopReason() == lldb.eStopReasonBreakpoint: + print "Hit breakpoint" + frame = t.GetFrameAtIndex(0) + if frame: + if frame.line_entry: + print "Stopped at %s:%d" % (frame.line_entry.file.basename, frame.line_entry.line) + if frame.function: + print "Stopped in %s" % (frame.function.name,) + var = frame.FindVariable('intvar') + if var: + print "intvar = %s" % (var.GetValue(),) + else: + print "no intvar" + else: + print "Process state", state + process.Destroy() +else: + print "Failed to create target a.exe" + +lldb.SBDebugger.Destroy(debugger) +sys.exit() +` + +const expectedLldbOutput = `Created target +Created breakpoint +Process launched +Hit breakpoint +Stopped at main.go:10 +Stopped in main.main +intvar = 42 +` + +func TestLldbPython(t *testing.T) { + testenv.MustHaveGoBuild(t) + if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final { + t.Skip("gdb test can fail with GOROOT_FINAL pending") + } + + checkLldbPython(t) + + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatalf("failed to create temp directory: %v", err) + } + defer os.RemoveAll(dir) + + src := filepath.Join(dir, "main.go") + err = ioutil.WriteFile(src, []byte(lldbHelloSource), 0644) + if err != nil { + t.Fatalf("failed to create file: %v", err) + } + + cmd := exec.Command("go", "build", "-gcflags", "-N -l", "-o", "a.exe") + cmd.Dir = dir + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("building source %v\n%s", err, out) + } + + src = filepath.Join(dir, "script.py") + err = ioutil.WriteFile(src, []byte(lldbScriptSource), 0755) + if err != nil { + t.Fatalf("failed to create script: %v", err) + } + + cmd = exec.Command("/usr/bin/python2.7", "script.py", lldbPath) + cmd.Dir = dir + got, _ := cmd.CombinedOutput() + + if string(got) != expectedLldbOutput { + if strings.Contains(string(got), "Timeout launching") { + t.Skip("Timeout launching") + } + t.Fatalf("Unexpected lldb output:\n%s", got) + } +} + +// Check that aranges are valid even when lldb isn't installed. +func TestDwarfAranges(t *testing.T) { + testenv.MustHaveGoBuild(t) + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatalf("failed to create temp directory: %v", err) + } + defer os.RemoveAll(dir) + + src := filepath.Join(dir, "main.go") + err = ioutil.WriteFile(src, []byte(lldbHelloSource), 0644) + if err != nil { + t.Fatalf("failed to create file: %v", err) + } + + cmd := exec.Command("go", "build", "-o", "a.exe") + cmd.Dir = dir + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("building source %v\n%s", err, out) + } + + filename := filepath.Join(dir, "a.exe") + if f, err := elf.Open(filename); err == nil { + sect := f.Section(".debug_aranges") + if sect == nil { + t.Fatal("Missing aranges section") + } + verifyAranges(t, f.ByteOrder, sect.Open()) + } else if f, err := macho.Open(filename); err == nil { + sect := f.Section("__debug_aranges") + if sect == nil { + t.Fatal("Missing aranges section") + } + verifyAranges(t, f.ByteOrder, sect.Open()) + } else { + t.Skip("Not an elf or macho binary.") + } +} + +func verifyAranges(t *testing.T, byteorder binary.ByteOrder, data io.ReadSeeker) { + var header struct { + UnitLength uint32 // does not include the UnitLength field + Version uint16 + Offset uint32 + AddressSize uint8 + SegmentSize uint8 + } + for { + offset, err := data.Seek(0, 1) + if err != nil { + t.Fatalf("Seek error: %v", err) + } + if err = binary.Read(data, byteorder, &header); err == io.EOF { + return + } else if err != nil { + t.Fatalf("Error reading arange header: %v", err) + } + tupleSize := int64(header.SegmentSize) + 2*int64(header.AddressSize) + lastTupleOffset := offset + int64(header.UnitLength) + 4 - tupleSize + if lastTupleOffset%tupleSize != 0 { + t.Fatalf("Invalid arange length %d, (addr %d, seg %d)", header.UnitLength, header.AddressSize, header.SegmentSize) + } + if _, err = data.Seek(lastTupleOffset, 0); err != nil { + t.Fatalf("Seek error: %v", err) + } + buf := make([]byte, tupleSize) + if n, err := data.Read(buf); err != nil || int64(n) < tupleSize { + t.Fatalf("Read error: %v", err) + } + for _, val := range buf { + if val != 0 { + t.Fatalf("Invalid terminator") + } + } + } +} diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 917fe89d38..6a4dfa17b8 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -311,7 +311,6 @@ type m struct { park note alllink *m // on allm schedlink muintptr - machport uint32 // return address for mach ipc (os x) mcache *mcache lockedg *g createstack [32]uintptr // stack that created this thread. diff --git a/src/runtime/runtime_linux_test.go b/src/runtime/runtime_linux_test.go index 5344ed2051..58c797f1dd 100644 --- a/src/runtime/runtime_linux_test.go +++ b/src/runtime/runtime_linux_test.go @@ -8,6 +8,7 @@ import ( . "runtime" "syscall" "testing" + "unsafe" ) var pid, tid int @@ -27,3 +28,15 @@ func TestLockOSThread(t *testing.T) { t.Fatalf("pid=%d but tid=%d", pid, tid) } } + +// Test that error values are negative. Use address 1 (a misaligned +// pointer) to get -EINVAL. +func TestMincoreErrorSign(t *testing.T) { + var dst byte + v := Mincore(unsafe.Pointer(uintptr(1)), 1, &dst) + + const EINVAL = 0x16 + if v != -EINVAL { + t.Errorf("mincore = %v, want %v", v, -EINVAL) + } +} diff --git a/src/runtime/runtime_mmap_test.go b/src/runtime/runtime_mmap_test.go new file mode 100644 index 0000000000..ff5e733cb0 --- /dev/null +++ b/src/runtime/runtime_mmap_test.go @@ -0,0 +1,30 @@ +// Copyright 2016 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. + +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris + +package runtime_test + +import ( + "runtime" + "runtime/internal/sys" + "testing" +) + +// Test that the error value returned by mmap is positive, as that is +// what the code in mem_bsd.go, mem_darwin.go, and mem_linux.go expects. +// See the uses of ENOMEM in sysMap in those files. +func TestMmapErrorSign(t *testing.T) { + p := runtime.Mmap(nil, ^uintptr(0)&^(sys.PhysPageSize-1), 0, runtime.MAP_ANON|runtime.MAP_PRIVATE, -1, 0) + + // The runtime.mmap function is nosplit, but t.Errorf is not. + // Reset the pointer so that we don't get an "invalid stack + // pointer" error from t.Errorf if we call it. + v := uintptr(p) + p = nil + + if v != runtime.ENOMEM { + t.Errorf("mmap = %v, want %v", v, runtime.ENOMEM) + } +} diff --git a/src/runtime/string_test.go b/src/runtime/string_test.go index 150a25520a..37b75c1a89 100644 --- a/src/runtime/string_test.go +++ b/src/runtime/string_test.go @@ -102,6 +102,17 @@ func BenchmarkRuneIterate2(b *testing.B) { } } +func BenchmarkArrayEqual(b *testing.B) { + a1 := [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} + a2 := [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} + b.ResetTimer() + for i := 0; i < b.N; i++ { + if a1 != a2 { + b.Fatal("not equal") + } + } +} + func TestStringW(t *testing.T) { strings := []string{ "hello", diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go index f060182c22..6c28fd2e78 100644 --- a/src/runtime/stubs.go +++ b/src/runtime/stubs.go @@ -85,7 +85,7 @@ func fastrand1() uint32 // in asm_*.s //go:noescape -func memeq(a, b unsafe.Pointer, size uintptr) bool +func memequal(a, b unsafe.Pointer, size uintptr) bool // noescape hides a pointer from escape analysis. noescape is // the identity function but escape analysis doesn't think the diff --git a/src/runtime/sys_darwin_arm.s b/src/runtime/sys_darwin_arm.s index 82a8db9914..6b6437dddd 100644 --- a/src/runtime/sys_darwin_arm.s +++ b/src/runtime/sys_darwin_arm.s @@ -261,7 +261,7 @@ cont: MOVW R1, 24(R6) // switch stack and g - MOVW R6, R13 // sigtramp can not re-entrant, so no need to back up R13. + MOVW R6, R13 // sigtramp is not re-entrant, so no need to back up R13. MOVW R5, g BL (R0) diff --git a/src/runtime/sys_darwin_arm64.s b/src/runtime/sys_darwin_arm64.s index d0034d5a33..a3b851d2fc 100644 --- a/src/runtime/sys_darwin_arm64.s +++ b/src/runtime/sys_darwin_arm64.s @@ -245,7 +245,7 @@ cont: MOVD R1, 48(R6) // switch stack and g - MOVD R6, RSP // sigtramp can not re-entrant, so no need to back up RSP. + MOVD R6, RSP // sigtramp is not re-entrant, so no need to back up RSP. MOVD R5, g BL (R0) diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s index aed85cb0aa..f407078176 100644 --- a/src/runtime/sys_linux_amd64.s +++ b/src/runtime/sys_linux_amd64.s @@ -258,7 +258,7 @@ TEXT runtime·sysMmap(SB),NOSPLIT,$0 // Call the function stored in _cgo_mmap using the GCC calling convention. // This must be called on the system stack. -TEXT runtime·callCgoMmap(SB),NOSPLIT,$0 +TEXT runtime·callCgoMmap(SB),NOSPLIT,$16 MOVQ addr+0(FP), DI MOVQ n+8(FP), SI MOVL prot+16(FP), DX @@ -266,7 +266,11 @@ TEXT runtime·callCgoMmap(SB),NOSPLIT,$0 MOVL fd+24(FP), R8 MOVL off+28(FP), R9 MOVQ _cgo_mmap(SB), AX + MOVQ SP, BX + ANDQ $~15, SP // alignment as per amd64 psABI + MOVQ BX, 0(SP) CALL AX + MOVQ 0(SP), SP MOVQ AX, ret+32(FP) RET diff --git a/src/runtime/sys_linux_arm64.s b/src/runtime/sys_linux_arm64.s index ca0e07aaa4..94c101a3d4 100644 --- a/src/runtime/sys_linux_arm64.s +++ b/src/runtime/sys_linux_arm64.s @@ -269,6 +269,9 @@ TEXT runtime·mmap(SB),NOSPLIT,$-8 MOVD $SYS_mmap, R8 SVC + CMN $4095, R0 + BCC 2(PC) + NEG R0,R0 MOVD R0, ret+32(FP) RET diff --git a/src/runtime/sys_linux_mips64x.s b/src/runtime/sys_linux_mips64x.s index 6ccb38f90b..26437ddde4 100644 --- a/src/runtime/sys_linux_mips64x.s +++ b/src/runtime/sys_linux_mips64x.s @@ -168,6 +168,7 @@ TEXT runtime·mincore(SB),NOSPLIT,$-8-28 MOVV dst+16(FP), R6 MOVV $SYS_mincore, R2 SYSCALL + SUBVU R2, R0, R2 // caller expects negative errno MOVW R2, ret+24(FP) RET diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s index ba410c51b6..d063e025a6 100644 --- a/src/runtime/sys_linux_ppc64x.s +++ b/src/runtime/sys_linux_ppc64x.s @@ -153,6 +153,7 @@ TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28 MOVD n+8(FP), R4 MOVD dst+16(FP), R5 SYSCALL $SYS_mincore + NEG R3 // caller expects negative errno MOVW R3, ret+24(FP) RET diff --git a/src/runtime/sys_nacl_386.s b/src/runtime/sys_nacl_386.s index bf2d36ec85..e69a0b7bfe 100644 --- a/src/runtime/sys_nacl_386.s +++ b/src/runtime/sys_nacl_386.s @@ -227,6 +227,9 @@ TEXT runtime·mmap(SB),NOSPLIT,$32 LEAL 24(SP), AX MOVL AX, 20(SP) NACL_SYSCALL(SYS_mmap) + CMPL AX, $-4095 + JNA 2(PC) + NEGL AX MOVL AX, ret+24(FP) RET diff --git a/src/runtime/testdata/testprog/deadlock.go b/src/runtime/testdata/testprog/deadlock.go index 7f0a0cd1e0..ff9c82d61b 100644 --- a/src/runtime/testdata/testprog/deadlock.go +++ b/src/runtime/testdata/testprog/deadlock.go @@ -29,7 +29,9 @@ func init() { register("GoexitInPanic", GoexitInPanic) register("PanicAfterGoexit", PanicAfterGoexit) register("RecoveredPanicAfterGoexit", RecoveredPanicAfterGoexit) - + register("PanicTraceback", PanicTraceback) + register("GoschedInPanic", GoschedInPanic) + register("SyscallInPanic", SyscallInPanic) } func SimpleDeadlock() { @@ -152,6 +154,29 @@ func GoexitInPanic() { runtime.Goexit() } +type errorThatGosched struct{} + +func (errorThatGosched) Error() string { + runtime.Gosched() + return "errorThatGosched" +} + +func GoschedInPanic() { + panic(errorThatGosched{}) +} + +type errorThatPrint struct{} + +func (errorThatPrint) Error() string { + fmt.Println("1") + fmt.Println("2") + return "3" +} + +func SyscallInPanic() { + panic(errorThatPrint{}) +} + func PanicAfterGoexit() { defer func() { panic("hello") @@ -171,3 +196,21 @@ func RecoveredPanicAfterGoexit() { }() runtime.Goexit() } + +func PanicTraceback() { + pt1() +} + +func pt1() { + defer func() { + panic("panic pt1") + }() + pt2() +} + +func pt2() { + defer func() { + panic("panic pt2") + }() + panic("hello") +} diff --git a/src/runtime/testdata/testprogcgo/cgo.go b/src/runtime/testdata/testprogcgo/cgo.go index cf1af8268c..5d2550dbb0 100644 --- a/src/runtime/testdata/testprogcgo/cgo.go +++ b/src/runtime/testdata/testprogcgo/cgo.go @@ -6,17 +6,20 @@ package main /* void foo1(void) {} +void foo2(void* p) {} */ import "C" import ( "fmt" "runtime" "time" + "unsafe" ) func init() { register("CgoSignalDeadlock", CgoSignalDeadlock) register("CgoTraceback", CgoTraceback) + register("CgoCheckBytes", CgoCheckBytes) } func CgoSignalDeadlock() { @@ -78,3 +81,10 @@ func CgoTraceback() { runtime.Stack(buf, true) fmt.Printf("OK\n") } + +func CgoCheckBytes() { + b := make([]byte, 1e6) + for i := 0; i < 1e3; i++ { + C.foo2(unsafe.Pointer(&b[0])) + } +} diff --git a/src/runtime/testdata/testprogcgo/deadlock.go b/src/runtime/testdata/testprogcgo/deadlock.go new file mode 100644 index 0000000000..ac8855af3b --- /dev/null +++ b/src/runtime/testdata/testprogcgo/deadlock.go @@ -0,0 +1,30 @@ +// Copyright 2016 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 + +/* +char *geterror() { + return "cgo error"; +} +*/ +import "C" +import ( + "fmt" +) + +func init() { + register("CgoPanicDeadlock", CgoPanicDeadlock) +} + +type cgoError struct{} + +func (cgoError) Error() string { + fmt.Print("") // necessary to trigger the deadlock + return C.GoString(C.geterror()) +} + +func CgoPanicDeadlock() { + panic(cgoError{}) +} diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index 6559cd7ba3..b4bfe71627 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -380,7 +380,11 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in if (n > 0 || flags&_TraceTrap == 0) && frame.pc > f.entry && !waspanic { tracepc-- } - print(funcname(f), "(") + name := funcname(f) + if name == "runtime.gopanic" { + name = "panic" + } + print(name, "(") argp := (*[100]uintptr)(unsafe.Pointer(frame.argp)) for i := uintptr(0); i < frame.arglen/sys.PtrSize; i++ { if i >= 10 { @@ -617,10 +621,10 @@ func showframe(f *_func, gp *g) bool { level, _, _ := gotraceback() name := funcname(f) - // Special case: always show runtime.panic frame, so that we can + // Special case: always show runtime.gopanic frame, so that we can // see where a panic started in the middle of a stack trace. // See golang.org/issue/5832. - if name == "runtime.panic" { + if name == "runtime.gopanic" { return true } diff --git a/src/runtime/vdso_linux_amd64.go b/src/runtime/vdso_linux_amd64.go index 38914bb2b9..42571e063c 100644 --- a/src/runtime/vdso_linux_amd64.go +++ b/src/runtime/vdso_linux_amd64.go @@ -263,7 +263,7 @@ func vdso_find_version(info *vdso_info, ver *version_key) int32 { def = (*elf64Verdef)(add(unsafe.Pointer(def), uintptr(def.vd_next))) } - return -1 // can not match any version + return -1 // cannot match any version } func vdso_parse_symbols(info *vdso_info, version int32) { diff --git a/src/sort/sort.go b/src/sort/sort.go index ac8f4a661f..5eb45c6d4a 100644 --- a/src/sort/sort.go +++ b/src/sort/sort.go @@ -335,7 +335,7 @@ func StringsAreSorted(a []string) bool { return IsSorted(StringSlice(a)) } // unstable or rely on enough different elements in each step to encode the // performed block rearrangements. See also "In-Place Merging Algorithms", // Denham Coates-Evely, Department of Computer Science, Kings College, -// January 2004 and the reverences in there. +// January 2004 and the references in there. // - Often "optimal" algorithms are optimal in the number of assignments // but Interface has only Swap as operation. diff --git a/src/sync/pool.go b/src/sync/pool.go index 381af0bead..4fb1a1af9d 100644 --- a/src/sync/pool.go +++ b/src/sync/pool.go @@ -149,7 +149,7 @@ func (p *Pool) getSlow() (x interface{}) { func (p *Pool) pin() *poolLocal { pid := runtime_procPin() // In pinSlow we store to localSize and then to local, here we load in opposite order. - // Since we've disabled preemption, GC can not happen in between. + // Since we've disabled preemption, GC cannot happen in between. // Thus here we must observe local at least as large localSize. // We can observe a newer/larger local, it is fine (we must observe its zero-initialized-ness). s := atomic.LoadUintptr(&p.localSize) // load-acquire diff --git a/src/text/scanner/scanner.go b/src/text/scanner/scanner.go index 0155800f34..dd87850fcd 100644 --- a/src/text/scanner/scanner.go +++ b/src/text/scanner/scanner.go @@ -333,7 +333,7 @@ func (s *Scanner) error(msg string) { if !pos.IsValid() { pos = s.Pos() } - fmt.Fprintf(os.Stderr, "%s: %s\n", pos, msg) + fmt.Fprintf(os.Stderr, "text/scanner: %s: %s\n", pos, msg) } func (s *Scanner) isIdentRune(ch rune, i int) bool { diff --git a/src/text/template/exec.go b/src/text/template/exec.go index efe1817173..5ea45a4c53 100644 --- a/src/text/template/exec.go +++ b/src/text/template/exec.go @@ -446,7 +446,7 @@ func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value { switch { case constant.IsComplex: return reflect.ValueOf(constant.Complex128) // incontrovertible. - case constant.IsFloat && !isHexConstant(constant.Text) && strings.IndexAny(constant.Text, ".eE") >= 0: + case constant.IsFloat && !isHexConstant(constant.Text) && strings.ContainsAny(constant.Text, ".eE"): return reflect.ValueOf(constant.Float64) case constant.IsInt: n := int(constant.Int64) diff --git a/src/text/template/funcs.go b/src/text/template/funcs.go index 49e9e7419a..58b8ea372d 100644 --- a/src/text/template/funcs.go +++ b/src/text/template/funcs.go @@ -515,7 +515,7 @@ func HTMLEscape(w io.Writer, b []byte) { // HTMLEscapeString returns the escaped HTML equivalent of the plain text data s. func HTMLEscapeString(s string) string { // Avoid allocation if we can. - if strings.IndexAny(s, `'"&<>`) < 0 { + if !strings.ContainsAny(s, `'"&<>`) { return s } var b bytes.Buffer diff --git a/src/text/template/parse/lex.go b/src/text/template/parse/lex.go index ea93e05142..079c0ea6f7 100644 --- a/src/text/template/parse/lex.go +++ b/src/text/template/parse/lex.go @@ -155,7 +155,7 @@ func (l *lexer) ignore() { // accept consumes the next rune if it's from the valid set. func (l *lexer) accept(valid string) bool { - if strings.IndexRune(valid, l.next()) >= 0 { + if strings.ContainsRune(valid, l.next()) { return true } l.backup() @@ -164,7 +164,7 @@ func (l *lexer) accept(valid string) bool { // acceptRun consumes a run of runes from the valid set. func (l *lexer) acceptRun(valid string) { - for strings.IndexRune(valid, l.next()) >= 0 { + for strings.ContainsRune(valid, l.next()) { } l.backup() } diff --git a/src/time/format_test.go b/src/time/format_test.go index af950a7c25..8c47dbcdd1 100644 --- a/src/time/format_test.go +++ b/src/time/format_test.go @@ -447,7 +447,7 @@ func TestParseErrors(t *testing.T) { _, err := Parse(test.format, test.value) if err == nil { t.Errorf("expected error for %q %q", test.format, test.value) - } else if strings.Index(err.Error(), test.expect) < 0 { + } else if !strings.Contains(err.Error(), test.expect) { t.Errorf("expected error with %q for %q %q; got %s", test.expect, test.format, test.value, err) } } diff --git a/src/unicode/utf16/utf16.go b/src/unicode/utf16/utf16.go index b497500778..276fce9e56 100644 --- a/src/unicode/utf16/utf16.go +++ b/src/unicode/utf16/utf16.go @@ -36,7 +36,7 @@ func IsSurrogate(r rune) bool { // the Unicode replacement code point U+FFFD. func DecodeRune(r1, r2 rune) rune { if surr1 <= r1 && r1 < surr2 && surr2 <= r2 && r2 < surr3 { - return (r1-surr1)<<10 | (r2 - surr2) + 0x10000 + return (r1-surr1)<<10 | (r2 - surr2) + surrSelf } return replacementChar } @@ -88,21 +88,19 @@ func Decode(s []uint16) []rune { n := 0 for i := 0; i < len(s); i++ { switch r := s[i]; { + case r < surr1, surr3 <= r: + // normal rune + a[n] = rune(r) case surr1 <= r && r < surr2 && i+1 < len(s) && surr2 <= s[i+1] && s[i+1] < surr3: // valid surrogate sequence a[n] = DecodeRune(rune(r), rune(s[i+1])) i++ - n++ - case surr1 <= r && r < surr3: + default: // invalid surrogate sequence a[n] = replacementChar - n++ - default: - // normal rune - a[n] = rune(r) - n++ } + n++ } - return a[0:n] + return a[:n] } diff --git a/src/unicode/utf16/utf16_test.go b/src/unicode/utf16/utf16_test.go index 3dca472bbe..e5be504e08 100644 --- a/src/unicode/utf16/utf16_test.go +++ b/src/unicode/utf16/utf16_test.go @@ -147,3 +147,56 @@ func TestIsSurrogate(t *testing.T) { } } } + +func BenchmarkDecodeValidASCII(b *testing.B) { + // "hello world" + data := []uint16{104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100} + for i := 0; i < b.N; i++ { + Decode(data) + } +} + +func BenchmarkDecodeValidJapaneseChars(b *testing.B) { + // "日本語日本語日本語" + data := []uint16{26085, 26412, 35486, 26085, 26412, 35486, 26085, 26412, 35486} + for i := 0; i < b.N; i++ { + Decode(data) + } +} + +func BenchmarkDecodeRune(b *testing.B) { + rs := make([]rune, 10) + // U+1D4D0 to U+1D4D4: MATHEMATICAL BOLD SCRIPT CAPITAL LETTERS + for i, u := range []rune{'𝓐', '𝓑', '𝓒', '𝓓', '𝓔'} { + rs[2*i], rs[2*i+1] = EncodeRune(u) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + for j := 0; j < 5; j++ { + DecodeRune(rs[2*j], rs[2*j+1]) + } + } +} + +func BenchmarkEncodeValidASCII(b *testing.B) { + data := []rune{'h', 'e', 'l', 'l', 'o'} + for i := 0; i < b.N; i++ { + Encode(data) + } +} + +func BenchmarkEncodeValidJapaneseChars(b *testing.B) { + data := []rune{'日', '本', '語'} + for i := 0; i < b.N; i++ { + Encode(data) + } +} + +func BenchmarkEncodeRune(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, u := range []rune{'𝓐', '𝓑', '𝓒', '𝓓', '𝓔'} { + EncodeRune(u) + } + } +} diff --git a/test/escape_iface.go b/test/escape_iface.go index 2b1144ad2c..9149fa1770 100644 --- a/test/escape_iface.go +++ b/test/escape_iface.go @@ -225,3 +225,23 @@ func dotTypeEscape() *T2 { // #11931 T1: *(x.(*T1)), // ERROR "&T2 literal escapes to heap" } } + +func dotTypeEscape2() { // #13805 + { + i := 0 + var v int + var x interface{} = i // ERROR "i does not escape" + *(&v) = x.(int) // ERROR "&v does not escape" + } + { + i := 0 + var x interface{} = i // ERROR "i does not escape" + sink = x.(int) // ERROR "x.\(int\) escapes to heap" + + } + { + i := 0 // ERROR "moved to heap: i" + var x interface{} = &i // ERROR "&i escapes to heap" + sink = x.(*int) // ERROR "x.\(\*int\) escapes to heap" + } +} diff --git a/test/fixedbugs/issue11610.go b/test/fixedbugs/issue11610.go index a326249ed4..56f245dee5 100644 --- a/test/fixedbugs/issue11610.go +++ b/test/fixedbugs/issue11610.go @@ -9,9 +9,9 @@ package a import"" // ERROR "import path is empty" -var? // ERROR "invalid declaration" +var? // ERROR "unexpected \?" -var x int // ERROR "unexpected var" +var x int // ERROR "unexpected var" "cannot declare name" func main() { } diff --git a/test/fixedbugs/issue14331.dir/a.go b/test/fixedbugs/issue14331.dir/a.go new file mode 100644 index 0000000000..1b7f853bc9 --- /dev/null +++ b/test/fixedbugs/issue14331.dir/a.go @@ -0,0 +1,14 @@ +// Copyright 2016 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 a + +var S struct { + Str string `tag` +} + +func F() string { + v := S + return v.Str +} diff --git a/test/fixedbugs/issue14331.dir/b.go b/test/fixedbugs/issue14331.dir/b.go new file mode 100644 index 0000000000..7a0abb2506 --- /dev/null +++ b/test/fixedbugs/issue14331.dir/b.go @@ -0,0 +1,11 @@ +// Copyright 2016 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 b + +import "./a" + +func G() string { + return a.F() +} diff --git a/test/fixedbugs/issue14331.go b/test/fixedbugs/issue14331.go new file mode 100644 index 0000000000..32f3e5156c --- /dev/null +++ b/test/fixedbugs/issue14331.go @@ -0,0 +1,9 @@ +// compiledir + +// Copyright 2016 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. + +// Inline function misses struct tags. + +package ignored diff --git a/test/fixedbugs/issue14405.go b/test/fixedbugs/issue14405.go new file mode 100644 index 0000000000..c2a89464ea --- /dev/null +++ b/test/fixedbugs/issue14405.go @@ -0,0 +1,17 @@ +// compile + +// Copyright 2016 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. + +// Mention of field with large offset in struct literal causes crash +package p + +type T struct { + Slice [1 << 20][]int + Ptr *int +} + +func New(p *int) *T { + return &T{Ptr: p} +} diff --git a/test/linkx_run.go b/test/linkx_run.go index a6c7c67014..440271ac4a 100644 --- a/test/linkx_run.go +++ b/test/linkx_run.go @@ -18,7 +18,7 @@ import ( ) func main() { - test(" ") // old deprecated syntax + // test(" ") // old deprecated & removed syntax test("=") // new syntax } diff --git a/test/writebarrier.go b/test/writebarrier.go index 9b741a60df..dcd20a0225 100644 --- a/test/writebarrier.go +++ b/test/writebarrier.go @@ -144,3 +144,17 @@ type T8 struct { func f16(x []T8, y T8) []T8 { return append(x, y) // ERROR "write barrier" } + +func t1(i interface{}) **int { + // From issue 14306, make sure we have write barriers in a type switch + // where the assigned variable escapes. + switch x := i.(type) { // ERROR "write barrier" + case *int: + return &x + } + switch y := i.(type) { // no write barrier here + case **int: + return y + } + return nil +}