diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index caedbaa0fe6..337f8ccbe7a 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -978,8 +978,8 @@ func typename(t *Type) *Node { } func itabname(t, itype *Type) *Node { - if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() { - Fatalf("itabname %v", t) + if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() || !itype.IsInterface() || itype.IsEmptyInterface() { + Fatalf("itabname(%v, %v)", t, itype) } s := Pkglookup(fmt.Sprintf("%-v,%-v", t, itype), itabpkg) if s.Def == nil { diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go index 743e508bf1b..4666433e531 100644 --- a/src/cmd/compile/internal/gc/sinit.go +++ b/src/cmd/compile/internal/gc/sinit.go @@ -4,9 +4,7 @@ package gc -import ( - "fmt" -) +import "fmt" // static initialization const ( @@ -479,6 +477,67 @@ func staticassign(l *Node, r *Node, out *[]*Node) bool { } else { closuredebugruntimecheck(r) } + + case OCONVIFACE: + // This logic is mirrored in isStaticCompositeLiteral. + // If you change something here, change it there, and vice versa. + + // Determine the underlying concrete type and value we are converting from. + val := r + for val.Op == OCONVIFACE { + val = val.Left + } + if val.Type.IsInterface() { + // val is an interface type. + // If val is nil, we can statically initialize l; + // both words are zero and so there no work to do, so report success. + // If val is non-nil, we have no concrete type to record, + // and we won't be able to statically initialize its value, so report failure. + return Isconst(val, CTNIL) + } + + var itab *Node + if l.Type.IsEmptyInterface() { + itab = typename(val.Type) + } else { + itab = itabname(val.Type, l.Type) + } + + // Create a copy of l to modify while we emit data. + n := *l + + // Emit itab, advance offset. + gdata(&n, itab, Widthptr) + n.Xoffset += int64(Widthptr) + + // Emit data. + if isdirectiface(val.Type) { + if Isconst(val, CTNIL) { + // Nil is zero, nothing to do. + return true + } + // Copy val directly into n. + n.Type = val.Type + setlineno(val) + a := Nod(OXXX, nil, nil) + *a = n + a.Orig = a + if !staticassign(a, val, out) { + *out = append(*out, Nod(OAS, a, val)) + } + } else { + // Construct temp to hold val, write pointer to temp into n. + a := staticname(val.Type) + inittemps[val] = a + if !staticassign(a, val, out) { + *out = append(*out, Nod(OAS, a, val)) + } + ptr := Nod(OADDR, a, nil) + n.Type = Ptrto(val.Type) + gdata(&n, ptr, Widthptr) + } + + return true } //dump("not static", r); @@ -593,6 +652,19 @@ func isStaticCompositeLiteral(n *Node) bool { return true case OLITERAL: return true + case OCONVIFACE: + // See staticassign's OCONVIFACE case for comments. + val := n + for val.Op == OCONVIFACE { + val = val.Left + } + if val.Type.IsInterface() { + return Isconst(val, CTNIL) + } + if isdirectiface(val.Type) && Isconst(val, CTNIL) { + return true + } + return isStaticCompositeLiteral(val) } return false } diff --git a/test/fixedbugs/issue15528.go b/test/fixedbugs/issue15528.go new file mode 100644 index 00000000000..b1f9dfbb5cc --- /dev/null +++ b/test/fixedbugs/issue15528.go @@ -0,0 +1,131 @@ +// run + +// 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 + +import ( + "fmt" + "io" + "os" + "reflect" + "unsafe" +) + +type RWS struct{} + +func (x *RWS) Read(p []byte) (n int, err error) { return } +func (x *RWS) Write(p []byte) (n int, err error) { return } +func (x *RWS) Seek(offset int64, whence int) (n int64, err error) { return } +func (x *RWS) String() string { return "rws" } + +func makeRWS() io.ReadWriteSeeker { return &RWS{} } +func makeStringer() fmt.Stringer { return &RWS{} } + +// Test correct construction of static empty interface values +var efaces = [...]struct { + x interface{} + s string +}{ + {nil, " "}, + {1, "int 1"}, + {int(1), "int 1"}, + {Int(int(2)), "main.Int Int=2"}, + {int(Int(3)), "int 3"}, + {[1]int{2}, "[1]int [2]"}, + {io.Reader(io.ReadWriter(io.ReadWriteSeeker(nil))), " "}, + {io.Reader(io.ReadWriter(io.ReadWriteSeeker(&RWS{}))), "*main.RWS rws"}, + {makeRWS(), "*main.RWS rws"}, + {map[string]string{"here": "there"}, "map[string]string map[here:there]"}, + {chan bool(nil), "chan bool "}, + {unsafe.Pointer(uintptr(0)), "unsafe.Pointer "}, + {(*byte)(nil), "*uint8 "}, + {io.Writer((*os.File)(nil)), "*os.File "}, + {(interface{})(io.Writer((*os.File)(nil))), "*os.File "}, + {fmt.Stringer(Strunger(((*Int)(nil)))), "*main.Int "}, +} + +type Int int + +func (i Int) String() string { return fmt.Sprintf("Int=%d", i) } +func (i Int) Strung() {} + +type Strunger interface { + fmt.Stringer + Strung() +} + +// Test correct construction of static non-empty interface values +var ifaces = [...]struct { + x fmt.Stringer + s string +}{ + {nil, " %!s()"}, + {Int(3), "main.Int 3 Int=3"}, + {Int(int(Int(4))), "main.Int 4 Int=4"}, + {Strunger(Int(5)), "main.Int 5 Int=5"}, + {makeStringer(), "*main.RWS &main.RWS{} rws"}, + {fmt.Stringer(nil), " %!s()"}, + {(*RWS)(nil), "*main.RWS (*main.RWS)(nil) rws"}, +} + +// Test correct handling of direct interface values +var ( + one int = 1 + iptr interface{} = &one + clos int + f interface{} = func() { clos++ } + deep interface{} = [1]struct{ a *[2]byte }{{a: &[2]byte{'z', 'w'}}} + ch interface{} = make(chan bool, 1) +) + +func main() { + var fail bool + for i, test := range efaces { + s := fmt.Sprintf("%[1]T %[1]v", test.x) + if s != test.s { + fmt.Printf("eface(%d)=%q want %q\n", i, s, test.s) + fail = true + } + } + + for i, test := range ifaces { + s := fmt.Sprintf("%[1]T %#[1]v %[1]s", test.x) + if s != test.s { + fmt.Printf("iface(%d)=%q want %q\n", i, s, test.s) + fail = true + } + } + + if got := *(iptr.(*int)); got != 1 { + fmt.Printf("bad int ptr %d\n", got) + fail = true + } + + f.(func())() + f.(func())() + f.(func())() + if clos != 3 { + fmt.Printf("bad closure exec %d\n", clos) + fail = true + } + + if !reflect.DeepEqual(*(deep.([1]struct{ a *[2]byte })[0].a), [2]byte{'z', 'w'}) { + fmt.Printf("bad deep directiface\n") + fail = true + } + + cc := ch.(chan bool) + cc <- true + if got := <-cc; !got { + fmt.Printf("bad chan\n") + fail = true + } + + if fail { + fmt.Println("BUG") + os.Exit(1) + } +}