mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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 <danscales@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org> Trust: Dan Scales <danscales@google.com>
This commit is contained in:
parent
8157960d7f
commit
be1a693477
6 changed files with 95 additions and 5 deletions
|
|
@ -138,10 +138,18 @@ func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool)
|
||||||
// until arg type known
|
// until arg type known
|
||||||
// OAPPEND: transformAppend requires that the arg is a slice
|
// OAPPEND: transformAppend requires that the arg is a slice
|
||||||
// ODELETE: transformDelete requires that the arg is a map
|
// ODELETE: transformDelete requires that the arg is a map
|
||||||
|
// OALIGNOF, OSIZEOF: can be eval'ed to a constant until types known.
|
||||||
switch fun.BuiltinOp {
|
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
|
hasTParam := false
|
||||||
for _, arg := range args {
|
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() {
|
if arg.Type().HasTParam() {
|
||||||
hasTParam = true
|
hasTParam = true
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -1037,7 +1037,7 @@ func (subst *subster) node(n ir.Node) ir.Node {
|
||||||
name := call.X.Name()
|
name := call.X.Name()
|
||||||
if name.BuiltinOp != ir.OXXX {
|
if name.BuiltinOp != ir.OXXX {
|
||||||
switch name.BuiltinOp {
|
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
|
// Transform these builtins now that we
|
||||||
// know the type of the args.
|
// know the type of the args.
|
||||||
m = transformBuiltin(call)
|
m = transformBuiltin(call)
|
||||||
|
|
|
||||||
|
|
@ -811,7 +811,10 @@ func transformBuiltin(n *ir.CallExpr) ir.Node {
|
||||||
return transformRealImag(u1.(*ir.UnaryExpr))
|
return transformRealImag(u1.(*ir.UnaryExpr))
|
||||||
case ir.OPANIC:
|
case ir.OPANIC:
|
||||||
return transformPanic(u1.(*ir.UnaryExpr))
|
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
|
// nothing more to do
|
||||||
return u1
|
return u1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,16 @@ func (g *irgen) validateBuiltin(name string, call *syntax.CallExpr) {
|
||||||
// Check that types2+gcSizes calculates sizes the same
|
// Check that types2+gcSizes calculates sizes the same
|
||||||
// as cmd/compile does.
|
// 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 {
|
if !ok {
|
||||||
base.FatalfAt(g.pos(call), "expected int64 constant value")
|
base.FatalfAt(g.pos(call), "expected int64 constant value")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -881,7 +881,9 @@ func evalunsafe(n ir.Node) int64 {
|
||||||
case ir.OOFFSETOF:
|
case ir.OOFFSETOF:
|
||||||
// must be a selector.
|
// must be a selector.
|
||||||
n := n.(*ir.UnaryExpr)
|
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)
|
base.Errorf("invalid expression %v", n)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
68
test/typeparam/issue47716.go
Normal file
68
test/typeparam/issue47716.go
Normal file
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue