Skip to content

Commit be1a693

Browse files
committed
cmd/compile: fixes for non-constant Sizeof/Alignof/Offsetof
Includes Robert's suggested fix in validate.go to not fail on non-constant alignof/offsetof/sizeof calls. Further changes to wait on transforming these calls until stenciling time, when we can call EvalConst() to evaluate them once all the relevant types are known. Added a bunch of new tests for non-constant Sizeof/Alignof/Offsetof. Fixes #47716 Change-Id: I469af888eb9ce3a853124d919eda753971009b3e Reviewed-on: https://go-review.googlesource.com/c/go/+/344250 Run-TryBot: Dan Scales <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]> Trust: Dan Scales <[email protected]>
1 parent 8157960 commit be1a693

File tree

6 files changed

+95
-5
lines changed

6 files changed

+95
-5
lines changed

src/cmd/compile/internal/noder/helpers.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,18 @@ func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool)
138138
// until arg type known
139139
// OAPPEND: transformAppend requires that the arg is a slice
140140
// ODELETE: transformDelete requires that the arg is a map
141+
// OALIGNOF, OSIZEOF: can be eval'ed to a constant until types known.
141142
switch fun.BuiltinOp {
142-
case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.ODELETE:
143+
case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.ODELETE, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
143144
hasTParam := false
144145
for _, arg := range args {
146+
if fun.BuiltinOp == ir.OOFFSETOF {
147+
// It's the type of left operand of the
148+
// selection that matters, not the type of
149+
// the field itself (which is irrelevant for
150+
// offsetof).
151+
arg = arg.(*ir.SelectorExpr).X
152+
}
145153
if arg.Type().HasTParam() {
146154
hasTParam = true
147155
break

src/cmd/compile/internal/noder/stencil.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1037,7 +1037,7 @@ func (subst *subster) node(n ir.Node) ir.Node {
10371037
name := call.X.Name()
10381038
if name.BuiltinOp != ir.OXXX {
10391039
switch name.BuiltinOp {
1040-
case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.ODELETE:
1040+
case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.ODELETE, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
10411041
// Transform these builtins now that we
10421042
// know the type of the args.
10431043
m = transformBuiltin(call)

src/cmd/compile/internal/noder/transform.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,10 @@ func transformBuiltin(n *ir.CallExpr) ir.Node {
811811
return transformRealImag(u1.(*ir.UnaryExpr))
812812
case ir.OPANIC:
813813
return transformPanic(u1.(*ir.UnaryExpr))
814-
case ir.OCLOSE, ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
814+
case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
815+
// This corresponds to the EvalConst() call near end of typecheck().
816+
return typecheck.EvalConst(u1)
817+
case ir.OCLOSE, ir.ONEW:
815818
// nothing more to do
816819
return u1
817820
}

src/cmd/compile/internal/noder/validate.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,16 @@ func (g *irgen) validateBuiltin(name string, call *syntax.CallExpr) {
8181
// Check that types2+gcSizes calculates sizes the same
8282
// as cmd/compile does.
8383

84-
got, ok := constant.Int64Val(g.info.Types[call].Value)
84+
tv := g.info.Types[call]
85+
if !tv.IsValue() {
86+
base.FatalfAt(g.pos(call), "expected a value")
87+
}
88+
89+
if tv.Value == nil {
90+
break // unsafe op is not a constant, so no further validation
91+
}
92+
93+
got, ok := constant.Int64Val(tv.Value)
8594
if !ok {
8695
base.FatalfAt(g.pos(call), "expected int64 constant value")
8796
}

src/cmd/compile/internal/typecheck/const.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,9 @@ func evalunsafe(n ir.Node) int64 {
881881
case ir.OOFFSETOF:
882882
// must be a selector.
883883
n := n.(*ir.UnaryExpr)
884-
if n.X.Op() != ir.OXDOT {
884+
// ODOT and ODOTPTR are allowed in case the OXDOT transformation has
885+
// already happened (e.g. during -G=3 stenciling).
886+
if n.X.Op() != ir.OXDOT && n.X.Op() != ir.ODOT && n.X.Op() != ir.ODOTPTR {
885887
base.Errorf("invalid expression %v", n)
886888
return 0
887889
}

test/typeparam/issue47716.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// run -gcflags=-G=3
2+
3+
// Copyright 2021 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package main
8+
9+
import (
10+
"fmt"
11+
"unsafe"
12+
)
13+
14+
// size returns the size of type T
15+
func size[T any](x T) uintptr {
16+
return unsafe.Sizeof(x)
17+
}
18+
19+
// size returns the alignment of type T
20+
func align[T any](x T) uintptr {
21+
return unsafe.Alignof(x)
22+
}
23+
24+
type Tstruct[T any] struct {
25+
f1 T
26+
f2 int
27+
}
28+
29+
// offset returns the offset of field f2 in the generic type Tstruct
30+
func (r *Tstruct[T]) offset() uintptr {
31+
return unsafe.Offsetof(r.f2)
32+
}
33+
34+
func main() {
35+
v1 := int(5)
36+
if got, want := size(v1), unsafe.Sizeof(v1); got != want {
37+
panic(fmt.Sprintf("got %d, want %d", got, want))
38+
}
39+
if got, want := align(v1), unsafe.Alignof(v1); got != want {
40+
panic(fmt.Sprintf("got %d, want %d", got, want))
41+
}
42+
43+
v2 := "abc"
44+
if got, want := size(v2), unsafe.Sizeof(v2); got != want {
45+
panic(fmt.Sprintf("got %d, want %d", got, want))
46+
}
47+
if got, want := align(v2), unsafe.Alignof(v2); got != want {
48+
panic(fmt.Sprintf("got %d, want %d", got, want))
49+
}
50+
51+
var v3 Tstruct[int]
52+
if got, want := unsafe.Offsetof(v3.f2), unsafe.Sizeof(v1); got != want {
53+
panic(fmt.Sprintf("got %d, want %d", got, want))
54+
}
55+
56+
var v4 Tstruct[interface{}]
57+
var v5 interface{}
58+
if got, want := unsafe.Offsetof(v4.f2), unsafe.Sizeof(v5); got != want {
59+
panic(fmt.Sprintf("got %d, want %d", got, want))
60+
}
61+
62+
if got, want := v3.offset(), unsafe.Offsetof(v3.f2); got != want {
63+
panic(fmt.Sprintf("got %d, want %d", got, want))
64+
}
65+
if got, want := v4.offset(), unsafe.Offsetof(v4.f2); got != want {
66+
panic(fmt.Sprintf("got %d, want %d", got, want))
67+
}
68+
}

0 commit comments

Comments
 (0)