diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index cb37e5229a..e87757f164 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -1288,7 +1288,7 @@ func (p *parser) parseChanType(typeContext bool) *ast.ChanType { // ContractType = "(" [ IdentList [ "," ] ] ")" "{" { Constraint ";" } "}" . // (The "contract" keyword is already consumed.) -func (p *parser) parseContractType() *ast.ContractType { +func (p *parser) parseContractType(pos token.Pos) *ast.ContractType { if p.trace { defer un(trace(p, "ContractType")) } @@ -1314,7 +1314,7 @@ func (p *parser) parseContractType() *ast.ContractType { } rbrace := p.expect(token.RBRACE) - return &ast.ContractType{TParams: params, Lbrace: lbrace, Constraints: constraints, Rbrace: rbrace} + return &ast.ContractType{Contract: pos, TParams: params, Lbrace: lbrace, Constraints: constraints, Rbrace: rbrace} } // Constraint = TypeParam TypeOrMethod { "," TypeOrMethod } | ContractTypeName "(" [ TypeList [ "," ] ] ")" . @@ -1408,8 +1408,7 @@ func (p *parser) tryIdentOrType(typeContext bool) ast.Expr { if p.lit == "contract" { pos := p.pos p.next() - typ := p.parseContractType() - typ.Contract = pos // set keyword position + typ := p.parseContractType(pos) return typ } typ := p.parseTypeName(nil) @@ -2655,7 +2654,7 @@ func (p *parser) parseStmt() (s ast.Stmt) { // ---------------------------------------------------------------------------- // Declarations -type parseSpecFunction func(doc *ast.CommentGroup, keyword token.Token, iota int) ast.Spec +type parseSpecFunction func(doc *ast.CommentGroup, pos token.Pos, keyword token.Token, iota int) ast.Spec func isValidImport(lit string) bool { const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD" @@ -2668,7 +2667,7 @@ func isValidImport(lit string) bool { return s != "" } -func (p *parser) parseImportSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast.Spec { +func (p *parser) parseImportSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token, _ int) ast.Spec { if p.trace { defer un(trace(p, "ImportSpec")) } @@ -2707,7 +2706,7 @@ func (p *parser) parseImportSpec(doc *ast.CommentGroup, _ token.Token, _ int) as return spec } -func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota int) ast.Spec { +func (p *parser) parseValueSpec(doc *ast.CommentGroup, _ token.Pos, keyword token.Token, iota int) ast.Spec { if p.trace { defer un(trace(p, keyword.String()+"Spec")) } @@ -2754,7 +2753,7 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota return spec } -func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast.Spec { +func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token, _ int) ast.Spec { if p.trace { defer un(trace(p, "TypeSpec")) } @@ -2835,7 +2834,7 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast. return spec } -func (p *parser) parseContractSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast.Spec { +func (p *parser) parseContractSpec(doc *ast.CommentGroup, pos token.Pos, _ token.Token, _ int) ast.Spec { if p.trace { defer un(trace(p, "ContractSpec")) } @@ -2843,7 +2842,7 @@ func (p *parser) parseContractSpec(doc *ast.CommentGroup, _ token.Token, _ int) // For now we represent a contract specification like a type representation. // They cannot have "outer" type parameters, though. ident := p.parseIdent() - typ := p.parseContractType() + typ := p.parseContractType(pos) spec := &ast.TypeSpec{Doc: doc, Name: ident, Type: typ} p.declare(spec, nil, p.topScope, ast.Typ, ident) p.expectSemi() // call before accessing p.linecomment @@ -2865,12 +2864,12 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.Gen lparen = p.pos p.next() for iota := 0; p.tok != token.RPAREN && p.tok != token.EOF; iota++ { - list = append(list, f(p.leadComment, keyword, iota)) + list = append(list, f(p.leadComment, pos, keyword, iota)) } rparen = p.expect(token.RPAREN) p.expectSemi() } else { - list = append(list, f(nil, keyword, 0)) + list = append(list, f(nil, pos, keyword, 0)) } return &ast.GenDecl{ diff --git a/src/go/types/check.go b/src/go/types/check.go index 27b271e0ba..bac9aee300 100644 --- a/src/go/types/check.go +++ b/src/go/types/check.go @@ -271,7 +271,7 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) { print("== collectObjects ==") check.collectObjects() - print("== packagetObjects ==") + print("== packageObjects ==") check.packageObjects() print("== processDelayed ==") diff --git a/src/go/types/contracts.go b/src/go/types/contracts.go index cbe1cf7eed..2723fd8195 100644 --- a/src/go/types/contracts.go +++ b/src/go/types/contracts.go @@ -153,7 +153,7 @@ func (check *Checker) contractType(contr *Contract, e *ast.ContractType) { if iface == nil { ifaces[tpar] = &emptyInterface } else { - check.completeInterface(iface) + check.completeInterface(e.Pos(), iface) } } diff --git a/src/go/types/decl.go b/src/go/types/decl.go index f3a59bacb8..530a66e0fa 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -52,7 +52,7 @@ func pathString(path []Object) string { // objDecl type-checks the declaration of obj in its respective (file) context. // For the meaning of def, see Checker.definedType, in typexpr.go. func (check *Checker) objDecl(obj Object, def *Named) { - if check.conf.Trace { + if check.conf.Trace && obj.Type() == nil { check.trace(obj.Pos(), "-- checking %s (%s, objPath = %s)", obj, obj.color(), pathString(check.objPath)) check.indent++ defer func() { diff --git a/src/go/types/expr.go b/src/go/types/expr.go index 5dd89e9cec..b01a4950f9 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -558,7 +558,7 @@ func (check *Checker) convertUntyped(x *operand, target Type) { target = Typ[UntypedNil] } else { // cannot assign untyped values to non-empty interfaces - check.completeInterface(t) + check.completeInterface(token.NoPos, t) if !t.Empty() { goto Error } diff --git a/src/go/types/exprstring.go b/src/go/types/exprstring.go index 28d605f5ee..e89950e19b 100644 --- a/src/go/types/exprstring.go +++ b/src/go/types/exprstring.go @@ -31,7 +31,7 @@ func WriteExpr(buf *bytes.Buffer, x ast.Expr) { switch x := x.(type) { default: - buf.WriteString("(bad expr)") // nil, ast.BadExpr, ast.KeyValueExpr + buf.WriteString("(ast: bad expr)") // nil, ast.BadExpr, ast.KeyValueExpr case *ast.Ident: buf.WriteString(x.Name) @@ -164,6 +164,12 @@ func WriteExpr(buf *bytes.Buffer, x ast.Expr) { } buf.WriteString(s) WriteExpr(buf, x.Value) + + case *ast.ContractType: + buf.WriteString("contract(") + writeIdentList(buf, x.TParams) + buf.WriteString("){...}") + // TODO(gri) fill in the rest } } @@ -199,12 +205,7 @@ func writeFieldList(buf *bytes.Buffer, fields *ast.FieldList, sep string, iface } // field list names - for i, name := range f.Names { - if i > 0 { - buf.WriteString(", ") - } - buf.WriteString(name.Name) - } + writeIdentList(buf, f.Names) // types of interface methods consist of signatures only if sig, _ := f.Type.(*ast.FuncType); sig != nil && iface { @@ -222,3 +223,12 @@ func writeFieldList(buf *bytes.Buffer, fields *ast.FieldList, sep string, iface // ignore tag } } + +func writeIdentList(buf *bytes.Buffer, list []*ast.Ident) { + for i, x := range list { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(x.Name) + } +} diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go index a6c9edc35e..78b0f52f9c 100644 --- a/src/go/types/lookup.go +++ b/src/go/types/lookup.go @@ -6,6 +6,8 @@ package types +import "go/token" + // LookupFieldOrMethod looks up a field or method with given package and name // in T and returns the corresponding *Var or *Func, an index sequence, and a // bool indicating if there were any pointer indirections on the path to the @@ -175,7 +177,7 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack case *Interface: // look for a matching method // TODO(gri) t.allMethods is sorted - use binary search - check.completeInterface(t) + check.completeInterface(token.NoPos, t) if i, m := lookupMethod(t.allMethods, pkg, name); m != nil { assert(m.typ != nil) index = concat(e.index, i) @@ -276,7 +278,7 @@ func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType b // To improve error messages, also report the wrong signature // when the method exists on *V instead of V. func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, wrongType *Func) { - check.completeInterface(T) + check.completeInterface(token.NoPos, T) // fast path for common case if T.Empty() { @@ -284,7 +286,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, } if ityp, _ := V.Underlying().(*Interface); ityp != nil { - check.completeInterface(ityp) + check.completeInterface(token.NoPos, ityp) // TODO(gri) allMethods is sorted - can do this more efficiently for _, m := range T.allMethods { _, obj := lookupMethod(ityp.allMethods, m.pkg, m.name) diff --git a/src/go/types/operand.go b/src/go/types/operand.go index 05ac8ad838..a39c33d32f 100644 --- a/src/go/types/operand.go +++ b/src/go/types/operand.go @@ -247,7 +247,7 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) bool { return Vb.kind == UntypedBool && isBoolean(Tu) } case *Interface: - check.completeInterface(t) + check.completeInterface(token.NoPos, t) return x.isNil() || t.Empty() case *Pointer, *Signature, *Slice, *Map, *Chan: return x.isNil() diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go index 8c6bf21435..6b6dbf7555 100644 --- a/src/go/types/predicates.go +++ b/src/go/types/predicates.go @@ -6,7 +6,10 @@ package types -import "sort" +import ( + "go/token" + "sort" +) func isNamed(typ Type) bool { if _, ok := typ.(*Basic); ok { @@ -212,8 +215,8 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair, tparams // that case, interfaces are expected to be complete and lazy completion // here is not needed. if check != nil { - check.completeInterface(x) - check.completeInterface(y) + check.completeInterface(token.NoPos, x) + check.completeInterface(token.NoPos, y) } a := x.allMethods b := y.allMethods diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 2e5884ff27..7262bdc364 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -307,22 +307,6 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type { def.setUnderlying(typ) return typ - /* - // We may have a parameterized type or an "instantiated" contract. - typ := new(Parameterized) - def.setUnderlying(typ) - if check.parameterizedType(typ, e) { - if IsParameterizedList(typ.targs) { - return typ - } - typ := check.inst(typ.tname, typ.targs) - def.setUnderlying(typ) // TODO(gri) do we need this? - return typ - } - // TODO(gri) If we have a cycle and we reach here, "leafs" of - // the cycle may refer to a not fully set up Parameterized typ. - */ - case *ast.ParenExpr: return check.definedType(e.X, def) @@ -649,10 +633,10 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d sort.Sort(byUniqueMethodName(ityp.methods)) sort.Stable(byUniqueTypeName(ityp.embeddeds)) - check.later(func() { check.completeInterface(ityp) }) + check.later(func() { check.completeInterface(iface.Pos(), ityp) }) } -func (check *Checker) completeInterface(ityp *Interface) { +func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) { if ityp.allMethods != nil { return } @@ -666,11 +650,18 @@ func (check *Checker) completeInterface(ityp *Interface) { } if check.conf.Trace { - check.trace(token.NoPos, "complete %s", ityp) + // Types don't generally have position information. + // If we don't have a valid pos provided, try to use + // one close enough. + if !pos.IsValid() && len(ityp.methods) > 0 { + pos = ityp.methods[0].pos + } + + check.trace(pos, "complete %s", ityp) check.indent++ defer func() { check.indent-- - check.trace(token.NoPos, "=> %s", ityp) + check.trace(pos, "=> %s", ityp) }() } @@ -729,7 +720,7 @@ func (check *Checker) completeInterface(ityp *Interface) { // Ignore it. continue } - check.completeInterface(typ) + check.completeInterface(pos, typ) for _, m := range typ.allMethods { addMethod(pos, m, false) // use embedding position pos rather than m.pos }