diff --git a/pointer/testdata/conv.go b/pointer/testdata/conv.go index 49cc8cad..5439a39e 100644 --- a/pointer/testdata/conv.go +++ b/pointer/testdata/conv.go @@ -14,10 +14,6 @@ func conv1() { } func conv2() { - // []byte/[]rune literal - print([]byte("foo")) // @pointsto "foo":[]byte - print([]rune("bar")) // @pointsto "bar":[]rune - // string -> []byte/[]rune conversion s := "foo" ba := []byte(s) // @line c2ba diff --git a/ssa/emit.go b/ssa/emit.go index 5319064d..edb40677 100644 --- a/ssa/emit.go +++ b/ssa/emit.go @@ -218,13 +218,19 @@ func emitConv(f *Function, val Value, typ types.Type) Value { return f.emit(mi) } - // Conversion of a constant to a non-interface type results in - // a new constant of the destination type and (initially) the - // same abstract value. We don't compute the representation - // change yet; this defers the point at which the number of - // possible representations explodes. + // Conversion of a compile-time constant value? if c, ok := val.(*Const); ok { - return NewConst(c.Value, typ) + if _, ok := ut_dst.(*types.Basic); ok || c.IsNil() { + // Conversion of a compile-time constant to + // another constant type results in a new + // constant of the destination type and + // (initially) the same abstract value. + // We don't truncate the value yet. + return NewConst(c.Value, typ) + } + + // We're converting from constant to non-constant type, + // e.g. string -> []byte/[]rune. } // A representation-changing conversion. diff --git a/ssa/interp/ops.go b/ssa/interp/ops.go index 2b9fea9e..3af2b336 100644 --- a/ssa/interp/ops.go +++ b/ssa/interp/ops.go @@ -37,9 +37,7 @@ func constValue(c *ssa.Const) value { return zero(c.Type()) // typed nil } - // By destination type: - switch t := c.Type().Underlying().(type) { - case *types.Basic: + if t, ok := c.Type().Underlying().(*types.Basic); ok { // TODO(adonovan): eliminate untyped constants from SSA form. switch t.Kind() { case types.Bool, types.UntypedBool: @@ -82,29 +80,6 @@ func constValue(c *ssa.Const) value { return exact.StringVal(c.Value) } return string(rune(c.Int64())) - case types.UnsafePointer: - panic("unsafe.Pointer constant") // not possible - case types.UntypedNil: - // nil was handled above. - } - - case *types.Slice: - switch et := t.Elem().Underlying().(type) { - case *types.Basic: - switch et.Kind() { - case types.Byte: // string -> []byte - var v []value - for _, b := range []byte(exact.StringVal(c.Value)) { - v = append(v, b) - } - return v - case types.Rune: // string -> []rune - var v []value - for _, r := range []rune(exact.StringVal(c.Value)) { - v = append(v, r) - } - return v - } } } diff --git a/ssa/interp/testdata/coverage.go b/ssa/interp/testdata/coverage.go index da2499a1..2bf8960d 100644 --- a/ssa/interp/testdata/coverage.go +++ b/ssa/interp/testdata/coverage.go @@ -610,3 +610,17 @@ func init() { _ = x _ = y } + +// Regression test for issue 6949: +// []byte("foo") is not a constant since it allocates memory. +func init() { + var r string + for i, b := range "ABC" { + x := []byte("abc") + x[i] = byte(b) + r += string(x) + } + if r != "AbcaBcabC" { + panic(r) + } +} diff --git a/ssa/ssa.go b/ssa/ssa.go index 70386946..7c2c7229 100644 --- a/ssa/ssa.go +++ b/ssa/ssa.go @@ -95,7 +95,7 @@ type NamedConst struct { pkg *Package } -// An SSA value that can be referenced by an instruction. +// A Value is an SSA value that can be referenced by an instruction. type Value interface { // Name returns the name of this value, and determines how // this Value appears when used as an operand of an @@ -232,7 +232,7 @@ type Instruction interface { // or method. // // If Blocks is nil, this indicates an external function for which no -// Go source code is available. In this case, Captures and Locals +// Go source code is available. In this case, FreeVars and Locals // will be nil too. Clients performing whole-program analysis must // handle external functions specially. // @@ -368,15 +368,10 @@ type Parameter struct { // A Const represents the value of a constant expression. // -// It may have a nil, boolean, string or numeric (integer, fraction or -// complex) value, or a []byte or []rune conversion of a string -// constant. -// -// Consts may be of named types. A constant's underlying type can be -// a basic type, possibly one of the "untyped" types, or a slice type -// whose elements' underlying type is byte or rune. A nil constant can -// have any reference type: interface, map, channel, pointer, slice, -// or function---but not "untyped nil". +// The underlying type of a constant may be any boolean, numeric, or +// string type. In addition, a Const may represent the nil value of +// any reference type: interface, map, channel, pointer, slice, or +// function---but not "untyped nil". // // All source-level constant expressions are represented by a Const // of equal type and value.