mirror of
https://github.com/golang/go
synced 2024-11-19 01:44:40 -07:00
go.tools/go/types: provide Sizes interface instead of individual sizing functions
This will make it a bit easier to create commonly used "custom" sizes for types. With this CL, interfaces are now by default 2*WordSize (= 16) instead of 1*WordSize as before. Also: minor unrelated cleanups. R=adonovan CC=golang-dev https://golang.org/cl/14719043
This commit is contained in:
parent
fea69e5bab
commit
5fcdb7b3ff
@ -85,22 +85,9 @@ type Config struct {
|
|||||||
// the package.
|
// the package.
|
||||||
Import func(imports map[string]*Package, path string) (pkg *Package, err error)
|
Import func(imports map[string]*Package, path string) (pkg *Package, err error)
|
||||||
|
|
||||||
// If Alignof != nil, it is called to determine the alignment
|
// If Sizes != nil, it provides the sizing functions for package unsafe.
|
||||||
// of the given type. Otherwise DefaultAlignmentof is called.
|
// Otherwise &StdSize{WordSize: 8, MaxAlign: 8} is used instead.
|
||||||
// Alignof must implement the alignment guarantees required by
|
Sizes Sizes
|
||||||
// the spec.
|
|
||||||
Alignof func(Type) int64
|
|
||||||
|
|
||||||
// If Offsetsof != nil, it is called to determine the offsets
|
|
||||||
// of the given struct fields, in bytes. Otherwise DefaultOffsetsof
|
|
||||||
// is called. Offsetsof must implement the offset guarantees
|
|
||||||
// required by the spec.
|
|
||||||
Offsetsof func(fields []*Var) []int64
|
|
||||||
|
|
||||||
// If Sizeof != nil, it is called to determine the size of the
|
|
||||||
// given type. Otherwise, DefaultSizeof is called. Sizeof must
|
|
||||||
// implement the size guarantees required by the spec.
|
|
||||||
Sizeof func(Type) int64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info holds result type information for a type-checked package.
|
// Info holds result type information for a type-checked package.
|
||||||
|
@ -887,7 +887,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind {
|
|||||||
check.indent++
|
check.indent++
|
||||||
}
|
}
|
||||||
|
|
||||||
kind := check.expr0(x, e, hint)
|
kind := check.exprInternal(x, e, hint)
|
||||||
|
|
||||||
// convert x into a user-friendly set of values
|
// convert x into a user-friendly set of values
|
||||||
record := true
|
record := true
|
||||||
@ -926,10 +926,10 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind {
|
|||||||
return kind
|
return kind
|
||||||
}
|
}
|
||||||
|
|
||||||
// expr0 contains the core of type checking of expressions.
|
// exprInternal contains the core of type checking of expressions.
|
||||||
// Must only be called by rawExpr.
|
// Must only be called by rawExpr.
|
||||||
//
|
//
|
||||||
func (check *checker) expr0(x *operand, e ast.Expr, hint Type) exprKind {
|
func (check *checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
||||||
// make sure x has a valid state in case of bailout
|
// make sure x has a valid state in case of bailout
|
||||||
// (was issue 5770)
|
// (was issue 5770)
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
@ -1360,7 +1360,7 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) exprKind {
|
|||||||
*ast.InterfaceType, *ast.MapType, *ast.ChanType:
|
*ast.InterfaceType, *ast.MapType, *ast.ChanType:
|
||||||
x.mode = typexpr
|
x.mode = typexpr
|
||||||
x.typ = check.typ(e, nil, false)
|
x.typ = check.typ(e, nil, false)
|
||||||
// Note: rawExpr (caller of expr0) will call check.recordTypeAndValue
|
// Note: rawExpr (caller of exprInternal) will call check.recordTypeAndValue
|
||||||
// even though check.typ has already called it. This is fine as both
|
// even though check.typ has already called it. This is fine as both
|
||||||
// times the same expression and type are recorded. It is also not a
|
// times the same expression and type are recorded. It is also not a
|
||||||
// performance issue because we only reach here for composite literal
|
// performance issue because we only reach here for composite literal
|
||||||
|
@ -182,7 +182,7 @@ func IsIdentical(x, y Type) bool {
|
|||||||
// the same names and identical function types. Lower-case method names from
|
// the same names and identical function types. Lower-case method names from
|
||||||
// different packages are always different. The order of the methods is irrelevant.
|
// different packages are always different. The order of the methods is irrelevant.
|
||||||
if y, ok := y.(*Interface); ok {
|
if y, ok := y.(*Interface); ok {
|
||||||
return identicalMethods(x.methods, y.methods) // methods are sorted
|
return identicalMethods(x.methods, y.methods)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Map:
|
case *Map:
|
||||||
|
@ -2,39 +2,151 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// This file implements support for (unsafe) Alignof, Offsetof, and Sizeof.
|
// This file implements Sizes.
|
||||||
|
|
||||||
package types
|
package types
|
||||||
|
|
||||||
func (conf *Config) alignof(typ Type) int64 {
|
// Sizes defines the sizing functions for package unsafe.
|
||||||
if f := conf.Alignof; f != nil {
|
type Sizes interface {
|
||||||
if a := f(typ); a >= 1 {
|
// Alignof returns the alignment of a variable of type T.
|
||||||
return a
|
// Alignof must implement the alignment guarantees required by the spec.
|
||||||
}
|
Alignof(T Type) int64
|
||||||
panic("Config.Alignof returned an alignment < 1")
|
|
||||||
}
|
// Offsetsof returns the offsets of the given struct fields, in bytes.
|
||||||
return DefaultAlignof(typ)
|
// Offsetsof must implement the offset guarantees required by the spec.
|
||||||
|
Offsetsof(fields []*Var) []int64
|
||||||
|
|
||||||
|
// Sizeof returns the size of a variable of type T.
|
||||||
|
// Sizeof must implement the size guarantees required by the spec.
|
||||||
|
Sizeof(T Type) int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conf *Config) offsetsof(s *Struct) []int64 {
|
// StdSizes is a convenience type for creating commonly used Sizes.
|
||||||
offsets := s.offsets
|
// It makes the following simplifying assumptions:
|
||||||
if offsets == nil && s.NumFields() > 0 {
|
//
|
||||||
|
// - The size of explicitly sized basic types (int16, etc.) is the
|
||||||
|
// specified size.
|
||||||
|
// - The size of strings, functions, and interfaces is 2*WordSize.
|
||||||
|
// - The size of slices is 3*WordSize.
|
||||||
|
// - All other types have size WordSize.
|
||||||
|
// - Arrays and structs are aligned per spec definition; all other
|
||||||
|
// types are naturally aligned with a maximum alignment MaxAlign.
|
||||||
|
//
|
||||||
|
// *StdSizes implements Sizes.
|
||||||
|
//
|
||||||
|
type StdSizes struct {
|
||||||
|
WordSize int64 // word size in bytes - must be >= 4 (32bits)
|
||||||
|
MaxAlign int64 // maximum alignment in bytes - must be >= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StdSizes) Alignof(T 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 *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.elt)
|
||||||
|
case *Struct:
|
||||||
|
// 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 _, f := range t.fields {
|
||||||
|
if a := s.Alignof(f.typ); a > max {
|
||||||
|
max = a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if a > s.MaxAlign {
|
||||||
|
return s.MaxAlign
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
|
||||||
|
offsets := make([]int64, len(fields))
|
||||||
|
var o int64
|
||||||
|
for i, f := range fields {
|
||||||
|
a := s.Alignof(f.typ)
|
||||||
|
o = align(o, a)
|
||||||
|
offsets[i] = o
|
||||||
|
o += s.Sizeof(f.typ)
|
||||||
|
}
|
||||||
|
return offsets
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StdSizes) Sizeof(T Type) int64 {
|
||||||
|
switch t := T.Underlying().(type) {
|
||||||
|
case *Basic:
|
||||||
|
if z := t.size; z > 0 {
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
if t.kind == String {
|
||||||
|
return s.WordSize * 2
|
||||||
|
}
|
||||||
|
case *Array:
|
||||||
|
a := s.Alignof(t.elt)
|
||||||
|
z := s.Sizeof(t.elt)
|
||||||
|
return align(z, a) * t.len // may be 0
|
||||||
|
case *Slice:
|
||||||
|
return s.WordSize * 3
|
||||||
|
case *Struct:
|
||||||
|
n := t.NumFields()
|
||||||
|
if n == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
offsets := t.offsets
|
||||||
|
if t.offsets == nil {
|
||||||
|
// compute offsets on demand
|
||||||
|
offsets = stdSizes.Offsetsof(t.fields)
|
||||||
|
t.offsets = offsets
|
||||||
|
}
|
||||||
|
return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
|
||||||
|
case *Signature, *Interface:
|
||||||
|
return s.WordSize * 2
|
||||||
|
}
|
||||||
|
return s.WordSize // catch-all
|
||||||
|
}
|
||||||
|
|
||||||
|
// stdSizes is used if Config.Sizes == nil.
|
||||||
|
var stdSizes = StdSizes{8, 8}
|
||||||
|
|
||||||
|
func (conf *Config) alignof(T Type) int64 {
|
||||||
|
if s := conf.Sizes; s != nil {
|
||||||
|
if a := s.Alignof(T); a >= 1 {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
panic("Config.Sizes.Alignof returned an alignment < 1")
|
||||||
|
}
|
||||||
|
return stdSizes.Alignof(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conf *Config) offsetsof(T *Struct) []int64 {
|
||||||
|
offsets := T.offsets
|
||||||
|
if offsets == nil && T.NumFields() > 0 {
|
||||||
// compute offsets on demand
|
// compute offsets on demand
|
||||||
if f := conf.Offsetsof; f != nil {
|
if s := conf.Sizes; s != nil {
|
||||||
offsets = f(s.fields)
|
offsets = s.Offsetsof(T.fields)
|
||||||
// sanity checks
|
// sanity checks
|
||||||
if len(offsets) != s.NumFields() {
|
if len(offsets) != T.NumFields() {
|
||||||
panic("Config.Offsetsof returned the wrong number of offsets")
|
panic("Config.Sizes.Offsetsof returned the wrong number of offsets")
|
||||||
}
|
}
|
||||||
for _, o := range offsets {
|
for _, o := range offsets {
|
||||||
if o < 0 {
|
if o < 0 {
|
||||||
panic("Config.Offsetsof returned an offset < 0")
|
panic("Config.Sizes.Offsetsof returned an offset < 0")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
offsets = DefaultOffsetsof(s.fields)
|
offsets = stdSizes.Offsetsof(T.fields)
|
||||||
}
|
}
|
||||||
s.offsets = offsets
|
T.offsets = offsets
|
||||||
}
|
}
|
||||||
return offsets
|
return offsets
|
||||||
}
|
}
|
||||||
@ -52,51 +164,14 @@ func (conf *Config) offsetof(typ Type, index []int) int64 {
|
|||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conf *Config) sizeof(typ Type) int64 {
|
func (conf *Config) sizeof(T Type) int64 {
|
||||||
if f := conf.Sizeof; f != nil {
|
if s := conf.Sizes; s != nil {
|
||||||
if s := f(typ); s >= 0 {
|
if z := s.Sizeof(T); z >= 0 {
|
||||||
return s
|
return z
|
||||||
}
|
}
|
||||||
panic("Config.Sizeof returned a size < 0")
|
panic("Config.Sizes.Sizeof returned a size < 0")
|
||||||
}
|
}
|
||||||
return DefaultSizeof(typ)
|
return stdSizes.Sizeof(T)
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultMaxAlign is the default maximum alignment, in bytes,
|
|
||||||
// used by DefaultAlignof.
|
|
||||||
const DefaultMaxAlign = 8
|
|
||||||
|
|
||||||
// DefaultAlignof implements the default alignment computation
|
|
||||||
// for unsafe.Alignof. It is used if Config.Alignof == nil.
|
|
||||||
func DefaultAlignof(typ Type) int64 {
|
|
||||||
// For arrays and structs, alignment is defined in terms
|
|
||||||
// of alignment of the elements and fields, respectively.
|
|
||||||
switch t := typ.Underlying().(type) {
|
|
||||||
case *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 DefaultAlignof(t.elt)
|
|
||||||
case *Struct:
|
|
||||||
// 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 _, f := range t.fields {
|
|
||||||
if a := DefaultAlignof(f.typ); a > max {
|
|
||||||
max = a
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return max
|
|
||||||
}
|
|
||||||
a := DefaultSizeof(typ) // may be 0
|
|
||||||
// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
|
|
||||||
if a < 1 {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
if a > DefaultMaxAlign {
|
|
||||||
return DefaultMaxAlign
|
|
||||||
}
|
|
||||||
return a
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// align returns the smallest y >= x such that y % a == 0.
|
// align returns the smallest y >= x such that y % a == 0.
|
||||||
@ -104,56 +179,3 @@ func align(x, a int64) int64 {
|
|||||||
y := x + a - 1
|
y := x + a - 1
|
||||||
return y - y%a
|
return y - y%a
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultOffsetsof implements the default field offset computation
|
|
||||||
// for unsafe.Offsetof. It is used if Config.Offsetsof == nil.
|
|
||||||
func DefaultOffsetsof(fields []*Var) []int64 {
|
|
||||||
offsets := make([]int64, len(fields))
|
|
||||||
var o int64
|
|
||||||
for i, f := range fields {
|
|
||||||
a := DefaultAlignof(f.typ)
|
|
||||||
o = align(o, a)
|
|
||||||
offsets[i] = o
|
|
||||||
o += DefaultSizeof(f.typ)
|
|
||||||
}
|
|
||||||
return offsets
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultPtrSize is the default size of ints, uint, and pointers, in bytes,
|
|
||||||
// used by DefaultSizeof.
|
|
||||||
const DefaultPtrSize = 8
|
|
||||||
|
|
||||||
// DefaultSizeof implements the default size computation
|
|
||||||
// for unsafe.Sizeof. It is used if Config.Sizeof == nil.
|
|
||||||
func DefaultSizeof(typ Type) int64 {
|
|
||||||
switch t := typ.Underlying().(type) {
|
|
||||||
case *Basic:
|
|
||||||
if s := t.size; s > 0 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
if t.kind == String {
|
|
||||||
return DefaultPtrSize * 2
|
|
||||||
}
|
|
||||||
case *Array:
|
|
||||||
a := DefaultAlignof(t.elt)
|
|
||||||
s := DefaultSizeof(t.elt)
|
|
||||||
return align(s, a) * t.len // may be 0
|
|
||||||
case *Slice:
|
|
||||||
return DefaultPtrSize * 3
|
|
||||||
case *Struct:
|
|
||||||
n := t.NumFields()
|
|
||||||
if n == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
offsets := t.offsets
|
|
||||||
if t.offsets == nil {
|
|
||||||
// compute offsets on demand
|
|
||||||
offsets = DefaultOffsetsof(t.fields)
|
|
||||||
t.offsets = offsets
|
|
||||||
}
|
|
||||||
return offsets[n-1] + DefaultSizeof(t.fields[n-1].typ)
|
|
||||||
case *Signature:
|
|
||||||
return DefaultPtrSize * 2
|
|
||||||
}
|
|
||||||
return DefaultPtrSize // catch-all
|
|
||||||
}
|
|
||||||
|
@ -121,7 +121,7 @@ func (check *checker) typ(e ast.Expr, def *Named, cycleOk bool) Type {
|
|||||||
check.indent++
|
check.indent++
|
||||||
}
|
}
|
||||||
|
|
||||||
t := check.typ0(e, def, cycleOk)
|
t := check.typInternal(e, def, cycleOk)
|
||||||
assert(e != nil && t != nil && isTyped(t))
|
assert(e != nil && t != nil && isTyped(t))
|
||||||
|
|
||||||
check.recordTypeAndValue(e, t, nil)
|
check.recordTypeAndValue(e, t, nil)
|
||||||
@ -195,10 +195,10 @@ func (check *checker) funcType(recv *ast.FieldList, ftyp *ast.FuncType, def *Nam
|
|||||||
return sig
|
return sig
|
||||||
}
|
}
|
||||||
|
|
||||||
// typ0 contains the core of type checking of types.
|
// typInternal contains the core of type checking of types.
|
||||||
// Must only be called by typ.
|
// Must only be called by typ.
|
||||||
//
|
//
|
||||||
func (check *checker) typ0(e ast.Expr, def *Named, cycleOk bool) Type {
|
func (check *checker) typInternal(e ast.Expr, def *Named, cycleOk bool) Type {
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
case *ast.BadExpr:
|
case *ast.BadExpr:
|
||||||
// ignore - error reported before
|
// ignore - error reported before
|
||||||
|
Loading…
Reference in New Issue
Block a user