exp/types: fixed field/method lookup
also:
- composite literal checking close to complete
- cleaned up parameter, method, field checking
- don't let panics escape type checker
- more TODOs eliminated
R=rsc
CC=golang-dev
https://golang.org/cl/6816083
diff --git a/src/pkg/exp/types/check.go b/src/pkg/exp/types/check.go
index 1300d0a..07c16d5 100644
--- a/src/pkg/exp/types/check.go
+++ b/src/pkg/exp/types/check.go
@@ -36,7 +36,7 @@
//
// TODO(gri) This is very similar to the declare function in go/parser; it
// is only used to associate methods with their respective receiver base types.
-// In a future version, it might be simpler and cleaner do to all the resolution
+// In a future version, it might be simpler and cleaner to do all the resolution
// in the type-checking phase. It would simplify the parser, AST, and also
// reduce some amount of code duplication.
//
@@ -188,11 +188,7 @@
case ast.Fun:
fdecl := obj.Decl.(*ast.FuncDecl)
- if fdecl.Recv != nil {
- // This will ensure that the method base type is
- // type-checked
- check.collectFields(token.FUNC, fdecl.Recv, true)
- }
+ check.collectParams(fdecl.Recv) // ensure method base is type-checked
ftyp := check.typ(fdecl.Type, cycleOk).(*Signature)
obj.Type = ftyp
check.function(ftyp, fdecl.Body)
@@ -355,12 +351,19 @@
check.mapf = f
check.initexprs = make(map[*ast.ValueSpec][]ast.Expr)
- // handle bailouts
+ // handle panics
defer func() {
- if p := recover(); p != nil {
- _ = p.(bailout) // re-panic if not a bailout
+ switch p := recover().(type) {
+ case nil:
+ // normal return - nothing to do
+ case bailout:
+ // early exit
+ err = check.firsterr
+ default:
+ // unexpected panic: don't crash clients
+ // panic(p) // enable for debugging
+ err = fmt.Errorf("types.check internal error: %v", p)
}
- err = check.firsterr
}()
// determine missing constant initialization expressions
diff --git a/src/pkg/exp/types/check_test.go b/src/pkg/exp/types/check_test.go
index bfa4d2c..3a0cc04 100644
--- a/src/pkg/exp/types/check_test.go
+++ b/src/pkg/exp/types/check_test.go
@@ -48,6 +48,7 @@
{"decls0", []string{"testdata/decls0.src"}},
{"decls1", []string{"testdata/decls1.src"}},
{"decls2", []string{"testdata/decls2a.src", "testdata/decls2b.src"}},
+ {"decls3", []string{"testdata/decls3.src"}},
{"const0", []string{"testdata/const0.src"}},
{"expr0", []string{"testdata/expr0.src"}},
{"expr1", []string{"testdata/expr1.src"}},
diff --git a/src/pkg/exp/types/expr.go b/src/pkg/exp/types/expr.go
index 58a33d0..c952507 100644
--- a/src/pkg/exp/types/expr.go
+++ b/src/pkg/exp/types/expr.go
@@ -17,70 +17,98 @@
// - simplify invalid handling: maybe just use Typ[Invalid] as marker, get rid of invalid Mode for values?
// - rethink error handling: should all callers check if x.mode == valid after making a call?
-func (check *checker) tag(field *ast.Field) string {
- if t := field.Tag; t != nil {
- assert(t.Kind == token.STRING)
- if tag, err := strconv.Unquote(t.Value); err == nil {
- return tag
+func (check *checker) collectParams(list *ast.FieldList) (params ObjList, isVariadic bool) {
+ if list == nil {
+ return
+ }
+ for _, field := range list.List {
+ ftype := field.Type
+ if t, ok := ftype.(*ast.Ellipsis); ok {
+ ftype = t.Elt
+ isVariadic = true
+ }
+ // the parser ensures that f.Tag is nil and we don't
+ // care if a constructed AST contains a non-nil tag
+ typ := check.typ(ftype, true)
+ if len(field.Names) > 0 {
+ // named parameter
+ for _, name := range field.Names {
+ obj := name.Obj
+ obj.Type = typ
+ params = append(params, obj)
+ }
+ } else {
+ // anonymous parameter
+ obj := ast.NewObj(ast.Var, "")
+ obj.Type = typ
+ params = append(params, obj)
+ }
+ }
+ return
+}
+
+func (check *checker) collectMethods(list *ast.FieldList) (methods ObjList) {
+ if list == nil {
+ return
+ }
+ for _, f := range list.List {
+ typ := check.typ(f.Type, len(f.Names) > 0) // cycles are not ok for embedded interfaces
+ // the parser ensures that f.Tag is nil and we don't
+ // care if a constructed AST contains a non-nil tag
+ if len(f.Names) > 0 {
+ // methods (the parser ensures that there's only one
+ // and we don't care if a constructed AST has more)
+ if _, ok := typ.(*Signature); !ok {
+ check.invalidAST(f.Type.Pos(), "%s is not a method signature", typ)
+ continue
+ }
+ for _, name := range f.Names {
+ obj := name.Obj
+ obj.Type = typ
+ methods = append(methods, obj)
+ }
+ } else {
+ // embedded interface
+ utyp := underlying(typ)
+ if ityp, ok := utyp.(*Interface); ok {
+ methods = append(methods, ityp.Methods...)
+ } else if utyp != Typ[Invalid] {
+ // if utyp is invalid, don't complain (the root cause was reported before)
+ check.errorf(f.Type.Pos(), "%s is not an interface type", typ)
+ }
+ }
+ }
+ // check for double declarations
+ methods.Sort()
+ prev := ""
+ for _, obj := range methods {
+ if obj.Name == prev {
+ check.errorf(list.Pos(), "multiple methods named %s", prev)
+ return // keep multiple entries, lookup will only return the first entry
+ }
+ }
+ return
+}
+
+func (check *checker) tag(t *ast.BasicLit) string {
+ if t != nil {
+ if t.Kind == token.STRING {
+ if val, err := strconv.Unquote(t.Value); err == nil {
+ return val
+ }
}
check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value)
}
return ""
}
-// collectFields collects interface methods (tok = token.INTERFACE), and function arguments/results (tok = token.FUNC).
-func (check *checker) collectFields(tok token.Token, list *ast.FieldList, cycleOk bool) (fields ObjList, tags []string, isVariadic bool) {
- if list != nil {
- for _, field := range list.List {
- ftype := field.Type
- if t, ok := ftype.(*ast.Ellipsis); ok {
- ftype = t.Elt
- isVariadic = true
- }
- typ := check.typ(ftype, cycleOk)
- tag := check.tag(field)
- if len(field.Names) > 0 {
- // named fields
- for _, name := range field.Names {
- obj := name.Obj
- obj.Type = typ
- fields = append(fields, obj)
- if tok == token.STRUCT {
- tags = append(tags, tag)
- }
- }
- } else {
- // anonymous field
- switch tok {
- case token.FUNC:
- obj := ast.NewObj(ast.Var, "")
- obj.Type = typ
- fields = append(fields, obj)
- case token.INTERFACE:
- utyp := underlying(typ)
- if typ, ok := utyp.(*Interface); ok {
- // TODO(gri) This is not good enough. Check for double declarations!
- fields = append(fields, typ.Methods...)
- } else if utyp != Typ[Invalid] {
- // if utyp is invalid, don't complain (the root cause was reported before)
- check.errorf(ftype.Pos(), "interface contains embedded non-interface type")
- }
- default:
- panic("unreachable")
- }
- }
- }
- }
- return
-}
-
-func (check *checker) collectStructFields(list *ast.FieldList, cycleOk bool) (fields []*StructField) {
+func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*StructField) {
if list == nil {
return
}
for _, f := range list.List {
typ := check.typ(f.Type, cycleOk)
- tag := check.tag(f)
+ tag := check.tag(f.Tag)
if len(f.Names) > 0 {
// named fields
for _, name := range f.Names {
@@ -115,9 +143,6 @@
func (check *checker) op(m opPredicates, x *operand, op token.Token) bool {
if pred := m[op]; pred != nil {
if !pred(x.typ) {
- // TODO(gri) better error message for <-x where x is a send-only channel
- // (<- is defined but not permitted). Special-case here or
- // handle higher up.
check.invalidOp(x.pos(), "operator %s not defined for %s", op, x)
return false
}
@@ -537,27 +562,155 @@
}
case *ast.FuncLit:
- x.mode = value
- x.typ = check.typ(e.Type, false)
- // TODO(gri) handle errors (e.g. x.typ is not a *Signature)
- check.function(x.typ.(*Signature), e.Body)
+ if typ, ok := check.typ(e.Type, false).(*Signature); ok {
+ x.mode = value
+ x.typ = typ
+ check.function(typ, e.Body)
+ } else {
+ check.invalidAST(e.Pos(), "invalid function literal %s", e)
+ goto Error
+ }
case *ast.CompositeLit:
- // TODO(gri)
- // - determine element type if nil
- // - deal with map elements
- var typ Type
+ typ := hint
if e.Type != nil {
- // TODO(gri) Fix this - just to get going for now
typ = check.typ(e.Type, false)
}
- for _, e := range e.Elts {
- var x operand
- check.expr(&x, e, hint, iota)
- // TODO(gri) check assignment compatibility to element type
+ if typ == nil {
+ check.errorf(e.Pos(), "missing type in composite literal")
+ goto Error
}
- // TODO(gri) this is not correct - leave for now to get going
- x.mode = variable
+
+ // TODO(gri) try to factor code below better
+
+ switch utyp := underlying(deref(typ)).(type) {
+ case *Struct:
+ if len(e.Elts) == 0 {
+ break
+ }
+ fields := utyp.Fields
+ if _, ok := e.Elts[0].(*ast.KeyValueExpr); ok {
+ // all elements must have keys
+ visited := make([]bool, len(fields))
+ for _, e := range e.Elts {
+ kv, _ := e.(*ast.KeyValueExpr)
+ if kv == nil {
+ check.errorf(e.Pos(), "mixture of field:value and value elements in struct literal")
+ continue
+ }
+ key, _ := kv.Key.(*ast.Ident)
+ if key == nil {
+ check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key)
+ continue
+ }
+ i := utyp.fieldIndex(key.Name)
+ if i < 0 {
+ check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name)
+ continue
+ }
+ // 0 <= i < len(fields)
+ if visited[i] {
+ check.errorf(kv.Pos(), "duplicate field name %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) {
+ check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp)
+ continue
+ }
+ }
+ } else {
+ // no element must have a key
+ for i, e := range e.Elts {
+ if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
+ check.errorf(kv.Pos(), "mixture of field:value and value elements in struct literal")
+ continue
+ }
+ check.expr(x, e, nil, iota)
+ if i >= len(fields) {
+ check.errorf(x.pos(), "too many values in struct literal")
+ goto Error
+ }
+ etyp := fields[i].Type
+ if !x.isAssignable(etyp) {
+ check.errorf(x.pos(), "cannot use %s as an element of type %s in struct literal", x, etyp)
+ continue
+ }
+ }
+ if len(e.Elts) < len(fields) {
+ check.errorf(e.Rbrace, "too few values in struct literal")
+ goto Error
+ }
+ }
+
+ case *Array:
+ var index int64
+ for _, e := range e.Elts {
+ eval := e
+ if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
+ check.index(kv.Key, -1, iota)
+ eval = kv.Value
+ }
+ // TODO(gri) missing index range & duplicate check
+ check.expr(x, eval, utyp.Elt, iota)
+ if !x.isAssignable(utyp.Elt) {
+ check.errorf(x.pos(), "cannot use %s as %s value in array literal", x, utyp.Elt)
+ }
+ index++
+ }
+
+ case *Slice:
+ var index int64
+ for _, e := range e.Elts {
+ eval := e
+ if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
+ // TODO(gri) check key
+ check.index(kv.Key, -1, iota)
+ eval = kv.Value
+ }
+ // TODO(gri) missing index range & duplicate check
+ check.expr(x, eval, utyp.Elt, iota)
+ if !x.isAssignable(utyp.Elt) {
+ check.errorf(x.pos(), "cannot use %s as %s value in slice literal", x, utyp.Elt)
+ }
+ index++
+ }
+
+ case *Map:
+ visited := make(map[interface{}]bool, len(e.Elts))
+ for _, e := range e.Elts {
+ kv, _ := e.(*ast.KeyValueExpr)
+ if kv == nil {
+ check.errorf(e.Pos(), "missing key in map literal")
+ continue
+ }
+ check.expr(x, kv.Key, nil, iota)
+ if !x.isAssignable(utyp.Key) {
+ check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.Key)
+ continue
+ }
+ if x.mode == constant {
+ if visited[x.val] {
+ check.errorf(x.pos(), "duplicate key %s in map literal", x.val)
+ continue
+ }
+ visited[x.val] = true
+ }
+ check.expr(x, kv.Value, utyp.Elt, iota)
+ if !x.isAssignable(utyp.Elt) {
+ check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.Elt)
+ continue
+ }
+ }
+
+ default:
+ check.errorf(e.Pos(), "%s is not a valid composite literal type", typ)
+ goto Error
+ }
+
+ x.mode = variable // TODO(gri) mode is really a value - keep for now to get going
x.typ = typ
case *ast.ParenExpr:
@@ -604,7 +757,7 @@
}
mode, typ := lookupField(x.typ, sel)
if mode == invalid {
- check.invalidOp(e.Pos(), "%s has no field or method %s", x, sel)
+ check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel)
goto Error
}
if x.mode == typexpr {
@@ -617,7 +770,7 @@
// the receiver type becomes the type of the first function
// argument of the method expression's function type
// TODO(gri) at the moment, method sets don't correctly track
- // pointer vs non-pointer receivers -> typechecker is too lenient
+ // pointer vs non-pointer receivers => typechecker is too lenient
arg := ast.NewObj(ast.Var, "")
arg.Type = x.typ
x.mode = value
@@ -665,7 +818,12 @@
x.typ = typ.Elt
case *Map:
- // TODO(gri) check index type
+ var key operand
+ check.expr(&key, e.Index, nil, iota)
+ if key.mode == invalid || !key.isAssignable(typ.Key) {
+ check.invalidOp(x.pos(), "cannot use %s as map index of type %s", &key, typ.Key)
+ goto Error
+ }
x.mode = valueok
x.typ = typ.Elt
return
@@ -827,7 +985,9 @@
check.binary(x, &y, e.Op, hint)
case *ast.KeyValueExpr:
- unimplemented()
+ // key:value expressions are handled in composite literals
+ check.invalidAST(e.Pos(), "no key:value expected")
+ goto Error
case *ast.ArrayType:
if e.Len != nil {
@@ -862,19 +1022,17 @@
case *ast.StructType:
x.mode = typexpr
- x.typ = &Struct{Fields: check.collectStructFields(e.Fields, cycleOk)}
+ x.typ = &Struct{Fields: check.collectFields(e.Fields, cycleOk)}
case *ast.FuncType:
- params, _, isVariadic := check.collectFields(token.FUNC, e.Params, true)
- results, _, _ := check.collectFields(token.FUNC, e.Results, true)
+ params, isVariadic := check.collectParams(e.Params)
+ results, _ := check.collectParams(e.Results)
x.mode = typexpr
x.typ = &Signature{Recv: nil, Params: params, Results: results, IsVariadic: isVariadic}
case *ast.InterfaceType:
- methods, _, _ := check.collectFields(token.INTERFACE, e.Methods, cycleOk)
- methods.Sort()
x.mode = typexpr
- x.typ = &Interface{Methods: methods}
+ x.typ = &Interface{Methods: check.collectMethods(e.Methods)}
case *ast.MapType:
x.mode = typexpr
diff --git a/src/pkg/exp/types/operand.go b/src/pkg/exp/types/operand.go
index 49ba899..35c5fe3 100644
--- a/src/pkg/exp/types/operand.go
+++ b/src/pkg/exp/types/operand.go
@@ -125,7 +125,16 @@
return true // avoid spurious errors
}
- unimplemented()
+ // x implements T if it implements all methods of T.
+ // TODO(gri): distinguish pointer and non-pointer receivers
+ for _, m := range T.Methods {
+ mode, typ := lookupField(x.typ, m.Name)
+ if mode == invalid || !isIdentical(typ, m.Type.(Type)) {
+ // TODO(gri) should report which method is missing
+ return false
+ }
+ }
+
return true
}
@@ -134,6 +143,10 @@
return x.mode == constant && x.val == nilConst
}
+// TODO(gri) The functions operand.isAssignable, checker.convertUntyped,
+// checker.isRepresentable, and checker.assignOperand are
+// 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 {
if x.mode == invalid || T == Typ[Invalid] {
@@ -181,8 +194,18 @@
}
// x is an untyped constant representable by a value of type T
- // - this is taken care of in the assignment check
- // TODO(gri) double-check - isAssignable is used elsewhere
+ // TODO(gri) This is borrowing from checker.convertUntyped and
+ // checker.isRepresentable. Need to clean up.
+ if isUntyped(Vu) {
+ switch t := Tu.(type) {
+ case *Basic:
+ return x.mode == constant && isRepresentableConst(x.val, t.Kind)
+ case *Interface:
+ return x.isNil() || len(t.Methods) == 0
+ case *Pointer, *Signature, *Slice, *Map, *Chan:
+ return x.isNil()
+ }
+ }
return false
}
@@ -199,35 +222,50 @@
typ Type
}
-// lookupFieldRecursive is similar to FieldByNameFunc in reflect/type.go
-// TODO(gri): FieldByNameFunc seems more complex - what are we missing?
-func lookupFieldRecursive(list []*NamedType, name string) (res lookupResult) {
- // visited records the types that have been searched already
- visited := make(map[Type]bool)
+type embeddedType struct {
+ typ *NamedType
+ multiples bool // if set, typ is embedded multiple times at the same level
+}
+
+// lookupFieldBreadthFirst searches all types in list for a single entry (field
+// or method) of the given name. If such a field is found, the result describes
+// the field mode and type; otherwise the result mode is invalid.
+// (This function is similar in structure to FieldByNameFunc in reflect/type.go)
+//
+func lookupFieldBreadthFirst(list []embeddedType, name string) (res lookupResult) {
+ // visited records the types that have been searched already.
+ visited := make(map[*NamedType]bool)
// embedded types of the next lower level
- var next []*NamedType
+ var next []embeddedType
- potentialMatch := func(mode operandMode, typ Type) bool {
- if res.mode != invalid {
- // name appeared multiple times at this level - annihilate
+ // potentialMatch is invoked every time a match is found.
+ potentialMatch := func(multiples bool, mode operandMode, typ Type) bool {
+ if multiples || res.mode != invalid {
+ // name appeared already at this level - annihilate
res.mode = invalid
return false
}
+ // first appearance of name
res.mode = mode
res.typ = typ
return true
}
- // look for name in all types of this level
+ // Search the current level if there is any work to do and collect
+ // embedded types of the next lower level in the next list.
for len(list) > 0 {
+ // The res.mode indicates whether we have found a match already
+ // on this level (mode != invalid), or not (mode == invalid).
assert(res.mode == invalid)
- for _, typ := range list {
+
+ // start with empty next list (don't waste underlying array)
+ next = next[:0]
+
+ // look for name in all types at this level
+ for _, e := range list {
+ typ := e.typ
if visited[typ] {
- // We have seen this type before, at a higher level.
- // That higher level shadows the lower level we are
- // at now, and either we would have found or not
- // found the field before. Ignore this type now.
continue
}
visited[typ] = true
@@ -236,7 +274,7 @@
if data := typ.Obj.Data; data != nil {
if obj := data.(*ast.Scope).Lookup(name); obj != nil {
assert(obj.Type != nil)
- if !potentialMatch(value, obj.Type.(Type)) {
+ if !potentialMatch(e.multiples, value, obj.Type.(Type)) {
return // name collision
}
}
@@ -244,21 +282,26 @@
switch typ := underlying(typ).(type) {
case *Struct:
- // look for a matching fieldm and collect embedded types
+ // look for a matching field and collect embedded types
for _, f := range typ.Fields {
if f.Name == name {
assert(f.Type != nil)
- if !potentialMatch(variable, f.Type) {
+ if !potentialMatch(e.multiples, variable, f.Type) {
return // name collision
}
continue
}
// Collect embedded struct fields for searching the next
- // lower level, but only if we have not seen a match yet.
+ // lower level, but only if we have not seen a match yet
+ // (if we have a match it is either the desired field or
+ // we have a name collision on the same level; in either
+ // case we don't need to look further).
// Embedded fields are always of the form T or *T where
- // T is a named type.
+ // T is a named type. If typ appeared multiple times at
+ // this level, f.Type appears multiple times at the next
+ // level.
if f.IsAnonymous && res.mode == invalid {
- next = append(next, deref(f.Type).(*NamedType))
+ next = append(next, embeddedType{deref(f.Type).(*NamedType), e.multiples})
}
}
@@ -267,7 +310,7 @@
for _, obj := range typ.Methods {
if obj.Name == name {
assert(obj.Type != nil)
- if !potentialMatch(value, obj.Type.(Type)) {
+ if !potentialMatch(e.multiples, value, obj.Type.(Type)) {
return // name collision
}
}
@@ -276,17 +319,41 @@
}
if res.mode != invalid {
- // we found a match on this level
+ // we found a single match on this level
return
}
- // search the next level
- list = append(list[:0], next...) // don't waste underlying arrays
- next = next[:0]
+ // No match and no collision so far.
+ // Compute the list to search for the next level.
+ list = list[:0] // don't waste underlying array
+ for _, e := range next {
+ // Instead of adding the same type multiple times, look for
+ // it in the list and mark it as multiple if it was added
+ // before.
+ // We use a sequential search (instead of a map for next)
+ // because the lists tend to be small, can easily be reused,
+ // and explicit search appears to be faster in this case.
+ if alt := findType(list, e.typ); alt != nil {
+ alt.multiples = true
+ } else {
+ list = append(list, e)
+ }
+ }
+
}
+
return
}
+func findType(list []embeddedType, typ *NamedType) *embeddedType {
+ for i := range list {
+ if p := &list[i]; p.typ == typ {
+ return p
+ }
+ }
+ return nil
+}
+
func lookupField(typ Type, name string) (operandMode, Type) {
typ = deref(typ)
@@ -301,17 +368,20 @@
switch typ := underlying(typ).(type) {
case *Struct:
- var list []*NamedType
+ var next []embeddedType
for _, f := range typ.Fields {
if f.Name == name {
return variable, f.Type
}
if f.IsAnonymous {
- list = append(list, deref(f.Type).(*NamedType))
+ // Possible optimization: If the embedded type
+ // is a pointer to the current type we could
+ // ignore it.
+ next = append(next, embeddedType{typ: deref(f.Type).(*NamedType)})
}
}
- if len(list) > 0 {
- res := lookupFieldRecursive(list, name)
+ if len(next) > 0 {
+ res := lookupFieldBreadthFirst(next, name)
return res.mode, res.typ
}
diff --git a/src/pkg/exp/types/testdata/decls0.src b/src/pkg/exp/types/testdata/decls0.src
index 3537a9e..f5fd3d8 100644
--- a/src/pkg/exp/types/testdata/decls0.src
+++ b/src/pkg/exp/types/testdata/decls0.src
@@ -41,7 +41,7 @@
type (
- p1 pi /* ERROR "no field or method foo" */ .foo
+ p1 pi /* ERROR "no single field or method foo" */ .foo
p2 unsafe.Pointer
)
@@ -131,7 +131,7 @@
m1(I5)
}
I6 interface {
- S0 /* ERROR "non-interface" */
+ S0 /* ERROR "not an interface" */
}
I7 interface {
I1
diff --git a/src/pkg/exp/types/testdata/decls3.src b/src/pkg/exp/types/testdata/decls3.src
new file mode 100644
index 0000000..4bc7d41
--- /dev/null
+++ b/src/pkg/exp/types/testdata/decls3.src
@@ -0,0 +1,231 @@
+// Copyright 2012 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.
+
+// embedded types
+
+package decls3
+
+// fields with the same name at the same level cancel each other out
+
+func _() {
+ type (
+ T1 struct { X int }
+ T2 struct { X int }
+ T3 struct { T1; T2 } // X is embedded twice at the same level via T1->X, T2->X
+ )
+
+ var t T3
+ _ = t /* ERROR "no single field or method" */ .X
+}
+
+func _() {
+ type (
+ T1 struct { X int }
+ T2 struct { T1 }
+ T3 struct { T1 }
+ T4 struct { T2; T3 } // X is embedded twice at the same level via T2->T1->X, T3->T1->X
+ )
+
+ var t T4
+ _ = t /* ERROR "no single field or method" */ .X
+}
+
+func issue4355() {
+ type (
+ T1 struct {X int}
+ T2 struct {T1}
+ T3 struct {T2}
+ T4 struct {T2}
+ T5 struct {T3; T4} // X is embedded twice at the same level via T3->T2->T1->X, T4->T2->T1->X
+ )
+
+ var t T5
+ _ = t /* ERROR "no single field or method" */ .X
+}
+
+// Borrowed from the FieldByName test cases in reflect/all_test.go.
+
+type D1 struct {
+ d int
+}
+type D2 struct {
+ d int
+}
+
+type S0 struct {
+ A, B, C int
+ D1
+ D2
+}
+
+type S1 struct {
+ B int
+ S0
+}
+
+type S2 struct {
+ A int
+ *S1
+}
+
+type S1x struct {
+ S1
+}
+
+type S1y struct {
+ S1
+}
+
+type S3 struct {
+ S1x
+ S2
+ D, E int
+ *S1y
+}
+
+type S4 struct {
+ *S4
+ A int
+}
+
+// The X in S6 and S7 annihilate, but they also block the X in S8.S9.
+type S5 struct {
+ S6
+ S7
+ S8
+}
+
+type S6 struct {
+ X int
+}
+
+type S7 S6
+
+type S8 struct {
+ S9
+}
+
+type S9 struct {
+ X int
+ Y int
+}
+
+// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9.
+type S10 struct {
+ S11
+ S12
+ S13
+}
+
+type S11 struct {
+ S6
+}
+
+type S12 struct {
+ S6
+}
+
+type S13 struct {
+ S8
+}
+
+func _() {
+ _ = struct /* ERROR "no single field or method" */ {}{}.Foo
+ _ = S0{}.A
+ _ = S0 /* ERROR "no single field or method" */ {}.D
+ _ = S1{}.A
+ _ = S1{}.B
+ _ = S1{}.S0
+ _ = S1{}.C
+ _ = S2{}.A
+ _ = S2{}.S1
+ _ = S2{}.B
+ _ = S2{}.C
+ _ = S2 /* ERROR "no single field or method" */ {}.D
+ _ = S3 /* ERROR "no single field or method" */ {}.S1
+ _ = S3{}.A
+ _ = S3 /* ERROR "no single field or method" */ {}.B
+ _ = S3{}.D
+ _ = S3{}.E
+ _ = S4{}.A
+ _ = S4 /* ERROR "no single field or method" */ {}.B
+ _ = S5 /* ERROR "no single field or method" */ {}.X
+ _ = S5{}.Y
+ _ = S10 /* ERROR "no single field or method" */ {}.X
+ _ = S10{}.Y
+}
+
+// Borrowed from the FieldByName benchmark in reflect/all_test.go.
+
+type R0 struct {
+ *R1
+ *R2
+ *R3
+ *R4
+}
+
+type R1 struct {
+ *R5
+ *R6
+ *R7
+ *R8
+}
+
+type R2 R1
+type R3 R1
+type R4 R1
+
+type R5 struct {
+ *R9
+ *R10
+ *R11
+ *R12
+}
+
+type R6 R5
+type R7 R5
+type R8 R5
+
+type R9 struct {
+ *R13
+ *R14
+ *R15
+ *R16
+}
+
+type R10 R9
+type R11 R9
+type R12 R9
+
+type R13 struct {
+ *R17
+ *R18
+ *R19
+ *R20
+}
+
+type R14 R13
+type R15 R13
+type R16 R13
+
+type R17 struct {
+ *R21
+ *R22
+ *R23
+ *R24
+}
+
+type R18 R17
+type R19 R17
+type R20 R17
+
+type R21 struct {
+ X int
+}
+
+type R22 R21
+type R23 R21
+type R24 R21
+
+var _ = R0 /* ERROR "no single field or method" */ {}.X
\ No newline at end of file
diff --git a/src/pkg/exp/types/testdata/expr3.src b/src/pkg/exp/types/testdata/expr3.src
index 890f5e9..1a7c5df 100644
--- a/src/pkg/exp/types/testdata/expr3.src
+++ b/src/pkg/exp/types/testdata/expr3.src
@@ -126,9 +126,59 @@
func (*T) m() {}
func method_expressions() {
- _ = T /* ERROR "no field or method" */ .a
+ _ = T /* ERROR "no single field or method" */ .a
_ = T /* ERROR "has no method" */ .x
_ = T.m
var f func(*T) = (*T).m
var g func(*T) = ( /* ERROR "cannot assign" */ T).m
+}
+
+func struct_literals() {
+ type T0 struct {
+ a, b, c int
+ }
+
+ type T1 struct {
+ T0
+ a, b int
+ u float64
+ s string
+ }
+
+ // keyed elements
+ _ = T1{}
+ _ = T1{a: 0, 1 /* ERROR "mixture of .* elements" */ }
+ _ = T1{aa /* ERROR "unknown field" */ : 0}
+ _ = T1{1 /* ERROR "invalid field name" */ : 0}
+ _ = T1{a: 0, s: "foo", u: 0, a /* ERROR "duplicate field" */: 10}
+ _ = T1{a: "foo" /* ERROR "cannot use" */ }
+ _ = T1{c /* ERROR "unknown field" */ : 0}
+ _ = T1{T0: { /* ERROR "missing type" */ }}
+ _ = T1{T0: T0{}}
+ _ = T1{T0 /* ERROR "invalid field name" */ .a: 0}
+
+ // unkeyed elements
+ _ = T0{1, 2, 3}
+ _ = T0{1, b /* ERROR "mixture" */ : 2, 3}
+ _ = T0{1, 2} /* ERROR "too few values" */
+ _ = T0{1, 2, 3, 4 /* ERROR "too many values" */ }
+ _ = T0{1, "foo" /* ERROR "cannot use" */, 3.4 /* ERROR "cannot use" */}
+}
+
+func array_literals() {
+ // TODO(gri)
+}
+
+func slice_literals() {
+ // TODO(gri)
+}
+
+func map_literals() {
+ type M0 map[string]int
+
+ _ = M0{}
+ _ = M0{1 /* ERROR "missing key" */ }
+ _ = M0{1 /* ERROR "cannot use .* as string key" */ : 2}
+ _ = M0{"foo": "bar" /* ERROR "cannot use .* as int value" */ }
+ _ = M0{"foo": 1, "bar": 2, "foo" /* ERROR "duplicate key" */ : 3 }
}
\ No newline at end of file
diff --git a/src/pkg/exp/types/types.go b/src/pkg/exp/types/types.go
index eed0c8a..83a0826 100644
--- a/src/pkg/exp/types/types.go
+++ b/src/pkg/exp/types/types.go
@@ -126,6 +126,15 @@
Fields []*StructField
}
+func (typ *Struct) fieldIndex(name string) int {
+ for i, f := range typ.Fields {
+ if f.Name == name {
+ return i
+ }
+ }
+ return -1
+}
+
// A Pointer represents a pointer type *Base.
type Pointer struct {
implementsType