1
0
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:
Robert Griesemer 2013-10-15 14:51:52 -07:00
parent fea69e5bab
commit 5fcdb7b3ff
5 changed files with 148 additions and 139 deletions

View File

@ -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.

View File

@ -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

View File

@ -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:

View File

@ -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
}

View File

@ -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