]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: fixes for non-constant Sizeof/Alignof/Offsetof
authorDan Scales <danscales@google.com>
Sun, 22 Aug 2021 18:50:58 +0000 (11:50 -0700)
committerDan Scales <danscales@google.com>
Mon, 23 Aug 2021 22:55:34 +0000 (22:55 +0000)
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 <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Dan Scales <danscales@google.com>

src/cmd/compile/internal/noder/helpers.go
src/cmd/compile/internal/noder/stencil.go
src/cmd/compile/internal/noder/transform.go
src/cmd/compile/internal/noder/validate.go
src/cmd/compile/internal/typecheck/const.go
test/typeparam/issue47716.go [new file with mode: 0644]

index b9dbd030af190a0c5d34cce1b9b02f2eda7b1a1b..9487e76336cea7f8938ebd8ee288ea8751d6e613 100644 (file)
@@ -138,10 +138,18 @@ func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool)
                //    until arg type known
                // OAPPEND: transformAppend requires that the arg is a slice
                // ODELETE: transformDelete requires that the arg is a map
+               // OALIGNOF, OSIZEOF: can be eval'ed to a constant until types known.
                switch fun.BuiltinOp {
-               case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.ODELETE:
+               case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.ODELETE, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
                        hasTParam := false
                        for _, arg := range args {
+                               if fun.BuiltinOp == ir.OOFFSETOF {
+                                       // It's the type of left operand of the
+                                       // selection that matters, not the type of
+                                       // the field itself (which is irrelevant for
+                                       // offsetof).
+                                       arg = arg.(*ir.SelectorExpr).X
+                               }
                                if arg.Type().HasTParam() {
                                        hasTParam = true
                                        break
index 4ed1850597b7c600207a2bf7d30ca0e4ca549c09..2d275d6a3b2c12b59c2bd3ef7f2c40438ec8d3a5 100644 (file)
@@ -1037,7 +1037,7 @@ func (subst *subster) node(n ir.Node) ir.Node {
                                name := call.X.Name()
                                if name.BuiltinOp != ir.OXXX {
                                        switch name.BuiltinOp {
-                                       case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.ODELETE:
+                                       case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.ODELETE, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
                                                // Transform these builtins now that we
                                                // know the type of the args.
                                                m = transformBuiltin(call)
index 140bb33234d6368ea3600b6d273895cf2f1a89f4..be8651d47b7b2465e2b62f626d6d36f95da7e83b 100644 (file)
@@ -811,7 +811,10 @@ func transformBuiltin(n *ir.CallExpr) ir.Node {
                        return transformRealImag(u1.(*ir.UnaryExpr))
                case ir.OPANIC:
                        return transformPanic(u1.(*ir.UnaryExpr))
-               case ir.OCLOSE, ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
+               case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
+                       // This corresponds to the EvalConst() call near end of typecheck().
+                       return typecheck.EvalConst(u1)
+               case ir.OCLOSE, ir.ONEW:
                        // nothing more to do
                        return u1
                }
index 68a059b96f4a29691e445dcfc5108765b41ba36f..dcacae7480c2975c50182aeb72d46d31e374d4f4 100644 (file)
@@ -81,7 +81,16 @@ func (g *irgen) validateBuiltin(name string, call *syntax.CallExpr) {
                // Check that types2+gcSizes calculates sizes the same
                // as cmd/compile does.
 
-               got, ok := constant.Int64Val(g.info.Types[call].Value)
+               tv := g.info.Types[call]
+               if !tv.IsValue() {
+                       base.FatalfAt(g.pos(call), "expected a value")
+               }
+
+               if tv.Value == nil {
+                       break // unsafe op is not a constant, so no further validation
+               }
+
+               got, ok := constant.Int64Val(tv.Value)
                if !ok {
                        base.FatalfAt(g.pos(call), "expected int64 constant value")
                }
index f8150d249a61c624d328814a9288fbb3ad2fb2ca..c27cf0e6465316284081afd08dd542d283dbdb68 100644 (file)
@@ -881,7 +881,9 @@ func evalunsafe(n ir.Node) int64 {
        case ir.OOFFSETOF:
                // must be a selector.
                n := n.(*ir.UnaryExpr)
-               if n.X.Op() != ir.OXDOT {
+               // ODOT and ODOTPTR are allowed in case the OXDOT transformation has
+               // already happened (e.g. during -G=3 stenciling).
+               if n.X.Op() != ir.OXDOT && n.X.Op() != ir.ODOT && n.X.Op() != ir.ODOTPTR {
                        base.Errorf("invalid expression %v", n)
                        return 0
                }
diff --git a/test/typeparam/issue47716.go b/test/typeparam/issue47716.go
new file mode 100644 (file)
index 0000000..7f34fcb
--- /dev/null
@@ -0,0 +1,68 @@
+// run -gcflags=-G=3
+
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+       "fmt"
+       "unsafe"
+)
+
+// size returns the size of type T
+func size[T any](x T) uintptr {
+       return unsafe.Sizeof(x)
+}
+
+// size returns the alignment of type T
+func align[T any](x T) uintptr {
+       return unsafe.Alignof(x)
+}
+
+type Tstruct[T any] struct {
+       f1 T
+       f2 int
+}
+
+// offset returns the offset of field f2 in the generic type Tstruct
+func (r *Tstruct[T]) offset() uintptr {
+       return unsafe.Offsetof(r.f2)
+}
+
+func main() {
+       v1 := int(5)
+       if got, want := size(v1), unsafe.Sizeof(v1); got != want {
+               panic(fmt.Sprintf("got %d, want %d", got, want))
+       }
+       if got, want := align(v1), unsafe.Alignof(v1); got != want {
+               panic(fmt.Sprintf("got %d, want %d", got, want))
+       }
+
+       v2 := "abc"
+       if got, want := size(v2), unsafe.Sizeof(v2); got != want {
+               panic(fmt.Sprintf("got %d, want %d", got, want))
+       }
+       if got, want := align(v2), unsafe.Alignof(v2); got != want {
+               panic(fmt.Sprintf("got %d, want %d", got, want))
+       }
+
+       var v3 Tstruct[int]
+       if got, want := unsafe.Offsetof(v3.f2), unsafe.Sizeof(v1); got != want {
+               panic(fmt.Sprintf("got %d, want %d", got, want))
+       }
+
+       var v4 Tstruct[interface{}]
+       var v5 interface{}
+       if got, want := unsafe.Offsetof(v4.f2), unsafe.Sizeof(v5); got != want {
+               panic(fmt.Sprintf("got %d, want %d", got, want))
+       }
+
+       if got, want := v3.offset(), unsafe.Offsetof(v3.f2); got != want {
+               panic(fmt.Sprintf("got %d, want %d", got, want))
+       }
+       if got, want := v4.offset(), unsafe.Offsetof(v4.f2); got != want {
+               panic(fmt.Sprintf("got %d, want %d", got, want))
+       }
+}