Skip to content

Commit 8e193c2

Browse files
committed
go/ssa: removes conversion of index value in Index and IndexAddr to int
Removes the forced conversion of the Index field in Index and IndexAddr to type int. ssa/interp now intprets indices as int64s instead of ints. Converts untyped indices for strings to ints before the lookup. Fixes golang/go#50949 Change-Id: Ib5d7f1ad28728d16c8e0a8411014d1209c62a3f2 Reviewed-on: https://go-review.googlesource.com/c/tools/+/387996 Reviewed-by: Robert Findley <[email protected]> Run-TryBot: Tim King <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Trust: Tim King <[email protected]>
1 parent 9d8009b commit 8e193c2

File tree

9 files changed

+131
-36
lines changed

9 files changed

+131
-36
lines changed

go/ssa/builder.go

+15-3
Original file line numberDiff line numberDiff line change
@@ -407,9 +407,13 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
407407
default:
408408
panic("unexpected container type in IndexExpr: " + t.String())
409409
}
410+
index := b.expr(fn, e.Index)
411+
if isUntyped(index.Type()) {
412+
index = emitConv(fn, index, tInt)
413+
}
410414
v := &IndexAddr{
411415
X: x,
412-
Index: emitConv(fn, b.expr(fn, e.Index), tInt),
416+
Index: index,
413417
}
414418
v.setPos(e.Lbrack)
415419
v.setType(et)
@@ -748,9 +752,13 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
748752
switch t := fn.typeOf(e.X).Underlying().(type) {
749753
case *types.Array:
750754
// Non-addressable array (in a register).
755+
index := b.expr(fn, e.Index)
756+
if isUntyped(index.Type()) {
757+
index = emitConv(fn, index, tInt)
758+
}
751759
v := &Index{
752760
X: b.expr(fn, e.X),
753-
Index: emitConv(fn, b.expr(fn, e.Index), tInt),
761+
Index: index,
754762
}
755763
v.setPos(e.Lbrack)
756764
v.setType(t.Elem())
@@ -769,9 +777,13 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
769777

770778
case *types.Basic: // => string
771779
// Strings are not addressable.
780+
index := b.expr(fn, e.Index)
781+
if isUntyped(index.Type()) {
782+
index = emitConv(fn, index, tInt)
783+
}
772784
v := &Lookup{
773785
X: b.expr(fn, e.X),
774-
Index: b.expr(fn, e.Index),
786+
Index: index,
775787
}
776788
v.setPos(e.Lbrack)
777789
v.setType(tByte)

go/ssa/emit.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func emitArith(f *Function, op token.Token, x, y Value, t types.Type, pos token.
7878
// There is a runtime panic if y is signed and <0. Instead of inserting a check for y<0
7979
// and converting to an unsigned value (like the compiler) leave y as is.
8080

81-
if b, ok := y.Type().Underlying().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {
81+
if isUntyped(y.Type().Underlying()) {
8282
// Untyped conversion:
8383
// Spec https://go.dev/ref/spec#Operators:
8484
// The right operand in a shift expression must have integer type or be an untyped constant

go/ssa/interp/interp.go

+11-8
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation {
279279
}()
280280

281281
case *ssa.MakeChan:
282-
fr.env[instr] = make(chan value, asInt(fr.get(instr.Size)))
282+
fr.env[instr] = make(chan value, asInt64(fr.get(instr.Size)))
283283

284284
case *ssa.Alloc:
285285
var addr *value
@@ -294,17 +294,20 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation {
294294
*addr = zero(deref(instr.Type()))
295295

296296
case *ssa.MakeSlice:
297-
slice := make([]value, asInt(fr.get(instr.Cap)))
297+
slice := make([]value, asInt64(fr.get(instr.Cap)))
298298
tElt := instr.Type().Underlying().(*types.Slice).Elem()
299299
for i := range slice {
300300
slice[i] = zero(tElt)
301301
}
302-
fr.env[instr] = slice[:asInt(fr.get(instr.Len))]
302+
fr.env[instr] = slice[:asInt64(fr.get(instr.Len))]
303303

304304
case *ssa.MakeMap:
305-
reserve := 0
305+
var reserve int64
306306
if instr.Reserve != nil {
307-
reserve = asInt(fr.get(instr.Reserve))
307+
reserve = asInt64(fr.get(instr.Reserve))
308+
}
309+
if !fitsInt(reserve, fr.i.sizes) {
310+
panic(fmt.Sprintf("ssa.MakeMap.Reserve value %d does not fit in int", reserve))
308311
}
309312
fr.env[instr] = makeMap(instr.Type().Underlying().(*types.Map).Key(), reserve)
310313

@@ -325,15 +328,15 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation {
325328
idx := fr.get(instr.Index)
326329
switch x := x.(type) {
327330
case []value:
328-
fr.env[instr] = &x[asInt(idx)]
331+
fr.env[instr] = &x[asInt64(idx)]
329332
case *value: // *array
330-
fr.env[instr] = &(*x).(array)[asInt(idx)]
333+
fr.env[instr] = &(*x).(array)[asInt64(idx)]
331334
default:
332335
panic(fmt.Sprintf("unexpected x type in IndexAddr: %T", x))
333336
}
334337

335338
case *ssa.Index:
336-
fr.env[instr] = fr.get(instr.X).(array)[asInt(fr.get(instr.Index))]
339+
fr.env[instr] = fr.get(instr.X).(array)[asInt64(fr.get(instr.Index))]
337340

338341
case *ssa.Lookup:
339342
fr.env[instr] = lookup(instr, fr.get(instr.X), fr.get(instr.Index))

go/ssa/interp/interp_test.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,13 @@ var testdataTests = []string{
123123
"recover.go",
124124
"reflect.go",
125125
"static.go",
126+
"width32.go",
127+
}
128+
129+
// Specific GOARCH to use for a test case in go.tools/go/ssa/interp/testdata/.
130+
// Defaults to amd64 otherwise.
131+
var testdataArchs = map[string]string{
132+
"width32.go": "386",
126133
}
127134

128135
func run(t *testing.T, input string) bool {
@@ -140,6 +147,9 @@ func run(t *testing.T, input string) bool {
140147
ctx.GOROOT = "testdata" // fake goroot
141148
ctx.GOOS = "linux"
142149
ctx.GOARCH = "amd64"
150+
if arch, ok := testdataArchs[filepath.Base(input)]; ok {
151+
ctx.GOARCH = arch
152+
}
143153

144154
conf := loader.Config{Build: &ctx}
145155
if _, err := conf.FromArgs([]string{input}, true); err != nil {
@@ -180,8 +190,9 @@ func run(t *testing.T, input string) bool {
180190

181191
interp.CapturedOutput = new(bytes.Buffer)
182192

193+
sizes := types.SizesFor("gc", ctx.GOARCH)
183194
hint = fmt.Sprintf("To trace execution, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -build=C -test -run --interp=T %s\n", input)
184-
exitCode := interp.Interpret(mainPkg, 0, &types.StdSizes{WordSize: 8, MaxAlign: 8}, input, []string{})
195+
exitCode := interp.Interpret(mainPkg, 0, sizes, input, []string{})
185196
if exitCode != 0 {
186197
t.Fatalf("interpreting %s: exit code was %d", input, exitCode)
187198
}

go/ssa/interp/map.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type hashmap struct {
3838

3939
// makeMap returns an empty initialized map of key type kt,
4040
// preallocating space for reserve elements.
41-
func makeMap(kt types.Type, reserve int) value {
41+
func makeMap(kt types.Type, reserve int64) value {
4242
if usesBuiltinMap(kt) {
4343
return make(map[value]value, reserve)
4444
}

go/ssa/interp/ops.go

+34-22
Original file line numberDiff line numberDiff line change
@@ -87,34 +87,46 @@ func constValue(c *ssa.Const) value {
8787
panic(fmt.Sprintf("constValue: %s", c))
8888
}
8989

90-
// asInt converts x, which must be an integer, to an int suitable for
91-
// use as a slice or array index or operand to make().
92-
func asInt(x value) int {
90+
// fitsInt returns true if x fits in type int according to sizes.
91+
func fitsInt(x int64, sizes types.Sizes) bool {
92+
intSize := sizes.Sizeof(types.Typ[types.Int])
93+
if intSize < sizes.Sizeof(types.Typ[types.Int64]) {
94+
maxInt := int64(1)<<(intSize-1) - 1
95+
minInt := -int64(1) << (intSize - 1)
96+
return minInt <= x && x <= maxInt
97+
}
98+
return true
99+
}
100+
101+
// asInt64 converts x, which must be an integer, to an int64.
102+
//
103+
// Callers that need a value directly usable as an int should combine this with fitsInt().
104+
func asInt64(x value) int64 {
93105
switch x := x.(type) {
94106
case int:
95-
return x
107+
return int64(x)
96108
case int8:
97-
return int(x)
109+
return int64(x)
98110
case int16:
99-
return int(x)
111+
return int64(x)
100112
case int32:
101-
return int(x)
113+
return int64(x)
102114
case int64:
103-
return int(x)
115+
return x
104116
case uint:
105-
return int(x)
117+
return int64(x)
106118
case uint8:
107-
return int(x)
119+
return int64(x)
108120
case uint16:
109-
return int(x)
121+
return int64(x)
110122
case uint32:
111-
return int(x)
123+
return int64(x)
112124
case uint64:
113-
return int(x)
125+
return int64(x)
114126
case uintptr:
115-
return int(x)
127+
return int64(x)
116128
}
117-
panic(fmt.Sprintf("cannot convert %T to int", x))
129+
panic(fmt.Sprintf("cannot convert %T to int64", x))
118130
}
119131

120132
// asUint64 converts x, which must be an unsigned integer, to a uint64
@@ -268,19 +280,19 @@ func slice(x, lo, hi, max value) value {
268280
Cap = cap(a)
269281
}
270282

271-
l := 0
283+
l := int64(0)
272284
if lo != nil {
273-
l = asInt(lo)
285+
l = asInt64(lo)
274286
}
275287

276-
h := Len
288+
h := int64(Len)
277289
if hi != nil {
278-
h = asInt(hi)
290+
h = asInt64(hi)
279291
}
280292

281-
m := Cap
293+
m := int64(Cap)
282294
if max != nil {
283-
m = asInt(max)
295+
m = asInt64(max)
284296
}
285297

286298
switch x := x.(type) {
@@ -316,7 +328,7 @@ func lookup(instr *ssa.Lookup, x, idx value) value {
316328
}
317329
return v
318330
case string:
319-
return x[asInt(idx)]
331+
return x[asInt64(idx)]
320332
}
321333
panic(fmt.Sprintf("unexpected x type in Lookup: %T", x))
322334
}

go/ssa/interp/testdata/convert.go

+9
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ func main() {
2222
},
2323
"runtime error: negative shift amount",
2424
)
25+
wantPanic(
26+
func() {
27+
const maxInt32 = 1<<31 - 1
28+
var idx int64 = maxInt32*2 + 8
29+
x := make([]int, 16)
30+
_ = x[idx]
31+
},
32+
"runtime error: runtime error: index out of range [4294967302] with length 16",
33+
)
2534
}
2635

2736
func wantPanic(fn func(), s string) {

go/ssa/interp/testdata/width32.go

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Test interpretation on 32 bit widths.
6+
7+
package main
8+
9+
func main() {
10+
mapSize()
11+
}
12+
13+
func mapSize() {
14+
// Tests for the size argument of make on a map type.
15+
const tooBigFor32 = 1<<33 - 1
16+
wantPanic(
17+
func() {
18+
_ = make(map[int]int, int64(tooBigFor32))
19+
},
20+
"runtime error: ssa.MakeMap.Reserve value 8589934591 does not fit in int",
21+
)
22+
23+
// TODO: Enable the following if sizeof(int) can be different for host and target.
24+
// _ = make(map[int]int, tooBigFor32)
25+
//
26+
// Second arg to make in `make(map[int]int, tooBigFor32)` is an untyped int and
27+
// is converted into an int explicitly in ssa.
28+
// This has a different value on 32 and 64 bit systems.
29+
}
30+
31+
func wantPanic(fn func(), s string) {
32+
defer func() {
33+
err := recover()
34+
if err == nil {
35+
panic("expected panic")
36+
}
37+
if got := err.(error).Error(); got != s {
38+
panic("expected panic " + s + " got " + got)
39+
}
40+
}()
41+
fn()
42+
}

go/ssa/util.go

+6
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ func recvType(obj *types.Func) types.Type {
6464
return obj.Type().(*types.Signature).Recv().Type()
6565
}
6666

67+
// isUntyped returns true for types that are untyped.
68+
func isUntyped(typ types.Type) bool {
69+
b, ok := typ.(*types.Basic)
70+
return ok && b.Info()&types.IsUntyped != 0
71+
}
72+
6773
// logStack prints the formatted "start" message to stderr and
6874
// returns a closure that prints the corresponding "end" message.
6975
// Call using 'defer logStack(...)()' to show builder stack on panic.

0 commit comments

Comments
 (0)