Source file src/go/types/sizes_test.go

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // This file contains tests for sizes.
     6  
     7  package types_test
     8  
     9  import (
    10  	"go/ast"
    11  	"go/token"
    12  	"go/types"
    13  	"internal/testenv"
    14  	"testing"
    15  )
    16  
    17  // findStructType typechecks src and returns the first struct type encountered.
    18  func findStructType(t *testing.T, src string) *types.Struct {
    19  	return findStructTypeConfig(t, src, &types.Config{})
    20  }
    21  
    22  func findStructTypeConfig(t *testing.T, src string, conf *types.Config) *types.Struct {
    23  	types_ := make(map[ast.Expr]types.TypeAndValue)
    24  	mustTypecheck(src, nil, &types.Info{Types: types_})
    25  	for _, tv := range types_ {
    26  		if ts, ok := tv.Type.(*types.Struct); ok {
    27  			return ts
    28  		}
    29  	}
    30  	t.Fatalf("failed to find a struct type in src:\n%s\n", src)
    31  	return nil
    32  }
    33  
    34  // go.dev/issue/16316
    35  func TestMultipleSizeUse(t *testing.T) {
    36  	const src = `
    37  package main
    38  
    39  type S struct {
    40      i int
    41      b bool
    42      s string
    43      n int
    44  }
    45  `
    46  	ts := findStructType(t, src)
    47  	sizes := types.StdSizes{WordSize: 4, MaxAlign: 4}
    48  	if got := sizes.Sizeof(ts); got != 20 {
    49  		t.Errorf("Sizeof(%v) with WordSize 4 = %d want 20", ts, got)
    50  	}
    51  	sizes = types.StdSizes{WordSize: 8, MaxAlign: 8}
    52  	if got := sizes.Sizeof(ts); got != 40 {
    53  		t.Errorf("Sizeof(%v) with WordSize 8 = %d want 40", ts, got)
    54  	}
    55  }
    56  
    57  // go.dev/issue/16464
    58  func TestAlignofNaclSlice(t *testing.T) {
    59  	const src = `
    60  package main
    61  
    62  var s struct {
    63  	x *int
    64  	y []byte
    65  }
    66  `
    67  	ts := findStructType(t, src)
    68  	sizes := &types.StdSizes{WordSize: 4, MaxAlign: 8}
    69  	var fields []*types.Var
    70  	// Make a copy manually :(
    71  	for i := 0; i < ts.NumFields(); i++ {
    72  		fields = append(fields, ts.Field(i))
    73  	}
    74  	offsets := sizes.Offsetsof(fields)
    75  	if offsets[0] != 0 || offsets[1] != 4 {
    76  		t.Errorf("OffsetsOf(%v) = %v want %v", ts, offsets, []int{0, 4})
    77  	}
    78  }
    79  
    80  func TestIssue16902(t *testing.T) {
    81  	const src = `
    82  package a
    83  
    84  import "unsafe"
    85  
    86  const _ = unsafe.Offsetof(struct{ x int64 }{}.x)
    87  `
    88  	info := types.Info{Types: make(map[ast.Expr]types.TypeAndValue)}
    89  	conf := types.Config{
    90  		// TODO(adonovan): use same FileSet as mustTypecheck.
    91  		Importer: defaultImporter(token.NewFileSet()),
    92  		Sizes:    &types.StdSizes{WordSize: 8, MaxAlign: 8},
    93  	}
    94  	mustTypecheck(src, &conf, &info)
    95  	for _, tv := range info.Types {
    96  		_ = conf.Sizes.Sizeof(tv.Type)
    97  		_ = conf.Sizes.Alignof(tv.Type)
    98  	}
    99  }
   100  
   101  // go.dev/issue/53884.
   102  func TestAtomicAlign(t *testing.T) {
   103  	testenv.MustHaveGoBuild(t) // The Go command is needed for the importer to determine the locations of stdlib .a files.
   104  
   105  	const src = `
   106  package main
   107  
   108  import "sync/atomic"
   109  
   110  var s struct {
   111  	x int32
   112  	y atomic.Int64
   113  	z int64
   114  }
   115  `
   116  
   117  	want := []int64{0, 8, 16}
   118  	for _, arch := range []string{"386", "amd64"} {
   119  		t.Run(arch, func(t *testing.T) {
   120  			conf := types.Config{
   121  				// TODO(adonovan): use same FileSet as findStructTypeConfig.
   122  				Importer: defaultImporter(token.NewFileSet()),
   123  				Sizes:    types.SizesFor("gc", arch),
   124  			}
   125  			ts := findStructTypeConfig(t, src, &conf)
   126  			var fields []*types.Var
   127  			// Make a copy manually :(
   128  			for i := 0; i < ts.NumFields(); i++ {
   129  				fields = append(fields, ts.Field(i))
   130  			}
   131  
   132  			offsets := conf.Sizes.Offsetsof(fields)
   133  			if offsets[0] != want[0] || offsets[1] != want[1] || offsets[2] != want[2] {
   134  				t.Errorf("OffsetsOf(%v) = %v want %v", ts, offsets, want)
   135  			}
   136  		})
   137  	}
   138  }
   139  
   140  type gcSizeTest struct {
   141  	name string
   142  	src  string
   143  }
   144  
   145  var gcSizesTests = []gcSizeTest{
   146  	{
   147  		"issue60431",
   148  		`
   149  package main
   150  
   151  import "unsafe"
   152  
   153  // The foo struct size is expected to be rounded up to 16 bytes.
   154  type foo struct {
   155  	a int64
   156  	b bool
   157  }
   158  
   159  func main() {
   160  	assert(unsafe.Sizeof(foo{}) == 16)
   161  }`,
   162  	},
   163  	{
   164  		"issue60734",
   165  		`
   166  package main
   167  
   168  import (
   169  	"unsafe"
   170  )
   171  
   172  // The Data struct size is expected to be rounded up to 16 bytes.
   173  type Data struct {
   174  	Value  uint32   // 4 bytes
   175  	Label  [10]byte // 10 bytes
   176  	Active bool     // 1 byte
   177  	// padded with 1 byte to make it align
   178  }
   179  
   180  func main() {
   181  	assert(unsafe.Sizeof(Data{}) == 16)
   182  }
   183  `,
   184  	},
   185  }
   186  
   187  func TestGCSizes(t *testing.T) {
   188  	types.DefPredeclaredTestFuncs()
   189  	for _, tc := range gcSizesTests {
   190  		tc := tc
   191  		t.Run(tc.name, func(t *testing.T) {
   192  			t.Parallel()
   193  			conf := types.Config{
   194  				// TODO(adonovan): use same FileSet as mustTypecheck.
   195  				Importer: defaultImporter(token.NewFileSet()),
   196  				Sizes:    types.SizesFor("gc", "amd64"),
   197  			}
   198  			mustTypecheck(tc.src, &conf, nil)
   199  		})
   200  	}
   201  }
   202  

View as plain text