}
var files []*ast.File
if b.mode&UseGCImporter != 0 {
- typkg, err = types.GcImport(&b.typechecker, imports, path)
+ typkg, err = types.GcImport(imports, path)
} else {
files, err = b.loader(b.Prog.Files, path)
if err == nil {
// Otherwise, GcImporter is called.
Import Importer
- // If Alignof != nil, it is called to determine alignment.
- // Otherwise DefaultAlignmentof is called.
- // Alignof must return a size > 0, in bytes. It is not called
- // for arrays and structs (those alignments are based on the
- // alignment of the array elements or struct fields, respectively).
+ // If Alignof != nil, it is called to determine the alignment
+ // of the given type. Otherwise DefaultAlignmentof is called.
+ // Alignof must implement the alignment guarantees required by
+ // the spec.
Alignof func(Type) int64
- // If Sizeof != nil, it is called to determine sizes of types.
- // Otherwise, DefaultSizeof is called.
- // Sizeof must return a size >= 0, in bytes. It is not called
- // for arrays and structs (those sizes are based on the sizes
- // of the array elements or struct fields, respectively).
+ // 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 []*Field) []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
}
if x.mode == invalid {
goto Error
}
- if !x.isAssignable(m.Key) {
+ if !x.isAssignable(check.ctxt, m.Key) {
check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.Key)
goto Error
}
var sizes []interface{} // constant integer arguments, if any
for _, arg := range args[1:] {
check.expr(x, arg, nil, iota)
- if x.isInteger() {
+ if x.isInteger(check.ctxt) {
if x.mode == constant {
if isNegConst(x.val) {
check.invalidArg(x.pos(), "%s must not be negative", x)
}
sel := arg.Sel.Name
res := lookupField(x.typ, QualifiedName{check.pkg, arg.Sel.Name})
- if res.mode != variable {
+ if res.index == nil {
check.invalidArg(x.pos(), "%s has no single field %s", x, sel)
goto Error
}
+ offs := check.ctxt.offsetof(x.typ, res.index)
+ if offs < 0 {
+ check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, x)
+ goto Error
+ }
x.mode = constant
- x.val = res.offset
+ x.val = offs
x.typ = Typ[Uintptr]
case _Sizeof:
check.invalidArg(x.pos(), "%s must be a float32, float64, or an untyped non-complex numeric constant", x)
return false
}
-
-func (ctxt *Context) alignof(typ Type) int64 {
- // For arrays and structs, alignment is defined in terms
- // of alignment of the elements and fields, respectively.
- switch typ := underlying(typ).(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 ctxt.alignof(typ.Elt)
- case *Struct:
- // spec: "For a variable x of struct type: unsafe.Alignof(x)
- // is the largest of of the values unsafe.Alignof(x.f) for
- // each field f of x, but at least 1."
- return typ.Alignment
- }
- // externally defined Alignof
- if f := ctxt.Alignof; f != nil {
- if a := f(typ); a > 0 {
- return a
- }
- panic("Context.Alignof returned value < 1")
- }
- // all other cases
- return DefaultAlignof(typ)
-}
-
-// 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 Context.Alignof == nil.
-func DefaultAlignof(typ Type) int64 {
- 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
-}
-
-func (ctxt *Context) sizeof(typ Type) int64 {
- // For arrays and structs, size is defined in terms
- // of size of the elements and fields, respectively.
- switch typ := underlying(typ).(type) {
- case *Array:
- return ctxt.sizeof(typ.Elt) * typ.Len // may be 0
- case *Struct:
- return typ.Size
- }
- // externally defined Sizeof
- if f := ctxt.Sizeof; f != nil {
- if s := f(typ); s >= 0 {
- return s
- }
- panic("Context.Sizeof returned value < 0")
- }
- // all other cases
- return DefaultSizeof(typ)
-}
-
-// DefaultPtrSize is the default size of pointers, in bytes,
-// used by DefaultSizeof.
-const DefaultPtrSize = 8
-
-// DefaultSizeof implements the default size computation
-// for unsafe.Sizeof. It is used if Context.Sizeof == nil.
-func DefaultSizeof(typ Type) int64 {
- switch typ := underlying(typ).(type) {
- case *Basic:
- if s := typ.size; s > 0 {
- return s
- }
- if typ.Kind == String {
- return DefaultPtrSize * 2
- }
- case *Array:
- return DefaultSizeof(typ.Elt) * typ.Len // may be 0
- case *Slice:
- return DefaultPtrSize * 3
- case *Struct:
- return typ.Size // may be 0
- case *Signature:
- return DefaultPtrSize * 2
- }
- return DefaultPtrSize // catch-all
-}
// resolve identifiers
imp := ctxt.Import
if imp == nil {
- imp = func(imports map[string]*Package, path string) (pkg *Package, err error) {
- return GcImport(ctxt, imports, path)
- }
+ imp = GcImport
}
methods := check.resolve(imp)
)
// TODO(gri) At the moment, constants are different types
-// passed around as interface{} values. Consider introducing
-// a Const type and use methods instead of xConst functions.
+// passed around as interface{} values. Introduce a Const
+// interface and use methods instead of xConst functions.
// Representation of constant values.
//
return "nil"
}
-// Implementation-specific constants.
-// TODO(gri) These need to go elsewhere.
-const (
- intBits = 32
- ptrBits = 64
-)
-
// Frequently used values.
var (
nilConst = NilType{}
// be represented as a value of the basic type Typ[as] without loss
// of precision.
//
-func isRepresentableConst(x interface{}, as BasicKind) bool {
+func isRepresentableConst(x interface{}, ctxt *Context, as BasicKind) bool {
switch x := x.(type) {
case bool:
return as == Bool || as == UntypedBool
case int64:
switch as {
case Int:
- return -1<<(intBits-1) <= x && x <= 1<<(intBits-1)-1
+ var s = uint(ctxt.sizeof(Typ[as])) * 8
+ return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1
case Int8:
- return -1<<(8-1) <= x && x <= 1<<(8-1)-1
+ const s = 8
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
case Int16:
- return -1<<(16-1) <= x && x <= 1<<(16-1)-1
- case Int32, UntypedRune:
- return -1<<(32-1) <= x && x <= 1<<(32-1)-1
+ const s = 16
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
+ case Int32:
+ const s = 32
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
case Int64:
return true
- case Uint:
- return 0 <= x && x <= 1<<intBits-1
+ case Uint, Uintptr:
+ var s = uint(ctxt.sizeof(Typ[as])) * 8
+ return 0 <= x && x <= int64(1)<<(s-1)-1
case Uint8:
- return 0 <= x && x <= 1<<8-1
+ const s = 8
+ return 0 <= x && x <= 1<<s-1
case Uint16:
- return 0 <= x && x <= 1<<16-1
+ const s = 16
+ return 0 <= x && x <= 1<<s-1
case Uint32:
- return 0 <= x && x <= 1<<32-1
+ const s = 32
+ return 0 <= x && x <= 1<<s-1
case Uint64:
return 0 <= x
- case Uintptr:
- assert(ptrBits == 64)
- return 0 <= x
case Float32:
return true // TODO(gri) fix this
case Float64:
case *big.Int:
switch as {
- case Uint:
- return x.Sign() >= 0 && x.BitLen() <= intBits
+ case Uint, Uintptr:
+ var s = uint(ctxt.sizeof(Typ[as])) * 8
+ return x.Sign() >= 0 && x.BitLen() <= int(s)
case Uint64:
return x.Sign() >= 0 && x.BitLen() <= 64
- case Uintptr:
- return x.Sign() >= 0 && x.BitLen() <= ptrBits
case Float32:
return true // TODO(gri) fix this
case Float64:
}
// unaryOpConst returns the result of the constant evaluation op x where x is of the given type.
-func unaryOpConst(x interface{}, op token.Token, typ *Basic) interface{} {
+func unaryOpConst(x interface{}, ctxt *Context, op token.Token, typ *Basic) interface{} {
switch op {
case token.ADD:
return x // nothing to do
// thus "too large": We must limit the result size to
// the type's size.
if typ.Info&IsUnsigned != 0 {
- s := uint(typ.size) * 8
- if s == 0 {
- // platform-specific type
- // TODO(gri) this needs to be factored out
- switch typ.Kind {
- case Uint:
- s = intBits
- case Uintptr:
- s = ptrBits
- default:
- unreachable()
- }
- }
- // z &^= (-1)<<s
- z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s))
+ s := uint(ctxt.sizeof(typ)) * 8
+ z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s)) // z &^= (-1)<<s
}
return normalizeIntConst(&z)
case token.NOT:
// TODO(gri) implement this
} else {
// non-constant conversion
- if !x.isConvertible(typ) {
+ if !x.isConvertible(check.ctxt, typ) {
check.invalidOp(conv.Pos(), "cannot convert %s to %s", x, typ)
goto Error
}
x.mode = invalid
}
-func (x *operand) isConvertible(T Type) bool {
+func (x *operand) isConvertible(ctxt *Context, T Type) bool {
// "x is assignable to T"
- if x.isAssignable(T) {
+ if x.isAssignable(ctxt, T) {
return true
}
var typ Type // current field typ
var tag string // current field tag
add := func(name string, isAnonymous bool) {
- fields = append(fields, &Field{QualifiedName{check.pkg, name}, typ, tag, 0, isAnonymous})
+ fields = append(fields, &Field{QualifiedName{check.pkg, name}, typ, tag, isAnonymous})
}
for _, f := range list.List {
return
}
-// align returns the smallest y >= x such that y % a == 0.
-func align(x, a int64) int64 {
- y := x + a - 1
- return y - y%a
-}
-
-func (ctxt *Context) newStruct(fields []*Field) *Struct {
- // spec: "For a variable x of struct type: unsafe.Alignof(x) is the largest of
- // of the values unsafe.Alignof(x.f) for each field f of x, but at least 1."
- maxAlign := int64(1)
- var offset int64
- for _, f := range fields {
- a := ctxt.alignof(f.Type)
- if a > maxAlign {
- maxAlign = a
- }
- offset = align(offset, a)
- f.Offset = offset
- offset += ctxt.sizeof(f.Type)
- }
- return &Struct{fields, maxAlign, offset}
-}
-
type opPredicates map[token.Token]func(Type) bool
var unaryOpPredicates = opPredicates{
if x.mode == constant {
typ := underlying(x.typ).(*Basic)
- x.val = unaryOpConst(x.val, op, typ)
+ x.val = unaryOpConst(x.val, check.ctxt, op, typ)
// Typed constants must be representable in
// their type after each constant operation.
check.isRepresentable(x, typ)
return
}
- if !isRepresentableConst(x.val, typ.Kind) {
+ if !isRepresentableConst(x.val, check.ctxt, typ.Kind) {
var msg string
if isNumeric(x.typ) && isNumeric(typ) {
msg = "%s overflows %s"
// TODO(gri) deal with interface vs non-interface comparison
valid := false
- if x.isAssignable(y.typ) || y.isAssignable(x.typ) {
+ if x.isAssignable(check.ctxt, y.typ) || y.isAssignable(check.ctxt, x.typ) {
switch op {
case token.EQL, token.NEQ:
valid = isComparable(x.typ) ||
switch {
case isInteger(y.typ) && isUnsigned(y.typ):
// nothing to do
- case y.mode == constant && isUntyped(y.typ) && isRepresentableConst(y.val, UntypedInt):
+ case y.mode == constant && isUntyped(y.typ) && isRepresentableConst(y.val, check.ctxt, UntypedInt):
y.typ = Typ[UntypedInt]
default:
check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y)
// constant shift - accept values of any (untyped) type
// as long as the value is representable as an integer
if x.mode == constant && isUntyped(x.typ) {
- if isRepresentableConst(x.val, UntypedInt) {
+ if isRepresentableConst(x.val, check.ctxt, UntypedInt) {
x.typ = Typ[UntypedInt]
}
}
var x operand
check.expr(&x, index, nil, iota)
- if !x.isInteger() {
+ if !x.isInteger(check.ctxt) {
check.errorf(x.pos(), "index %s must be integer", &x)
return -1
}
// check element against composite literal element type
var x operand
check.expr(&x, eval, typ, iota)
- if !x.isAssignable(typ) {
+ if !x.isAssignable(check.ctxt, typ) {
check.errorf(x.pos(), "cannot use %s as %s value in array or slice literal", &x, typ)
}
}
check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key)
continue
}
- i := utyp.fieldIndex(key.Name)
+ i := utyp.fieldIndex(QualifiedName{check.pkg, key.Name})
if i < 0 {
check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name)
continue
visited[i] = true
check.expr(x, kv.Value, nil, iota)
etyp := fields[i].Type
- if !x.isAssignable(etyp) {
+ if !x.isAssignable(check.ctxt, etyp) {
check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp)
continue
}
}
// i < len(fields)
etyp := fields[i].Type
- if !x.isAssignable(etyp) {
+ if !x.isAssignable(check.ctxt, etyp) {
check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp)
continue
}
}
check.compositeLitKey(kv.Key)
check.expr(x, kv.Key, nil, iota)
- if !x.isAssignable(utyp.Key) {
+ if !x.isAssignable(check.ctxt, utyp.Key) {
check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.Key)
continue
}
visited[x.val] = true
}
check.expr(x, kv.Value, utyp.Elt, iota)
- if !x.isAssignable(utyp.Elt) {
+ if !x.isAssignable(check.ctxt, utyp.Elt) {
check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.Elt)
continue
}
case *Map:
var key operand
check.expr(&key, e.Index, nil, iota)
- if key.mode == invalid || !key.isAssignable(typ.Key) {
+ if key.mode == invalid || !key.isAssignable(check.ctxt, typ.Key) {
check.invalidOp(x.pos(), "cannot use %s as map index of type %s", &key, typ.Key)
goto Error
}
// a constant) even if the string and the indices
// are constant
x.mode = value
- // x.typ doesn't change
+ // x.typ doesn't change, but if it is an untyped
+ // string it becomes string (see also issue 4913).
+ if typ.Kind == UntypedString {
+ x.typ = Typ[String]
+ }
}
case *Array:
case *ast.StructType:
x.mode = typexpr
- x.typ = check.ctxt.newStruct(check.collectFields(e.Fields, cycleOk))
+ x.typ = &Struct{Fields: check.collectFields(e.Fields, cycleOk)}
case *ast.FuncType:
params, isVariadic := check.collectParams(e.Params, true)
// can be used directly, and there is no need to call this function (but
// there is also no harm but for extra time used).
//
-func GcImportData(ctxt *Context, imports map[string]*Package, filename, id string, data *bufio.Reader) (pkg *Package, err error) {
+func GcImportData(imports map[string]*Package, filename, id string, data *bufio.Reader) (pkg *Package, err error) {
// support for gcParser error handling
defer func() {
if r := recover(); r != nil {
}()
var p gcParser
- p.init(ctxt, filename, id, data, imports)
+ p.init(filename, id, data, imports)
pkg = p.parseExport()
return
// The imports map must contains all packages already imported.
// GcImport satisfies the ast.Importer signature.
//
-func GcImport(ctxt *Context, imports map[string]*Package, path string) (pkg *Package, err error) {
+func GcImport(imports map[string]*Package, path string) (pkg *Package, err error) {
if path == "unsafe" {
return Unsafe, nil
}
return
}
- pkg, err = GcImportData(ctxt, imports, filename, id, buf)
+ pkg, err = GcImportData(imports, filename, id, buf)
return
}
// gcParser parses the exports inside a gc compiler-produced
// object/archive file and populates its scope with the results.
type gcParser struct {
- ctxt *Context
scanner scanner.Scanner
tok rune // current token
lit string // literal string; only valid for Ident, Int, String tokens
imports map[string]*Package // package id -> package object
}
-func (p *gcParser) init(ctxt *Context, filename, id string, src io.Reader, imports map[string]*Package) {
- p.ctxt = ctxt
+func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]*Package) {
p.scanner.Init(src)
p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
}
p.expect('}')
- return p.ctxt.newStruct(fields)
+ return &Struct{Fields: fields}
}
// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
func testPath(t *testing.T, path string) bool {
t0 := time.Now()
- _, err := GcImport(&Context{}, imports, path)
+ _, err := GcImport(imports, path)
if err != nil {
t.Errorf("testPath(%s): %s", path, err)
return false
importPath := s[0]
objName := s[1]
- pkg, err := GcImport(&Context{}, imports, importPath)
+ pkg, err := GcImport(imports, importPath)
if err != nil {
t.Error(err)
continue
// overlapping in functionality. Need to simplify and clean up.
// isAssignable reports whether x is assignable to a variable of type T.
-func (x *operand) isAssignable(T Type) bool {
+func (x *operand) isAssignable(ctxt *Context, T Type) bool {
if x.mode == invalid || T == Typ[Invalid] {
return true // avoid spurious errors
}
switch t := Tu.(type) {
case *Basic:
if x.mode == constant {
- return isRepresentableConst(x.val, t.Kind)
+ return isRepresentableConst(x.val, ctxt, t.Kind)
}
// The result of a comparison is an untyped boolean,
// but may not be a constant.
}
// isInteger reports whether x is a (typed or untyped) integer value.
-func (x *operand) isInteger() bool {
+func (x *operand) isInteger(ctxt *Context) bool {
return x.mode == invalid ||
isInteger(x.typ) ||
- x.mode == constant && isRepresentableConst(x.val, UntypedInt)
+ x.mode == constant && isRepresentableConst(x.val, ctxt, UntypedInt)
}
// lookupResult represents the result of a struct field/method lookup.
-// TODO(gri) mode (variable for fields vs value for methods) and offset
-// (>= 0 vs <0) provide redundant data - simplify!
type lookupResult struct {
- mode operandMode
- typ Type
- offset int64 // byte offset for struct fields, <0 for methods
+ mode operandMode
+ typ Type
+ index []int // field index sequence; nil for methods
}
type embeddedType struct {
typ *NamedType
- multiples bool // if set, typ is embedded multiple times at the same level
+ index []int // field index sequence
+ multiples bool // if set, typ is embedded multiple times at the same level
}
// lookupFieldBreadthFirst searches all types in list for a single entry (field
var next []embeddedType
// potentialMatch is invoked every time a match is found.
- potentialMatch := func(multiples bool, mode operandMode, typ Type, offset int64) bool {
+ potentialMatch := func(multiples bool, mode operandMode, typ Type) bool {
if multiples || res.mode != invalid {
// name appeared already at this level - annihilate
res.mode = invalid
// first appearance of name
res.mode = mode
res.typ = typ
- res.offset = offset
+ res.index = nil
return true
}
for _, m := range typ.Methods {
if name.IsSame(m.QualifiedName) {
assert(m.Type != nil)
- if !potentialMatch(e.multiples, value, m.Type, -1) {
+ if !potentialMatch(e.multiples, value, m.Type) {
return // name collision
}
}
switch t := typ.Underlying.(type) {
case *Struct:
// look for a matching field and collect embedded types
- for _, f := range t.Fields {
+ for i, f := range t.Fields {
if name.IsSame(f.QualifiedName) {
assert(f.Type != nil)
- if !potentialMatch(e.multiples, variable, f.Type, f.Offset) {
+ if !potentialMatch(e.multiples, variable, f.Type) {
return // name collision
}
+ var index []int
+ index = append(index, e.index...) // copy e.index
+ index = append(index, i)
+ res.index = index
continue
}
// Collect embedded struct fields for searching the next
// Ignore embedded basic types - only user-defined
// named types can have methods or have struct fields.
if t, _ := deref(f.Type).(*NamedType); t != nil {
- next = append(next, embeddedType{t, e.multiples})
+ var index []int
+ index = append(index, e.index...) // copy e.index
+ index = append(index, i)
+ next = append(next, embeddedType{t, index, e.multiples})
}
}
}
for _, m := range t.Methods {
if name.IsSame(m.QualifiedName) {
assert(m.Type != nil)
- if !potentialMatch(e.multiples, value, m.Type, -1) {
+ if !potentialMatch(e.multiples, value, m.Type) {
return // name collision
}
}
for _, m := range t.Methods {
if name.IsSame(m.QualifiedName) {
assert(m.Type != nil)
- return lookupResult{value, m.Type, -1}
+ return lookupResult{value, m.Type, nil}
}
}
typ = t.Underlying
switch t := typ.(type) {
case *Struct:
var next []embeddedType
- for _, f := range t.Fields {
+ for i, f := range t.Fields {
if name.IsSame(f.QualifiedName) {
- return lookupResult{variable, f.Type, f.Offset}
+ return lookupResult{variable, f.Type, []int{i}}
}
if f.IsAnonymous {
// Possible optimization: If the embedded type
// Ignore embedded basic types - only user-defined
// named types can have methods or have struct fields.
if t, _ := deref(f.Type).(*NamedType); t != nil {
- next = append(next, embeddedType{typ: t})
+ next = append(next, embeddedType{t, []int{i}, false})
}
}
}
case *Interface:
for _, m := range t.Methods {
if name.IsSame(m.QualifiedName) {
- return lookupResult{value, m.Type, -1}
+ return lookupResult{value, m.Type, nil}
}
}
}
--- /dev/null
+// Copyright 2013 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.
+
+// This file implements support for (unsafe) Alignof, Offsetof, and Sizeof.
+
+package types
+
+func (ctxt *Context) alignof(typ Type) int64 {
+ if f := ctxt.Alignof; f != nil {
+ if a := f(typ); a >= 1 {
+ return a
+ }
+ panic("Context.Alignof returned an alignment < 1")
+ }
+ return DefaultAlignof(typ)
+}
+
+func (ctxt *Context) offsetsof(s *Struct) []int64 {
+ offsets := s.offsets
+ if offsets == nil {
+ // compute offsets on demand
+ if f := ctxt.Offsetsof; f != nil {
+ offsets = f(s.Fields)
+ // sanity checks
+ if len(offsets) != len(s.Fields) {
+ panic("Context.Offsetsof returned the wrong number of offsets")
+ }
+ for _, o := range offsets {
+ if o < 0 {
+ panic("Context.Offsetsof returned an offset < 0")
+ }
+ }
+ } else {
+ offsets = DefaultOffsetsof(s.Fields)
+ }
+ s.offsets = offsets
+ }
+ return offsets
+}
+
+// offsetof returns the offset of the field specified via
+// the index sequence relative to typ. It returns a value
+// < 0 if the field is in an embedded pointer type.
+func (ctxt *Context) offsetof(typ Type, index []int) int64 {
+ var o int64
+ for _, i := range index {
+ s, _ := underlying(typ).(*Struct)
+ if s == nil {
+ return -1
+ }
+ o += ctxt.offsetsof(s)[i]
+ typ = s.Fields[i].Type
+ }
+ return o
+}
+
+func (ctxt *Context) sizeof(typ Type) int64 {
+ if f := ctxt.Sizeof; f != nil {
+ if s := f(typ); s >= 0 {
+ return s
+ }
+ panic("Context.Sizeof returned a size < 0")
+ }
+ return DefaultSizeof(typ)
+}
+
+// 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 Context.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 := underlying(typ).(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.Type); 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.
+func align(x, a int64) int64 {
+ y := x + a - 1
+ return y - y%a
+}
+
+// DefaultOffsetsof implements the default field offset computation
+// for unsafe.Offsetof. It is used if Context.Offsetsof == nil.
+func DefaultOffsetsof(fields []*Field) []int64 {
+ offsets := make([]int64, len(fields))
+ var o int64
+ for i, f := range fields {
+ a := DefaultAlignof(f.Type)
+ o = align(o, a)
+ offsets[i] = o
+ o += DefaultSizeof(f.Type)
+ }
+ 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 Context.Sizeof == nil.
+func DefaultSizeof(typ Type) int64 {
+ switch t := underlying(typ).(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 := len(t.Fields)
+ 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].Type)
+ case *Signature:
+ return DefaultPtrSize * 2
+ }
+ return DefaultPtrSize // catch-all
+}
check.convertUntyped(x, z.typ)
- if !x.isAssignable(z.typ) {
+ if !x.isAssignable(check.ctxt, z.typ) {
check.errorf(x.pos(), "cannot assign %s to %s", x, z)
x.mode = invalid
}
if ch.mode == invalid || x.mode == invalid {
return
}
- if tch, ok := underlying(ch.typ).(*Chan); !ok || tch.Dir&ast.SEND == 0 || !x.isAssignable(tch.Elt) {
+ if tch, ok := underlying(ch.typ).(*Chan); !ok || tch.Dir&ast.SEND == 0 || !x.isAssignable(check.ctxt, tch.Elt) {
check.invalidOp(ch.pos(), "cannot send %s to channel %s", &x, &ch)
}
}
// assuming types.DefaultPtrSize == 8
-type S struct{ // offset
+type S0 struct{ // offset
a bool // 0
b rune // 4
c *int // 8
e complex128 // 24
} // 40
+type S1 struct{ // offset
+ x float32 // 0
+ y string // 8
+ z *S1 // 24
+ S0 // 32
+} // 72
+
+type S2 struct{ // offset
+ *S1 // 0
+} // 8
+
func _Alignof() {
var x int
_ = unsafe /* ERROR "argument" */ .Alignof()
_ = unsafe.Alignof(new(struct{}))
unsafe /* ERROR "not used" */ .Alignof(x)
- var y S
+ var y S0
assert(unsafe.Alignof(y.a) == 1)
assert(unsafe.Alignof(y.b) == 4)
assert(unsafe.Alignof(y.c) == 8)
_ = unsafe.Offsetof((((((((x))).f)))))
unsafe /* ERROR "not used" */ .Offsetof(x.f)
- var y S
- assert(unsafe.Offsetof(y.a) == 0)
- assert(unsafe.Offsetof(y.b) == 4)
- assert(unsafe.Offsetof(y.c) == 8)
- assert(unsafe.Offsetof(y.d) == 16)
- assert(unsafe.Offsetof(y.e) == 24)
+ var y0 S0
+ assert(unsafe.Offsetof(y0.a) == 0)
+ assert(unsafe.Offsetof(y0.b) == 4)
+ assert(unsafe.Offsetof(y0.c) == 8)
+ assert(unsafe.Offsetof(y0.d) == 16)
+ assert(unsafe.Offsetof(y0.e) == 24)
+
+ var y1 S1
+ assert(unsafe.Offsetof(y1.x) == 0)
+ assert(unsafe.Offsetof(y1.y) == 8)
+ assert(unsafe.Offsetof(y1.z) == 24)
+ assert(unsafe.Offsetof(y1.S0) == 32)
+
+ assert(unsafe.Offsetof(y1.S0.a) == 0) // relative to S0
+ assert(unsafe.Offsetof(y1.a) == 32) // relative to S1
+ assert(unsafe.Offsetof(y1.b) == 36) // relative to S1
+ assert(unsafe.Offsetof(y1.c) == 40) // relative to S1
+ assert(unsafe.Offsetof(y1.d) == 48) // relative to S1
+ assert(unsafe.Offsetof(y1.e) == 56) // relative to S1
+
+ var y2 S2
+ assert(unsafe.Offsetof(y2.S1) == 0)
+ _ = unsafe.Offsetof(y2 /* ERROR "embedded via pointer" */ .x)
}
func _Sizeof() {
assert(unsafe.Sizeof(complex64(0)) == 8)
assert(unsafe.Sizeof(complex128(0)) == 16)
- var y S
- assert(unsafe.Sizeof(y.a) == 1)
- assert(unsafe.Sizeof(y.b) == 4)
- assert(unsafe.Sizeof(y.c) == 8)
- assert(unsafe.Sizeof(y.d) == 1)
- assert(unsafe.Sizeof(y.e) == 16)
- assert(unsafe.Sizeof(y) == 40)
+ var y0 S0
+ assert(unsafe.Sizeof(y0.a) == 1)
+ assert(unsafe.Sizeof(y0.b) == 4)
+ assert(unsafe.Sizeof(y0.c) == 8)
+ assert(unsafe.Sizeof(y0.d) == 1)
+ assert(unsafe.Sizeof(y0.e) == 16)
+ assert(unsafe.Sizeof(y0) == 40)
+
+ var y1 S1
+ assert(unsafe.Sizeof(y1) == 72)
+
+ var y2 S2
+ assert(unsafe.Sizeof(y2) == 8)
}
// self-testing only
v4 = r + 2147483647
v5 = r + 2147483648 /* ERROR "overflows" */
v6 = 42
- v7 = v6 + 2147483647
- v8 = v6 + 2147483648 /* ERROR "overflows" */
+ v7 = v6 + 9223372036854775807
+ v8 = v6 + 9223372036854775808 /* ERROR "overflows" */
v9 = i + 1 << 10
v10 byte = 1024 /* ERROR "overflows" */
v11 = xx/yy*yy - xx
_ = ""[0 /* ERROR "index .* out of bounds" */ ]
_ = s[1<<30] // no compile-time error here
+
+ // issue 4913
+ type mystring string
+ var ss string
+ var ms mystring
+ var i, j int
+ ss = "foo"[1:2]
+ ss = "foo"[i:j]
+ ms = "foo" /* ERROR "cannot assign" */ [1:2]
+ ms = "foo" /* ERROR "cannot assign" */ [i:j]
}
type T struct {
QualifiedName
Type Type
Tag string
- Offset int64 // offset within struct, in bytes
IsAnonymous bool
}
// A Struct represents a struct type struct{...}.
type Struct struct {
- Fields []*Field
- Alignment int64 // struct alignment in bytes
- Size int64 // struct size in bytes
+ Fields []*Field
+ offsets []int64 // field offsets in bytes, lazily computed
}
-func (typ *Struct) fieldIndex(name string) int {
+func (typ *Struct) fieldIndex(name QualifiedName) int {
for i, f := range typ.Fields {
- if f.Name == name {
+ if f.QualifiedName.IsSame(name) {
return i
}
}