}
// Special case: sync/atomic.align64 is an empty struct we recognize
// as a signal that the struct it contains must be 64-bit-aligned.
+ //
+ // This logic is duplicated in go/types and cmd/compile/internal/types2.
if isStruct && t.NumFields() == 0 && t.Sym() != nil && t.Sym().Name == "align64" && isAtomicStdPkg(t.Sym().Pkg) {
maxalign = 8
}
// is the same as unsafe.Alignof(x[0]), but at least 1."
return s.Alignof(t.elem)
case *Struct:
+ if len(t.fields) == 0 && isSyncAtomicAlign64(T) {
+ // Special case: sync/atomic.align64 is an
+ // empty struct we recognize as a signal that
+ // the struct it contains must be
+ // 64-bit-aligned.
+ //
+ // This logic is equivalent to the logic in
+ // cmd/compile/internal/types/size.go:calcStructOffset
+ return 8
+ }
+
// spec: "For a variable x of struct type: unsafe.Alignof(x)
// is the largest of the values unsafe.Alignof(x.f) for each
// field f of x, but at least 1."
return a
}
+func isSyncAtomicAlign64(T Type) bool {
+ named, ok := T.(*Named)
+ if !ok {
+ return false
+ }
+ obj := named.Obj()
+ return obj.Name() == "align64" &&
+ obj.Pkg() != nil &&
+ (obj.Pkg().Path() == "sync/atomic" ||
+ obj.Pkg().Path() == "runtime/internal/atomic")
+}
+
func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
offsets := make([]int64, len(fields))
var o int64
// findStructType typechecks src and returns the first struct type encountered.
func findStructType(t *testing.T, src string) *types2.Struct {
+ return findStructTypeConfig(t, src, &types2.Config{})
+}
+
+func findStructTypeConfig(t *testing.T, src string, conf *types2.Config) *types2.Struct {
f, err := parseSrc("x.go", src)
if err != nil {
t.Fatal(err)
}
info := types2.Info{Types: make(map[syntax.Expr]types2.TypeAndValue)}
- var conf types2.Config
_, err = conf.Check("x", []*syntax.File{f}, &info)
if err != nil {
t.Fatal(err)
_ = conf.Sizes.Alignof(tv.Type)
}
}
+
+// Issue #53884.
+func TestAtomicAlign(t *testing.T) {
+ const src = `
+package main
+
+import "sync/atomic"
+
+var s struct {
+ x int32
+ y atomic.Int64
+ z int64
+}
+`
+
+ want := []int64{0, 8, 16}
+ for _, arch := range []string{"386", "amd64"} {
+ t.Run(arch, func(t *testing.T) {
+ conf := types2.Config{
+ Importer: defaultImporter(),
+ Sizes: types2.SizesFor("gc", arch),
+ }
+ ts := findStructTypeConfig(t, src, &conf)
+ var fields []*types2.Var
+ // Make a copy manually :(
+ for i := 0; i < ts.NumFields(); i++ {
+ fields = append(fields, ts.Field(i))
+ }
+
+ offsets := conf.Sizes.Offsetsof(fields)
+ if offsets[0] != want[0] || offsets[1] != want[1] || offsets[2] != want[2] {
+ t.Errorf("OffsetsOf(%v) = %v want %v", ts, offsets, want)
+ }
+ })
+ }
+}
// is the same as unsafe.Alignof(x[0]), but at least 1."
return s.Alignof(t.elem)
case *Struct:
+ if len(t.fields) == 0 && isSyncAtomicAlign64(T) {
+ // Special case: sync/atomic.align64 is an
+ // empty struct we recognize as a signal that
+ // the struct it contains must be
+ // 64-bit-aligned.
+ //
+ // This logic is equivalent to the logic in
+ // cmd/compile/internal/types/size.go:calcStructOffset
+ return 8
+ }
+
// spec: "For a variable x of struct type: unsafe.Alignof(x)
// is the largest of the values unsafe.Alignof(x.f) for each
// field f of x, but at least 1."
return a
}
+func isSyncAtomicAlign64(T Type) bool {
+ named, ok := T.(*Named)
+ if !ok {
+ return false
+ }
+ obj := named.Obj()
+ return obj.Name() == "align64" &&
+ obj.Pkg() != nil &&
+ (obj.Pkg().Path() == "sync/atomic" ||
+ obj.Pkg().Path() == "runtime/internal/atomic")
+}
+
func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
offsets := make([]int64, len(fields))
var o int64
// findStructType typechecks src and returns the first struct type encountered.
func findStructType(t *testing.T, src string) *types.Struct {
+ return findStructTypeConfig(t, src, &types.Config{})
+}
+
+func findStructTypeConfig(t *testing.T, src string, conf *types.Config) *types.Struct {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "x.go", src, 0)
if err != nil {
t.Fatal(err)
}
info := types.Info{Types: make(map[ast.Expr]types.TypeAndValue)}
- var conf types.Config
_, err = conf.Check("x", fset, []*ast.File{f}, &info)
if err != nil {
t.Fatal(err)
_ = conf.Sizes.Alignof(tv.Type)
}
}
+
+// Issue #53884.
+func TestAtomicAlign(t *testing.T) {
+ const src = `
+package main
+
+import "sync/atomic"
+
+var s struct {
+ x int32
+ y atomic.Int64
+ z int64
+}
+`
+
+ want := []int64{0, 8, 16}
+ for _, arch := range []string{"386", "amd64"} {
+ t.Run(arch, func(t *testing.T) {
+ conf := types.Config{
+ Importer: importer.Default(),
+ Sizes: types.SizesFor("gc", arch),
+ }
+ ts := findStructTypeConfig(t, src, &conf)
+ var fields []*types.Var
+ // Make a copy manually :(
+ for i := 0; i < ts.NumFields(); i++ {
+ fields = append(fields, ts.Field(i))
+ }
+
+ offsets := conf.Sizes.Offsetsof(fields)
+ if offsets[0] != want[0] || offsets[1] != want[1] || offsets[2] != want[2] {
+ t.Errorf("OffsetsOf(%v) = %v want %v", ts, offsets, want)
+ }
+ })
+ }
+}