From 74b761d0998eba2c1f9d504f2e65480a0df149e8 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Thu, 31 Oct 2013 17:59:52 -0400 Subject: [PATCH] go.tools/ssa: clarify spec of (*builder).complit(). Added test for []*map composite literals containing nested literal subelements. This required implementing (reflect.Value).Map{Keys,Index} in ssa/interp. Plus two minor fixes in ssa/interp. R=gri CC=golang-dev https://golang.org/cl/20470043 --- ssa/builder.go | 26 +++++++++---------- ssa/interp/external.go | 2 ++ ssa/interp/interp.go | 3 +++ ssa/interp/reflect.go | 44 +++++++++++++++++++++++++++++++++ ssa/interp/testdata/coverage.go | 15 +++++++++++ ssa/interp/value.go | 2 +- 6 files changed, 76 insertions(+), 16 deletions(-) diff --git a/ssa/builder.go b/ssa/builder.go index 327cbc38..8e94a2ad 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -386,7 +386,7 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { v = fn.addLocal(t, e.Lbrace) } v.Comment = "complit" - b.compLit(fn, v, e, t) // initialize in place + b.compLit(fn, v, e) // initialize in place return &address{addr: v, expr: e} case *ast.ParenExpr: @@ -470,14 +470,13 @@ func (b *builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr) { } if _, ok := loc.(*address); ok { - typ := loc.typ() - if _, ok := typ.Underlying().(*types.Interface); ok { + if _, ok := loc.typ().Underlying().(*types.Interface); ok { // e.g. var x interface{} = T{...} // Can't in-place initialize an interface value. // Fall back to copying. } else { addr := loc.address(fn) - b.compLit(fn, addr, e, typ) // in place + b.compLit(fn, addr, e) // in place emitDebugRef(fn, e, addr, true) return } @@ -1184,10 +1183,13 @@ func (b *builder) arrayLen(fn *Function, elts []ast.Expr) int64 { // Nested composite literals are recursively initialized in place // where possible. // -func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ types.Type) { - // TODO(adonovan): document how and why typ ever differs from - // fn.Pkg.typeOf(e). - +// A CompositeLit may have pointer type only in the recursive (nested) +// case when the type name is implicit. e.g. in []*T{{}}, the inner +// literal has type *T behaves like &T{}. +// In that case, addr must hold a T, not a *T. +// +func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit) { + typ := deref(fn.Pkg.typeOf(e)) switch t := typ.Underlying().(type) { case *types.Struct: for i, e := range e.Elts { @@ -1250,7 +1252,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ typ if t != at { // slice s := &Slice{X: array} s.setPos(e.Lbrace) - s.setType(t) + s.setType(typ) emitStore(fn, addr, fn.emit(s)) } @@ -1270,12 +1272,6 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ typ b.exprInPlace(fn, loc, e.Value) } - case *types.Pointer: - // Pointers can only occur in the recursive case; we - // strip them off in addr() before calling compLit - // again, so that we allocate space for a T not a *T. - panic("compLit(fn, addr, e, *types.Pointer") - default: panic("unexpected CompositeLit type: " + t.String()) } diff --git a/ssa/interp/external.go b/ssa/interp/external.go index 2fe79a94..72c85780 100644 --- a/ssa/interp/external.go +++ b/ssa/interp/external.go @@ -42,6 +42,8 @@ var externals = map[string]externalFn{ "(reflect.Value).IsValid": ext۰reflect۰Value۰IsValid, "(reflect.Value).Kind": ext۰reflect۰Value۰Kind, "(reflect.Value).Len": ext۰reflect۰Value۰Len, + "(reflect.Value).MapIndex": ext۰reflect۰Value۰MapIndex, + "(reflect.Value).MapKeys": ext۰reflect۰Value۰MapKeys, "(reflect.Value).NumField": ext۰reflect۰Value۰NumField, "(reflect.Value).NumMethod": ext۰reflect۰Value۰NumMethod, "(reflect.Value).Pointer": ext۰reflect۰Value۰Pointer, diff --git a/ssa/interp/interp.go b/ssa/interp/interp.go index 5d8cf0fd..7b67f4ca 100644 --- a/ssa/interp/interp.go +++ b/ssa/interp/interp.go @@ -546,6 +546,9 @@ func runFrame(fr *frame) { } fr.panicking = true fr.panic = recover() + if fr.i.mode&EnableTracing != 0 { + fmt.Fprintf(os.Stderr, "Panicking: %s.\n", fr.panic) + } fr.runDefers() fr.block = fr.fn.Recover // recovered panic }() diff --git a/ssa/interp/reflect.go b/ssa/interp/reflect.go index f2048f2d..5735cc92 100644 --- a/ssa/interp/reflect.go +++ b/ssa/interp/reflect.go @@ -306,6 +306,50 @@ func ext۰reflect۰Value۰Len(fn *ssa.Function, args []value) value { return nil // unreachable } +func ext۰reflect۰Value۰MapIndex(fn *ssa.Function, args []value) value { + // Signature: func (reflect.Value) Value + tValue := rV2T(args[0]).t.Underlying().(*types.Map).Key() + k := rV2V(args[1]) + switch m := rV2V(args[0]).(type) { + case map[value]value: + if v, ok := m[k]; ok { + return makeReflectValue(tValue, v) + } + + case *hashmap: + if v := m.lookup(k.(hashable)); v != nil { + return makeReflectValue(tValue, v) + } + + default: + panic(fmt.Sprintf("(reflect.Value).MapIndex(%T, %T)", m, k)) + } + return makeReflectValue(nil, nil) +} + +func ext۰reflect۰Value۰MapKeys(fn *ssa.Function, args []value) value { + // Signature: func (reflect.Value) []Value + var keys []value + tKey := rV2T(args[0]).t.Underlying().(*types.Map).Key() + switch v := rV2V(args[0]).(type) { + case map[value]value: + for k := range v { + keys = append(keys, makeReflectValue(tKey, k)) + } + + case *hashmap: + for _, e := range v.table { + for ; e != nil; e = e.next { + keys = append(keys, makeReflectValue(tKey, e.key)) + } + } + + default: + panic(fmt.Sprintf("(reflect.Value).MapKeys(%T)", v)) + } + return keys +} + func ext۰reflect۰Value۰NumField(fn *ssa.Function, args []value) value { // Signature: func (reflect.Value) int return len(rV2V(args[0]).(structure)) diff --git a/ssa/interp/testdata/coverage.go b/ssa/interp/testdata/coverage.go index cef30bdf..80c426c6 100644 --- a/ssa/interp/testdata/coverage.go +++ b/ssa/interp/testdata/coverage.go @@ -543,3 +543,18 @@ func init() { }() _ = i == j // interface comparison recurses on types } + +// Composite literals + +func init() { + type M map[int]int + m1 := []*M{{1: 1}, &M{2: 2}} + want := "map[1:1] map[2:2]" + if got := fmt.Sprint(*m1[0], *m1[1]); got != want { + panic(got) + } + m2 := []M{{1: 1}, M{2: 2}} + if got := fmt.Sprint(m2[0], m2[1]); got != want { + panic(got) + } +} diff --git a/ssa/interp/value.go b/ssa/interp/value.go index 1630b764..67f05c73 100644 --- a/ssa/interp/value.go +++ b/ssa/interp/value.go @@ -359,7 +359,7 @@ func toWriter(w io.Writer, v value) { case map[value]value: io.WriteString(w, "map[") - sep := " " + sep := "" for k, e := range v { io.WriteString(w, sep) sep = " "