diff --git a/go/types/api.go b/go/types/api.go index eb90ad9c..eced819f 100644 --- a/go/types/api.go +++ b/go/types/api.go @@ -33,7 +33,6 @@ package types // pass without errors. Please do not file issues against these for now // since they are known already: // -// BUG(gri): Method sets are computed loosely and don't distinguish between ptr vs non-pointer receivers. // BUG(gri): Method expressions and method values work accidentally and may not be fully checked. // BUG(gri): Conversions of constants only change the type, not the value (e.g., int(1.1) is wrong). // BUG(gri): Some built-ins don't check parameters fully, yet (e.g. append). diff --git a/go/types/builtins.go b/go/types/builtins.go index f7ccdd7d..d38de4d2 100644 --- a/go/types/builtins.go +++ b/go/types/builtins.go @@ -341,17 +341,22 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota if x.mode == invalid { goto Error } + base := x.typ.Deref() sel := arg.Sel.Name - obj, index := LookupFieldOrMethod(x.typ, check.pkg, arg.Sel.Name) - if obj == nil { - check.invalidArg(x.pos(), "%s has no single field %s", x, sel) + obj, index, indirect := LookupFieldOrMethod(base, check.pkg, arg.Sel.Name) + switch obj.(type) { + case nil: + check.invalidArg(x.pos(), "%s has no single field %s", base, sel) + goto Error + case *Func: + check.invalidArg(arg0.Pos(), "%s is a method value", arg0) goto Error } - offs := check.ctxt.offsetof(x.typ.Deref(), index) - if offs < 0 { - check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, x) + if indirect { + check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base) goto Error } + offs := check.ctxt.offsetof(base, index) x.mode = constant x.val = exact.MakeInt64(offs) x.typ = Typ[Uintptr] diff --git a/go/types/check.go b/go/types/check.go index f4c80a3a..474b4d81 100644 --- a/go/types/check.go +++ b/go/types/check.go @@ -21,6 +21,12 @@ const ( trace = false // turn on for detailed type resolution traces ) +// If retainASTLinks is set, scopes maintain a link to the node +// responsible for it. +// TODO(gri) Decide if this should be a permanent (always present) +// or optional feature (enabled with a mode flag). +const retainASTLinks = true + // exprInfo stores type and constant value for an untyped expression. type exprInfo struct { isLhs bool // expression is lhs operand of a shift with delayed type check diff --git a/go/types/check_test.go b/go/types/check_test.go index b2afe387..25691775 100644 --- a/go/types/check_test.go +++ b/go/types/check_test.go @@ -55,6 +55,7 @@ var tests = []struct { {"expr1", []string{"testdata/expr1.src"}}, {"expr2", []string{"testdata/expr2.src"}}, {"expr3", []string{"testdata/expr3.src"}}, + {"methodsets", []string{"testdata/methodsets.src"}}, {"shifts", []string{"testdata/shifts.src"}}, {"builtins", []string{"testdata/builtins.src"}}, {"conversions", []string{"testdata/conversions.src"}}, diff --git a/go/types/expr.go b/go/types/expr.go index bc02e3e7..480b40d0 100644 --- a/go/types/expr.go +++ b/go/types/expr.go @@ -200,10 +200,24 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [ } else { // anonymous field pos := f.Type.Pos() - switch t := typ.Deref().(type) { + t, isPtr := deref(typ) + switch t := t.(type) { case *Basic: add(f, nil, t.name, true, pos) case *Named: + // spec: "An embedded type must be specified as a type name + // T or as a pointer to a non-interface type name *T, and T + // itself may not be a pointer type." + switch t.underlying.(type) { + case *Pointer: + check.errorf(pos, "anonymous field type cannot be a pointer") + continue // ignore this field + case *Interface: + if isPtr { + check.errorf(pos, "anonymous field type cannot be a pointer to an interface") + continue // ignore this field + } + } add(f, nil, t.obj.name, true, pos) default: if typ != Typ[Invalid] { @@ -1310,25 +1324,39 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle if x.mode == invalid { goto Error } - // TODO(gri) use collision information for better error message - obj, _ := LookupFieldOrMethod(x.typ, check.pkg, sel) + + obj, index, indirect := LookupFieldOrMethod(x.typ, check.pkg, sel) if obj == nil { - check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel) + if index != nil { + // TODO(gri) should provide actual type where the conflict happens + check.invalidOp(e.Pos(), "ambiguous selector %s", sel) + } else { + check.invalidOp(e.Pos(), "%s has no field or method %s", x, sel) + } goto Error } + check.callIdent(e.Sel, obj) + if x.mode == typexpr { // method expression - m, ok := obj.(*Func) - if !ok { + m, _ := obj.(*Func) + if m == nil { check.invalidOp(e.Pos(), "%s has no method %s", x, sel) goto Error } + // verify that m is in the method set of x.typ + // (the receiver is nil if f is an interface method) + if recv := m.typ.(*Signature).recv; recv != nil { + if _, isPtr := deref(recv.typ); isPtr && !indirect { + check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ) + goto Error + } + } + // 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 var params []*Var sig := m.typ.(*Signature) if sig.params != nil { @@ -1340,6 +1368,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle results: sig.results, isVariadic: sig.isVariadic, } + } else { // regular selector switch obj := obj.(type) { @@ -1350,6 +1379,22 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle // TODO(gri) Temporary assert to verify corresponding lookup via method sets. // Remove eventually. assert(NewMethodSet(x.typ).Lookup(check.pkg, sel) == obj) + + // TODO(gri) This code appears elsewhere, too. Factor! + // verify that obj is in the method set of x.typ (or &(x.typ) if x is addressable) + // (the receiver is nil if obj is an interface method) + // + // spec: "A method call x.m() is valid if the method set of (the type of) x + // contains m and the argument list can be assigned to the parameter + // list of m. If x is addressable and &x's method set contains m, x.m() + // is shorthand for (&x).m()". + if recv := obj.typ.(*Signature).recv; recv != nil { + if _, isPtr := deref(recv.typ); isPtr && !indirect && x.mode != variable { + check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x) + goto Error + } + } + x.mode = value x.typ = obj.typ default: @@ -1708,6 +1753,9 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle case *ast.FuncType: scope := NewScope(check.topScope) + if retainASTLinks { + scope.node = e + } params, isVariadic := check.collectParams(scope, e.Params, true) results, _ := check.collectParams(scope, e.Results, false) x.mode = typexpr diff --git a/go/types/lookup.go b/go/types/lookup.go index bd8681ac..e03e3ae0 100644 --- a/go/types/lookup.go +++ b/go/types/lookup.go @@ -8,8 +8,15 @@ package types import "go/ast" +// TODO(gri) The named type consolidation and seen maps below must be +// indexed by unique keys for a given type. Verify that named +// types always have only one representation (even when imported +// indirectly via different packages.) + // LookupFieldOrMethod looks up a field or method with given package and name -// in typ and returns the corresponding *Field or *Func, and an index sequence. +// in typ and returns the corresponding *Field or *Func, an index sequence, +// and a bool indicating if there were any pointer indirections on the path +// to the field or method. // // The last index entry is the field or method index in the (possibly embedded) // type where the entry was found, either: @@ -24,34 +31,27 @@ import "go/ast" // If no entry is found, a nil object is returned. In this case, the returned // index sequence points to an ambiguous entry if it exists, or it is nil. // -func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index []int) { +func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index []int, indirect bool) { if name == "_" { return // empty fields/methods are never found } - isPtr := false - if p, ok := typ.Underlying().(*Pointer); ok { - typ = p.base - isPtr = true - } - - // TODO(gri) consult isPtr for precise method set computation - _ = isPtr - - // named types that we have seen already - seen := make(map[*Named]bool) - // embedded represents an embedded named type type embedded struct { typ *Named // nil means use the outer typ variable instead - index []int // field/method indices, starting with index at depth 0 - multiples bool // if set, type appears multiple times at its depth + index []int // embedded field indices, starting with index at depth 0 + indirect bool // if set, there was a pointer indirection on the path to this field + multiples bool // if set, typ appears multiple times at this depth } // Start with typ as single entry at lowest depth. // If typ is not a named type, insert a nil type instead. + typ, isPtr := deref(typ) t, _ := typ.(*Named) - current := []embedded{{t, nil, false}} + current := []embedded{{t, nil, isPtr, false}} + + // named types that we have seen already + seen := make(map[*Named]bool) // search current depth if there's work to do for len(current) > 0 { @@ -64,6 +64,11 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index // we simply continue with the underlying type. if e.typ != nil { if seen[e.typ] { + // We have seen this type before, at a more shallow depth + // (note that multiples of this type at the current depth + // were eliminated before). The type at that depth shadows + // this same type at the current depth, so we can ignore + // this one. continue } seen[e.typ] = true @@ -78,6 +83,8 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index return } obj = m + indirect = e.indirect + continue // we can't have a matching field or interface method } // continue with underlying type @@ -96,7 +103,8 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index return } obj = f - continue + indirect = e.indirect + continue // we can't have a matching interface method } // Collect embedded struct fields for searching the next // lower depth, but only if we have not seen a match yet @@ -109,9 +117,10 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index // depth. if obj == nil && f.anonymous { // Ignore embedded basic types - only user-defined - // named types can have methods or have struct fields. - if t, _ := f.typ.Deref().(*Named); t != nil { - next = append(next, embedded{t, concat(e.index, i), e.multiples}) + // named types can have methods or struct fields. + typ, isPtr := deref(f.typ) + if t, _ := typ.Deref().(*Named); t != nil { + next = append(next, embedded{t, concat(e.index, i), e.indirect || isPtr, e.multiples}) } } } @@ -126,6 +135,7 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index return } obj = m + indirect = e.indirect } } } @@ -155,23 +165,15 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index } index = nil + indirect = false return // not found } -// concat returns the result of concatenating list and i. -// The result does not share its underlying array with list. -func concat(list []int, i int) []int { - var t []int - t = append(t, list...) - return append(t, i) -} - // MissingMethod returns (nil, false) if typ implements T, otherwise // it returns the first missing method required by T and whether it // is missing or simply has the wrong type. // func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) { - // TODO(gri): distinguish pointer and non-pointer receivers // an interface type implements T if it has no methods with conflicting signatures // Note: This is stronger than the current spec. Should the spec require this? @@ -192,10 +194,24 @@ func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) { // a concrete type implements T if it implements all methods of T. for _, m := range T.methods { - obj, _ := LookupFieldOrMethod(typ, m.pkg, m.name) + obj, _, indirect := LookupFieldOrMethod(typ, m.pkg, m.name) if obj == nil { return m, false } + + f, _ := obj.(*Func) + if f == nil { + return m, false + } + + // verify that f is in the method set of typ + // (the receiver is nil if f is an interface method) + if recv := f.typ.(*Signature).recv; recv != nil { + if _, isPtr := deref(recv.typ); isPtr && !indirect { + return m, false + } + } + if !IsIdentical(obj.Type(), m.typ) { return m, true } @@ -203,6 +219,25 @@ func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) { return } +// Deref dereferences typ if it is a pointer and returns its base and true. +// Otherwise it returns (typ, false). +// In contrast, Type.Deref also dereferenciates if type is a named type +// that is a pointer. +func deref(typ Type) (Type, bool) { + if p, _ := typ.(*Pointer); p != nil { + return p.base, true + } + return typ, false +} + +// concat returns the result of concatenating list and i. +// The result does not share its underlying array with list. +func concat(list []int, i int) []int { + var t []int + t = append(t, list...) + return append(t, i) +} + // fieldIndex returns the index for the field with matching package and name, or a value < 0. func fieldIndex(fields []*Field, pkg *Package, name string) int { if name == "_" { diff --git a/go/types/resolver.go b/go/types/resolver.go index ac47ddde..fbe354fc 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -88,6 +88,9 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) { check.callIdent(file.Name, pkg) fileScope = NewScope(pkg.scope) + if retainASTLinks { + fileScope.node = file + } scopes = append(scopes, fileScope) for _, decl := range file.Decls { diff --git a/go/types/scope.go b/go/types/scope.go index 5271b84a..caf93d01 100644 --- a/go/types/scope.go +++ b/go/types/scope.go @@ -20,11 +20,12 @@ import ( type Scope struct { parent *Scope entries []Object + node ast.Node } // NewScope returns a new, empty scope. func NewScope(parent *Scope) *Scope { - return &Scope{parent, nil} + return &Scope{parent: parent} } // Parent returns the scope's containing (parent) scope. @@ -32,6 +33,14 @@ func (s *Scope) Parent() *Scope { return s.parent } +// Node returns the ast.Node responsible for this scope. +// The result is nil if there is no corresponding node +// (e.g., for the universe scope, package scope, or +// imported packages). +func (s *Scope) Node() ast.Node { + return s.node +} + // NumEntries() returns the number of scope entries. // If s == nil, the result is 0. func (s *Scope) NumEntries() int { diff --git a/go/types/sizes.go b/go/types/sizes.go index 9154e125..6d1ab2e0 100644 --- a/go/types/sizes.go +++ b/go/types/sizes.go @@ -40,15 +40,12 @@ func (ctxt *Context) offsetsof(s *Struct) []int64 { } // 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. +// the index sequence relative to typ. All embedded fields +// must be structs (rather than pointer to structs). func (ctxt *Context) offsetof(typ Type, index []int) int64 { var o int64 for _, i := range index { - s, _ := typ.Underlying().(*Struct) - if s == nil { - return -1 - } + s := typ.Underlying().(*Struct) o += ctxt.offsetsof(s)[i] typ = s.fields[i].typ } diff --git a/go/types/stmt.go b/go/types/stmt.go index b6f2d92e..c0327a4e 100644 --- a/go/types/stmt.go +++ b/go/types/stmt.go @@ -299,8 +299,12 @@ func (check *checker) multipleDefaults(list []ast.Stmt) { } } -func (check *checker) openScope() { - check.topScope = NewScope(check.topScope) +func (check *checker) openScope(node ast.Node) { + s := NewScope(check.topScope) + if retainASTLinks { + s.node = node + } + check.topScope = s } func (check *checker) closeScope() { @@ -516,12 +520,12 @@ func (check *checker) stmt(s ast.Stmt) { // TODO(gri) implement this case *ast.BlockStmt: - check.openScope() + check.openScope(s) check.stmtList(s.List) check.closeScope() case *ast.IfStmt: - check.openScope() + check.openScope(s) check.optionalStmt(s.Init) var x operand check.expr(&x, s.Cond, nil, -1) @@ -533,7 +537,7 @@ func (check *checker) stmt(s ast.Stmt) { check.closeScope() case *ast.SwitchStmt: - check.openScope() + check.openScope(s) check.optionalStmt(s.Init) var x operand tag := s.Tag @@ -594,14 +598,14 @@ func (check *checker) stmt(s ast.Stmt) { } } } - check.openScope() + check.openScope(clause) check.stmtList(clause.Body) check.closeScope() } check.closeScope() case *ast.TypeSwitchStmt: - check.openScope() + check.openScope(s) check.optionalStmt(s.Init) // A type switch guard must be of the form: @@ -682,7 +686,7 @@ func (check *checker) stmt(s ast.Stmt) { } } } - check.openScope() + check.openScope(clause) // If lhs exists, declare a corresponding object in the case-local scope if necessary. if lhs != nil { // A single-type case clause implicitly declares a new variable shadowing lhs. @@ -704,14 +708,14 @@ func (check *checker) stmt(s ast.Stmt) { if clause == nil { continue // error reported before } - check.openScope() + check.openScope(clause) check.optionalStmt(clause.Comm) // TODO(gri) check correctness of c.Comm (must be Send/RecvStmt) check.stmtList(clause.Body) check.closeScope() } case *ast.ForStmt: - check.openScope() + check.openScope(s) check.optionalStmt(s.Init) if s.Cond != nil { var x operand @@ -725,7 +729,7 @@ func (check *checker) stmt(s ast.Stmt) { check.closeScope() case *ast.RangeStmt: - check.openScope() + check.openScope(s) // check expression to iterate over decl := s.Tok == token.DEFINE var x operand diff --git a/go/types/testdata/builtins.src b/go/types/testdata/builtins.src index 6df085df..d968e6e9 100644 --- a/go/types/testdata/builtins.src +++ b/go/types/testdata/builtins.src @@ -320,6 +320,8 @@ type S2 struct{ // offset *S1 // 0 } // 8 +func (S2) m() {} + func _Alignof() { var x int _ = unsafe /* ERROR "argument" */ .Alignof() @@ -368,9 +370,13 @@ func _Offsetof() { assert(unsafe.Offsetof(y1.d) == 48) // relative to S1 assert(unsafe.Offsetof(y1.e) == 56) // relative to S1 + var y1p *S1 + assert(unsafe.Offsetof(y1p.S0) == 32) + var y2 S2 assert(unsafe.Offsetof(y2.S1) == 0) _ = unsafe.Offsetof(y2 /* ERROR "embedded via a pointer" */ .x) + _ = unsafe.Offsetof(y2 /* ERROR "method value" */ .m) } func _Sizeof() { diff --git a/go/types/testdata/decls0.src b/go/types/testdata/decls0.src index f8a50482..d2a1b6be 100644 --- a/go/types/testdata/decls0.src +++ b/go/types/testdata/decls0.src @@ -60,7 +60,7 @@ type ( type ( - p1 pi /* ERROR "no single field or method foo" */ .foo + p1 pi /* ERROR "no field or method foo" */ .foo p2 unsafe.Pointer ) @@ -96,9 +96,8 @@ type ( u, v, a /* ERROR "redeclared" */ float32 } S2 struct { - U // anonymous field - // TODO(gri) recognize double-declaration below - // U /* ERROR "redeclared" */ int + S0 // anonymous field + S0 /* ERROR "redeclared" */ int } S3 struct { x S2 diff --git a/go/types/testdata/decls2a.src b/go/types/testdata/decls2a.src index 94d768c3..aec2349f 100644 --- a/go/types/testdata/decls2a.src +++ b/go/types/testdata/decls2a.src @@ -32,7 +32,7 @@ type T1c struct { func (T1c) Pointer /* ERROR "field and method" */ () int { return 0 } -var _ = T1c /* ERROR "no single field or method" */ {}.Pointer +var _ = T1c{}.Pointer // T2's method declared before the type. func (*T2) f /* ERROR "field and method" */ () {} diff --git a/go/types/testdata/decls3.src b/go/types/testdata/decls3.src index 9b54e0c0..993ce000 100644 --- a/go/types/testdata/decls3.src +++ b/go/types/testdata/decls3.src @@ -18,7 +18,7 @@ func _() { ) var t T3 - _ = t /* ERROR "no single field or method" */ .X + _ = t /* ERROR "ambiguous selector" */ .X } func _() { @@ -30,7 +30,7 @@ func _() { ) var t T4 - _ = t /* ERROR "no single field or method" */ .X + _ = t /* ERROR "ambiguous selector" */ .X } func issue4355() { @@ -43,7 +43,7 @@ func issue4355() { ) var t T5 - _ = t /* ERROR "no single field or method" */ .X + _ = t /* ERROR "ambiguous selector" */ .X } func _() { @@ -53,7 +53,7 @@ func _() { type T struct{ A; B } var t T - _ = t /* ERROR "no single field or method" */ .Pointer +_ = t /* ERROR "ambiguous selector" */ .Pointer } // Embedded fields can be predeclared types. @@ -78,6 +78,32 @@ func _() { _ = y.f } +// Restrictions on embedded field types. + +func _() { + type I1 interface{} + type I2 interface{} + type P1 *int + type P2 *int + + type T1 struct { + I1 + * /* ERROR "cannot be a pointer to an interface" */ I2 + * /* ERROR "cannot be a pointer to an interface" */ error + P1 /* ERROR "cannot be a pointer" */ + * /* ERROR "cannot be a pointer" */ P2 + } + + // unsafe.Pointers are not regular pointers + type T2 struct { + unsafe.Pointer + } + + type T3 struct { + *unsafe.Pointer + } +} + // Borrowed from the FieldByName test cases in reflect/all_test.go. type D1 struct { @@ -165,9 +191,9 @@ type S13 struct { } func _() { - _ = struct /* ERROR "no single field or method" */ {}{}.Foo + _ = struct /* ERROR "no field or method" */ {}{}.Foo _ = S0{}.A - _ = S0 /* ERROR "no single field or method" */ {}.D + _ = S0 /* ERROR "no field or method" */ {}.D _ = S1{}.A _ = S1{}.B _ = S1{}.S0 @@ -176,17 +202,17 @@ func _() { _ = S2{}.S1 _ = S2{}.B _ = S2{}.C - _ = S2 /* ERROR "no single field or method" */ {}.D - _ = S3 /* ERROR "no single field or method" */ {}.S1 + _ = S2 /* ERROR "no field or method" */ {}.D + _ = S3 /* ERROR "ambiguous selector" */ {}.S1 _ = S3{}.A - _ = S3 /* ERROR "no single field or method" */ {}.B + _ = S3 /* ERROR "ambiguous selector" */ {}.B _ = S3{}.D _ = S3{}.E _ = S4{}.A - _ = S4 /* ERROR "no single field or method" */ {}.B - _ = S5 /* ERROR "no single field or method" */ {}.X + _ = S4 /* ERROR "no field or method" */ {}.B + _ = S5 /* ERROR "ambiguous selector" */ {}.X _ = S5{}.Y - _ = S10 /* ERROR "no single field or method" */ {}.X + _ = S10 /* ERROR "ambiguous selector" */ {}.X _ = S10{}.Y } @@ -262,4 +288,4 @@ type R22 R21 type R23 R21 type R24 R21 -var _ = R0 /* ERROR "no single field or method" */ {}.X \ No newline at end of file +var _ = R0 /* ERROR "ambiguous selector" */ {}.X \ No newline at end of file diff --git a/go/types/testdata/expr3.src b/go/types/testdata/expr3.src index ccb084cc..0a9abd7f 100644 --- a/go/types/testdata/expr3.src +++ b/go/types/testdata/expr3.src @@ -104,11 +104,13 @@ type T struct { func (*T) m() {} func method_expressions() { - _ = T /* ERROR "no single field or method" */ .a + _ = T /* ERROR "no field or method" */ .a _ = T /* ERROR "has no method" */ .x - _ = T.m - var f func(*T) = (*T).m - var g func(*T) = ( /* ERROR "cannot initialize" */ T).m + _ = T /* ERROR "not in method set" */ .m + _ = (*T).m + + var f func(*T) = T /* ERROR "not in method set" */ .m + var g func(*T) = (*T).m _ = T /* ERROR "has no method" */ .y _ = ( /* ERROR "has no method" */ *T).y @@ -274,7 +276,8 @@ func type_asserts() { var t I _ = t /* ERROR "use of .* outside type switch" */ .(type) - _ = t.(T) + _ = t.(T /* ERROR "missing method m" */ ) + _ = t.(*T) _ = t.(T1 /* ERROR "missing method m" */ ) _ = t.(T2 /* ERROR "wrong type for method m" */ ) _ = t.(I2 /* ERROR "wrong type for method m" */ ) diff --git a/go/types/testdata/methodsets.src b/go/types/testdata/methodsets.src new file mode 100644 index 00000000..c12e7cd1 --- /dev/null +++ b/go/types/testdata/methodsets.src @@ -0,0 +1,190 @@ +// 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. + +package methodsets + +type T0 struct {} + +func (T0) v0() {} +func (*T0) p0() {} + +type T1 struct {} // like T0 with different method names + +func (T1) v1() {} +func (*T1) p1() {} + +type T2 interface { + v2() + p2() +} + +type T3 struct { + T0 + *T1 + T2 +} + +// Method expressions +func _() { + var ( + _ func(T0) = T0.v0 + _ = T0 /* ERROR "not in method set" */ .p0 + + _ func (*T0) = (*T0).v0 + _ func (*T0) = (*T0).p0 + + // T1 is like T0 + + _ func(T2) = T2.v2 + _ func(T2) = T2.p2 + + _ func(T3) = T3.v0 + _ func(T3) = T3 /* ERROR "not in method set" */ .p0 + _ func(T3) = T3.v1 + _ func(T3) = T3.p1 + _ func(T3) = T3.v2 + _ func(T3) = T3.p2 + + _ func(*T3) = (*T3).v0 + _ func(*T3) = (*T3).p0 + _ func(*T3) = (*T3).v1 + _ func(*T3) = (*T3).p1 + _ func(*T3) = (*T3).v2 + _ func(*T3) = (*T3).p2 + ) +} + +// Method values with addressable receivers +func _() { + var ( + v0 T0 + _ func() = v0.v0 + _ func() = v0.p0 + ) + + var ( + p0 *T0 + _ func() = p0.v0 + _ func() = p0.p0 + ) + + // T1 is like T0 + + var ( + v2 T2 + _ func() = v2.v2 + _ func() = v2.p2 + ) + + var ( + v4 T3 + _ func() = v4.v0 + _ func() = v4.p0 + _ func() = v4.v1 + _ func() = v4.p1 + _ func() = v4.v2 + _ func() = v4.p2 + ) + + var ( + p4 *T3 + _ func() = p4.v0 + _ func() = p4.p0 + _ func() = p4.v1 + _ func() = p4.p1 + _ func() = p4.v2 + _ func() = p4.p2 + ) +} + +// Method calls with addressable receivers +func _() { + var v0 T0 + v0.v0() + v0.p0() + + var p0 *T0 + p0.v0() + p0.p0() + + // T1 is like T0 + + var v2 T2 + v2.v2() + v2.p2() + + var v4 T3 + v4.v0() + v4.p0() + v4.v1() + v4.p1() + v4.v2() + v4.p2() + + var p4 *T3 + p4.v0() + p4.p0() + p4.v1() + p4.p1() + p4.v2() + p4.p2() +} + +// Method values with value receivers +func _() { + var ( + _ func() = T0{}.v0 + _ func() = T0 /* ERROR "not in method set" */ {}.p0 + + _ func() = (&T0{}).v0 + _ func() = (&T0{}).p0 + + // T1 is like T0 + + // no values for T2 + + _ func() = T3{}.v0 + _ func() = T3 /* ERROR "not in method set" */ {}.p0 + _ func() = T3{}.v1 + _ func() = T3{}.p1 + _ func() = T3{}.v2 + _ func() = T3{}.p2 + + _ func() = (&T3{}).v0 + _ func() = (&T3{}).p0 + _ func() = (&T3{}).v1 + _ func() = (&T3{}).p1 + _ func() = (&T3{}).v2 + _ func() = (&T3{}).p2 + ) +} + +// Method calls with value receivers +// TODO(gri) For internal reasons, these expressions are evaluated twice, +// hence the double (equal) error messages for now. Fix this. +func _() { + T0{}.v0() + T0 /* ERROR "not in method set" */ /* ERROR "not in method set" */ {}.p0() + + (&T0{}).v0() + (&T0{}).p0() + + // T1 is like T0 + + // no values for T2 + + T3{}.v0() + T3 /* ERROR "not in method set" */ /* ERROR "not in method set" */ {}.p0() + T3{}.v1() + T3{}.p1() + T3{}.v2() + T3{}.p2() + + (&T3{}).v0() + (&T3{}).p0() + (&T3{}).v1() + (&T3{}).p1() + (&T3{}).v2() + (&T3{}).p2() +} diff --git a/go/types/types.go b/go/types/types.go index 228d9f2e..fa878955 100644 --- a/go/types/types.go +++ b/go/types/types.go @@ -382,6 +382,7 @@ func (t *Interface) Deref() Type { return t } func (t *Map) Deref() Type { return t } func (t *Chan) Deref() Type { return t } func (t *Named) Deref() Type { + // TODO(gri) Is this the right operation here given how Deref is used? if p, ok := t.underlying.(*Pointer); ok { return p.base }