From d9001ef012aabcd28c66a2d1f321078f45a2ffac Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Fri, 22 Feb 2013 00:09:21 -0500 Subject: [PATCH] exp/ssa: cross off a few remaining TODO issues. - append: nothing to do (nonsemantic change). - delete: now performs correct conversion (+ test). - emitCompare: nothing to do. - emitArith (shifts): nothing to do (+ test). - "banish untyped types": give up on that. - real, imag: now do correct conversions. - added comment to interp.go re zero-size values. R=gri CC=golang-dev https://golang.org/cl/7391046 --- src/pkg/exp/ssa/builder.go | 52 +++++++++++++-------- src/pkg/exp/ssa/emit.go | 2 - src/pkg/exp/ssa/interp/interp.go | 4 ++ src/pkg/exp/ssa/interp/testdata/coverage.go | 28 +++++++++++ 4 files changed, 64 insertions(+), 22 deletions(-) diff --git a/src/pkg/exp/ssa/builder.go b/src/pkg/exp/ssa/builder.go index d8c17be500..a00a9e3443 100644 --- a/src/pkg/exp/ssa/builder.go +++ b/src/pkg/exp/ssa/builder.go @@ -31,15 +31,10 @@ package ssa // It uses a mutex so that access from multiple threads is serialized. // TODO(adonovan): fix the following: -// - append, delete details. // - support f(g()) where g has multiple result parameters. -// - finish emitCompare, emitArith. -// - banish "untyped" types everywhere except package/universal constants? // - concurrent SSA code generation of multiple packages. // - consider function-local NamedTypes. // They can have nonempty method-sets due to promotion. Test. -// - polish. -// - tests. import ( "fmt" @@ -58,6 +53,8 @@ var ( tByte = types.Typ[types.Byte] tFloat32 = types.Typ[types.Float32] tFloat64 = types.Typ[types.Float64] + tComplex64 = types.Typ[types.Complex64] + tComplex128 = types.Typ[types.Complex128] tInt = types.Typ[types.Int] tInvalid = types.Typ[types.Invalid] tUntypedNil = types.Typ[types.UntypedNil] @@ -1128,17 +1125,21 @@ func (b *Builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) { var bptypes []types.Type // formal parameter types of builtins switch builtin := e.Fun.(*ast.Ident).Name; builtin { case "append": - // append([]T, ...T) []T - // append([]byte, string...) []byte // TODO(adonovan): fix: support. // Infer arg types from result type: rt := b.exprType(e) - vt = underlyingType(rt).(*types.Slice).Elt // variadic - if !c.HasEllipsis { - args, varargs = args[:1], args[1:] - } bptypes = append(bptypes, rt) + if c.HasEllipsis { + // 2-arg '...' call form. No conversions. + // append([]T, []T) []T + // append([]byte, string) []byte + } else { + // variadic call form. + // append([]T, ...T) []T + args, varargs = args[:1], args[1:] + vt = underlyingType(rt).(*types.Slice).Elt + } case "close": - bptypes = append(bptypes, nil) // no conv + // no conv case "copy": // copy([]T, []T) int // Infer arg types from each other. Sleazy. @@ -1151,22 +1152,33 @@ func (b *Builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) { } case "delete": // delete(map[K]V, K) - // TODO(adonovan): fix: this is incorrect. - bptypes = append(bptypes, nil) // map - bptypes = append(bptypes, nil) // key + tkey := underlyingType(b.exprType(args[0])).(*types.Map).Key + bptypes = append(bptypes, nil) // map: no conv + bptypes = append(bptypes, tkey) // key case "print", "println": // print{,ln}(any, ...any) vt = tEface // variadic if !c.HasEllipsis { args, varargs = args[:1], args[1:] } case "len": - bptypes = append(bptypes, nil) // no conv + // no conv case "cap": - bptypes = append(bptypes, nil) // no conv + // no conv case "real", "imag": - // TODO(adonovan): fix: apply reverse conversion - // to "complex" case below. - bptypes = append(bptypes, nil) + // Reverse conversion to "complex" case below. + // Typechecker, help us out. :( + var argType types.Type + switch b.exprType(e).(*types.Basic).Kind { + case types.UntypedFloat: + argType = types.Typ[types.UntypedComplex] + case types.Float64: + argType = tComplex128 + case types.Float32: + argType = tComplex64 + default: + unreachable() + } + bptypes = append(bptypes, argType, argType) case "complex": // Typechecker, help us out. :( var argType types.Type diff --git a/src/pkg/exp/ssa/emit.go b/src/pkg/exp/ssa/emit.go index d361463d98..7070c18b42 100644 --- a/src/pkg/exp/ssa/emit.go +++ b/src/pkg/exp/ssa/emit.go @@ -34,7 +34,6 @@ func emitLoad(f *Function, addr Value) Value { func emitArith(f *Function, op token.Token, x, y Value, t types.Type) Value { switch op { case token.SHL, token.SHR: - // TODO(adonovan): fix: is this correct? x = emitConv(f, x, t) y = emitConv(f, y, types.Typ[types.Uint64]) @@ -59,7 +58,6 @@ func emitArith(f *Function, op token.Token, x, y Value, t types.Type) Value { // comparison comparison 'x op y'. // func emitCompare(f *Function, op token.Token, x, y Value) Value { - // TODO(adonovan): fix: this is incomplete. xt := underlyingType(x.Type()) yt := underlyingType(y.Type()) diff --git a/src/pkg/exp/ssa/interp/interp.go b/src/pkg/exp/ssa/interp/interp.go index 0fa4316bf0..c20ddb9622 100644 --- a/src/pkg/exp/ssa/interp/interp.go +++ b/src/pkg/exp/ssa/interp/interp.go @@ -35,6 +35,10 @@ // program are assumed to be the same as those of the interpreter // itself. // +// * all values occupy space, even those of types defined by the spec +// to have zero size, e.g. struct{}. This can cause asymptotic +// performance degradation. +// // * os.Exit is implemented using panic, causing deferred functions to // run. package interp diff --git a/src/pkg/exp/ssa/interp/testdata/coverage.go b/src/pkg/exp/ssa/interp/testdata/coverage.go index 7573e0f69e..1ef82e9cf8 100644 --- a/src/pkg/exp/ssa/interp/testdata/coverage.go +++ b/src/pkg/exp/ssa/interp/testdata/coverage.go @@ -292,3 +292,31 @@ func init() { panic(c) } } + +// Shifts. +func init() { + var i int64 = 1 + var u uint64 = 1 << 32 + if x := i << uint32(u); x != 1 { + panic(x) + } + if x := i << uint64(u); x != 0 { + panic(x) + } +} + +// Implicit conversion of delete() key operand. +func init() { + type I interface{} + m := make(map[I]bool) + m[1] = true + m[I(2)] = true + if len(m) != 2 { + panic(m) + } + delete(m, I(1)) + delete(m, 2) + if len(m) != 0 { + panic(m) + } +}