Revert "go/analysis: add Sizes that matches gc size computations"
This reverts CL 501495 (commit 5aa6acb96f843a0257c5c1c0e52753bcd18b77b3).
Reason for revert: Per internal discussion, we should fix go/types.
See issue #61035.
Change-Id: Ia63587af701b864acc1b43939954eae76572a407
Reviewed-on: https://go-review.googlesource.com/c/tools/+/506596
Reviewed-by: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org>
Run-TryBot: Robert Griesemer <gri@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/go/analysis/unitchecker/sizes.go b/go/analysis/unitchecker/sizes.go
deleted file mode 100644
index 332e2e4..0000000
--- a/go/analysis/unitchecker/sizes.go
+++ /dev/null
@@ -1,237 +0,0 @@
-// Copyright 2023 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 unitchecker
-
-import (
- "fmt"
- "go/types"
-)
-
-type gcSizes struct {
- WordSize int64 // word size in bytes - must be >= 4 (32bits)
- MaxAlign int64 // maximum alignment in bytes - must be >= 1
-}
-
-func (s *gcSizes) Alignof(T types.Type) int64 {
- // For arrays and structs, alignment is defined in terms
- // of alignment of the elements and fields, respectively.
- switch t := T.Underlying().(type) {
- case *types.Array:
- // spec: "For a variable x of array type: unsafe.Alignof(x)
- // is the same as unsafe.Alignof(x[0]), but at least 1."
- return s.Alignof(t.Elem())
- case *types.Struct:
- if t.NumFields() == 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."
- max := int64(1)
- for i, nf := 0, t.NumFields(); i < nf; i++ {
- if a := s.Alignof(t.Field(i).Type()); a > max {
- max = a
- }
- }
- return max
- case *types.Slice, *types.Interface:
- // Multiword data structures are effectively structs
- // in which each element has size PtrSize.
- return s.WordSize
- case *types.Basic:
- // Strings are like slices and interfaces.
- if t.Info()&types.IsString != 0 {
- return s.WordSize
- }
- }
- a := s.Sizeof(T) // may be 0
- // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
- if a < 1 {
- return 1
- }
- // complex{64,128} are aligned like [2]float{32,64}.
- if isComplex(T) {
- a /= 2
- }
- if a > s.MaxAlign {
- return s.MaxAlign
- }
- return a
-}
-
-func isComplex(T types.Type) bool {
- basic, ok := T.Underlying().(*types.Basic)
- return ok && basic.Info()&types.IsComplex != 0
-}
-
-func (s *gcSizes) Offsetsof(fields []*types.Var) []int64 {
- offsets := make([]int64, len(fields))
- var offs int64
- for i, f := range fields {
- if offs < 0 {
- // all remaining offsets are too large
- offsets[i] = -1
- continue
- }
- // offs >= 0
- typ := f.Type()
- a := s.Alignof(typ)
- offs = roundUp(offs, a) // possibly < 0 if align overflows
- offsets[i] = offs
- if d := s.Sizeof(typ); d >= 0 && offs >= 0 {
- offs += d // ok to overflow to < 0
- } else {
- offs = -1
- }
- }
- return offsets
-}
-
-func (s *gcSizes) Sizeof(T types.Type) int64 {
- switch t := T.Underlying().(type) {
- case *types.Basic:
- k := t.Kind()
- if int(k) < len(basicSizes) {
- if s := basicSizes[k]; s > 0 {
- return int64(s)
- }
- }
- switch k {
- case types.String:
- return s.WordSize * 2
- case types.Int, types.Uint, types.Uintptr, types.UnsafePointer:
- return s.WordSize
- }
- panic(fmt.Sprintf("unimplemented basic: %v (kind %v)", T, k))
- case *types.Array:
- n := t.Len()
- if n <= 0 {
- return 0
- }
- // n > 0
- // gc: Size includes alignment padding.
- esize := s.Sizeof(t.Elem())
- if esize < 0 {
- return -1 // array element too large
- }
- if esize == 0 {
- return 0 // 0-size element
- }
- // esize > 0
- // Final size is esize * n; and size must be <= maxInt64.
- const maxInt64 = 1<<63 - 1
- if esize > maxInt64/n {
- return -1 // esize * n overflows
- }
- return esize * n
- case *types.Slice:
- return s.WordSize * 3
- case *types.Struct:
- n := t.NumFields()
- if n == 0 {
- return 0
- }
- // n > 0
- fields := make([]*types.Var, n)
- for i := range fields {
- fields[i] = t.Field(i)
- }
- offsets := s.Offsetsof(fields)
- // gc: The last field of a non-zero-sized struct is not allowed to
- // have size 0.
- last := s.Sizeof(fields[n-1].Type())
- if last == 0 && offsets[n-1] > 0 {
- last = 1
- }
- // gc: Size includes alignment padding.
- return roundUp(offsets[n-1]+last, s.Alignof(t)) // may overflow to < 0 which is ok
- case *types.Interface:
- return s.WordSize * 2
- case *types.Chan, *types.Map, *types.Pointer, *types.Signature:
- return s.WordSize
- default:
- panic(fmt.Sprintf("Sizeof(%T) unimplemented", t))
- }
-}
-
-func isSyncAtomicAlign64(T types.Type) bool {
- named, ok := T.(*types.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")
-}
-
-// roundUp rounds o to a multiple of r, r is a power of 2.
-func roundUp(o int64, r int64) int64 {
- if r < 1 || r > 8 || r&(r-1) != 0 {
- panic(fmt.Sprintf("Round %d", r))
- }
- return (o + r - 1) &^ (r - 1)
-}
-
-var basicSizes = [...]byte{
- types.Invalid: 1,
- types.Bool: 1,
- types.Int8: 1,
- types.Int16: 2,
- types.Int32: 4,
- types.Int64: 8,
- types.Uint8: 1,
- types.Uint16: 2,
- types.Uint32: 4,
- types.Uint64: 8,
- types.Float32: 4,
- types.Float64: 8,
- types.Complex64: 8,
- types.Complex128: 16,
-}
-
-// common architecture word sizes and alignments
-var gcArchSizes = map[string]*gcSizes{
- "386": {4, 4},
- "amd64": {8, 8},
- "amd64p32": {4, 8},
- "arm": {4, 4},
- "arm64": {8, 8},
- "loong64": {8, 8},
- "mips": {4, 4},
- "mipsle": {4, 4},
- "mips64": {8, 8},
- "mips64le": {8, 8},
- "ppc64": {8, 8},
- "ppc64le": {8, 8},
- "riscv64": {8, 8},
- "s390x": {8, 8},
- "sparc64": {8, 8},
- "wasm": {8, 8},
- // When adding more architectures here,
- // update the doc string of sizesFor below.
-}
-
-// sizesFor returns the go/types.Sizes used by a compiler for an architecture.
-// The result is nil if a compiler/architecture pair is not known.
-func sizesFor(compiler, arch string) types.Sizes {
- if compiler != "gc" {
- return nil
- }
- s, ok := gcArchSizes[arch]
- if !ok {
- return nil
- }
- return s
-}
diff --git a/go/analysis/unitchecker/sizes_test.go b/go/analysis/unitchecker/sizes_test.go
deleted file mode 100644
index 5d98a23..0000000
--- a/go/analysis/unitchecker/sizes_test.go
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2023 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 unitchecker
-
-import (
- "go/ast"
- "go/importer"
- "go/parser"
- "go/token"
- "go/types"
- "testing"
-)
-
-type gcSizeTest struct {
- name string
- src string
-}
-
-var gcSizesTests = []gcSizeTest{
- {
- "issue60431",
- `package main
-
- import "unsafe"
-
- // The foo struct size is expected to be rounded up to 16 bytes.
- type foo struct {
- a int64
- b bool
- }
-
- func main() {
- var _ [unsafe.Sizeof(foo{}) - 16]byte
- println(unsafe.Sizeof(foo{}))
- }`,
- },
- {
- "issue60734",
- `package main
-
- import (
- "unsafe"
- )
-
- // The Data struct size is expected to be rounded up to 16 bytes.
- type Data struct {
- Value uint32 // 4 bytes
- Label [10]byte // 10 bytes
- Active bool // 1 byte
- // padded with 1 byte to make it align
- }
-
- const (
- dataSize = unsafe.Sizeof(Data{})
- dataSizeLiteral = 16
- )
-
- func main() {
- _ = [16]byte{0, 132, 95, 237, 80, 104, 111, 110, 101, 0, 0, 0, 0, 0, 1, 0}
- _ = [dataSize]byte{0, 132, 95, 237, 80, 104, 111, 110, 101, 0, 0, 0, 0, 0, 1, 0}
- _ = [dataSizeLiteral]byte{0, 132, 95, 237, 80, 104, 111, 110, 101, 0, 0, 0, 0, 0, 1, 0}
- }`,
- },
-}
-
-func TestGCSizes(t *testing.T) {
- for _, tc := range gcSizesTests {
- tc := tc
- t.Run(tc.name, func(t *testing.T) {
- t.Parallel()
- fset := token.NewFileSet()
- f, err := parser.ParseFile(fset, "x.go", tc.src, 0)
- if err != nil {
- t.Fatal(err)
- }
-
- conf := types.Config{Importer: importer.Default(), Sizes: sizesFor("gc", "amd64")}
- if _, err := conf.Check("main.go", fset, []*ast.File{f}, nil); err != nil {
- t.Fatal(err) // type error
- }
- })
- }
-}
diff --git a/go/analysis/unitchecker/unitchecker.go b/go/analysis/unitchecker/unitchecker.go
index 8334cf0..ff22d23 100644
--- a/go/analysis/unitchecker/unitchecker.go
+++ b/go/analysis/unitchecker/unitchecker.go
@@ -218,7 +218,7 @@
})
tc := &types.Config{
Importer: importer,
- Sizes: sizesFor("gc", build.Default.GOARCH), // assume gccgo ≡ gc?
+ Sizes: types.SizesFor("gc", build.Default.GOARCH), // assume gccgo ≡ gc?
}
info := &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),