diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go index ea8f21c498..fb530e41e8 100644 --- a/src/go/ast/ast.go +++ b/src/go/ast/ast.go @@ -257,11 +257,12 @@ func (f *FieldList) NumFields() int { return n } -// A TypeParamList represents a list of type parameters, enclosed by parentheses. +// A TypeParamList represents a list of type parameters with contract, enclosed by parentheses. type TypeParamList struct { - Lparen token.Pos // position of "(" - Names []*Ident // type parameter names; or nil - Rparen token.Pos // position of ")" + Lparen token.Pos // position of "(" + Names []*Ident // type parameter names; or nil + Contract Expr // contract; or nil + Rparen token.Pos // position of ")" } func (t *TypeParamList) Pos() token.Pos { return t.Lparen } @@ -464,7 +465,7 @@ type ( // A ContractType node represents a contract. ContractType struct { - Contract token.Pos // position of "contract" pseudo keeyword + Contract token.Pos // position of "contract" pseudo keyword Params *TypeParamList // list of type parameters; non-nil Lbrace token.Pos // position of "{" Constraints []*Constraint // list of constraints @@ -913,11 +914,12 @@ type ( // A TypeSpec node represents a type declaration (TypeSpec production). TypeSpec struct { - Doc *CommentGroup // associated documentation; or nil - Name *Ident // type name - Assign token.Pos // position of '=', if any - Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes - Comment *CommentGroup // line comments; or nil + Doc *CommentGroup // associated documentation; or nil + Name *Ident // type name + TPar *TypeParamList // type parameters; or nil + Assign token.Pos // position of '=', if any + Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes + Comment *CommentGroup // line comments; or nil } ) diff --git a/src/go/parser/error_test.go b/src/go/parser/error_test.go index 9b79097acf..c7f93c9a56 100644 --- a/src/go/parser/error_test.go +++ b/src/go/parser/error_test.go @@ -180,7 +180,7 @@ func TestErrors(t *testing.T) { } for _, fi := range list { name := fi.Name() - if !fi.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".src") { + if !fi.IsDir() && !strings.HasPrefix(name, ".") && (strings.HasSuffix(name, ".src") || strings.HasSuffix(name, ".go2")) { checkErrors(t, filepath.Join(testdata, name), nil) } } diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index 31f650bd34..ab6175047a 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -26,7 +26,7 @@ import ( "unicode" ) -const useBrackets = true +const useBrackets = false // The parser structure holds the parser's internal state. type parser struct { @@ -706,54 +706,6 @@ func (p *parser) makeIdentList(list []ast.Expr) []*ast.Ident { return idents } -func (p *parser) parseFieldDeclOld(scope *ast.Scope) *ast.Field { - if p.trace { - defer un(trace(p, "FieldDecl")) - } - - doc := p.leadComment - - // 1st FieldDecl - // A type name used as an anonymous field looks like a field identifier. - var list []ast.Expr - for { - list = append(list, p.parseVarType(false)) - if p.tok != token.COMMA { - break - } - p.next() - } - - typ := p.tryVarType(false) - - // analyze case - var idents []*ast.Ident - if typ != nil { - // IdentifierList Type - idents = p.makeIdentList(list) - } else { - // ["*"] TypeName (AnonymousField) - typ = list[0] // we always have at least one element - if n := len(list); n > 1 { - p.errorExpected(p.pos, "type") - typ = &ast.BadExpr{From: p.pos, To: p.pos} - } else if !isTypeName(deref(typ)) { - p.errorExpected(typ.Pos(), "anonymous field") - typ = &ast.BadExpr{From: typ.Pos(), To: p.safePos(typ.End())} - } - } - - tag := p.parseOptionalTag() - - p.expectSemi() // call before accessing p.linecomment - - field := &ast.Field{Doc: doc, Names: idents, Type: typ, Tag: tag, Comment: p.lineComment} - p.declare(field, nil, scope, ast.Var, idents...) - p.resolve(typ) - - return field -} - func (p *parser) parseOptionalTag() (tag *ast.BasicLit) { if p.trace { defer un(trace(p, "OptionalTag")) @@ -966,35 +918,6 @@ func (p *parser) parsePointerType(typeContext bool) *ast.StarExpr { return &ast.StarExpr{Star: star, X: base} } -// If the result is an identifier, it is not resolved. -func (p *parser) tryVarType(isParam bool) ast.Expr { - if isParam && p.tok == token.ELLIPSIS { - pos := p.pos - p.next() - typ := p.tryIdentOrType(true) // don't use parseType so we can provide better error message - if typ != nil { - p.resolve(typ) - } else { - p.error(pos, "'...' parameter is missing type") - typ = &ast.BadExpr{From: pos, To: p.pos} - } - return &ast.Ellipsis{Ellipsis: pos, Elt: typ} - } - return p.tryIdentOrType(true) -} - -// If the result is an identifier, it is not resolved. -func (p *parser) parseVarType(isParam bool) ast.Expr { - typ := p.tryVarType(isParam) - if typ == nil { - pos := p.pos - p.errorExpected(pos, "type") - p.next() // make progress - typ = &ast.BadExpr{From: pos, To: p.pos} - } - return typ -} - func (p *parser) parseDotsType() *ast.Ellipsis { if p.trace { defer un(trace(p, "DotsType")) @@ -1025,8 +948,12 @@ func (p *parser) parseParamDeclOrNil() (f field) { f.typ = p.parseType(true) case token.LBRACK: - // name[type1, type2, ...] or name []type or name [len]type - f.name, f.typ = p.parseArrayFieldOrTypeInstance(f.name) + if useBrackets { + // name[type1, type2, ...] or name []type or name [len]type + f.name, f.typ = p.parseArrayFieldOrTypeInstance(f.name) + } else { + f.typ = p.parseType(true) + } case token.ELLIPSIS: // name ...type @@ -1154,66 +1081,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [ return } -func (p *parser) parseParameterListOld(scope *ast.Scope, ellipsisOk bool) (params []*ast.Field) { - if p.trace { - defer un(trace(p, "ParameterList")) - } - - // 1st ParameterDecl - // A list of identifiers looks like a list of type names. - var list []ast.Expr - for { - list = append(list, p.parseVarType(ellipsisOk)) - if p.tok != token.COMMA { - break - } - p.next() - if p.tok == token.RPAREN { - break - } - } - - // analyze case - if typ := p.tryVarType(ellipsisOk); typ != nil { - // IdentifierList Type - idents := p.makeIdentList(list) - field := &ast.Field{Names: idents, Type: typ} - params = append(params, field) - // Go spec: The scope of an identifier denoting a function - // parameter or result variable is the function body. - p.declare(field, nil, scope, ast.Var, idents...) - p.resolve(typ) - if !p.atComma("parameter list", token.RPAREN) { - return - } - p.next() - for p.tok != token.RPAREN && p.tok != token.EOF { - idents := p.parseIdentList() - typ := p.parseVarType(ellipsisOk) - field := &ast.Field{Names: idents, Type: typ} - params = append(params, field) - // Go spec: The scope of an identifier denoting a function - // parameter or result variable is the function body. - p.declare(field, nil, scope, ast.Var, idents...) - p.resolve(typ) - if !p.atComma("parameter list", token.RPAREN) { - break - } - p.next() - } - return - } - - // Type { "," Type } (anonymous parameters) - params = make([]*ast.Field, len(list)) - for i, typ := range list { - p.resolve(typ) - params[i] = &ast.Field{Type: typ} - } - return -} - -func (p *parser) parseTypeParams(scope *ast.Scope, contractOk bool) *ast.TypeParamList { +func (p *parser) parseTypeParams(scope *ast.Scope) *ast.TypeParamList { if p.trace { defer un(trace(p, "TypeParams")) } @@ -1228,19 +1096,20 @@ func (p *parser) parseTypeParams(scope *ast.Scope, contractOk bool) *ast.TypePar } var names []*ast.Ident + var contract ast.Expr if p.tok != token.RBRACK && p.tok != token.RPAREN { names = p.parseIdentList() } - if contractOk && p.tok == token.IDENT { + if p.tok == token.IDENT { // contract - p.parseType(true) + contract = p.parseType(true) } if lbrack.IsValid() { rbrack = p.expect(token.RBRACK) } - tparams := &ast.TypeParamList{Lparen: lbrack, Names: names, Rparen: rbrack} + tparams := &ast.TypeParamList{Lparen: lbrack, Names: names, Contract: contract, Rparen: rbrack} p.declare(tparams, nil, scope, ast.Typ, names...) return tparams } @@ -1253,14 +1122,14 @@ func (p *parser) parseParameters(scope *ast.Scope, typeParamsOk, ellipsisOk bool var lparen token.Pos if useBrackets && p.tok == token.LBRACK { // assume [type T](params) syntax - tparams = p.parseTypeParams(scope, true) + tparams = p.parseTypeParams(scope) lparen = p.expect(token.LPAREN) } else { // assume (type T)(params) syntax lparen = p.expect(token.LPAREN) if p.tok == token.TYPE { p.next() - tparams = p.parseTypeParams(scope, true) + tparams = p.parseTypeParams(scope) rparen := p.expect(token.RPAREN) // fix tparams @@ -1407,14 +1276,13 @@ func (p *parser) parseChanType(typeContext bool) *ast.ChanType { return &ast.ChanType{Begin: pos, Arrow: arrow, Dir: dir, Value: value} } -// ContractType = "contract" "(" [ IdentList [ "," ] ] ")" "{" { Constraint ";" } "}" . +// ContractType = "(" [ IdentList [ "," ] ] ")" "{" { Constraint ";" } "}" . +// (The "contract" keyword is already consumed.) func (p *parser) parseContractType() *ast.ContractType { if p.trace { defer un(trace(p, "ContractType")) } - pos := p.expect(token.IDENT) // TODO(gri) check that it is "contract" - var names []*ast.Ident lparen := p.expect(token.LPAREN) scope := ast.NewScope(nil) // contract scope @@ -1437,12 +1305,11 @@ func (p *parser) parseContractType() *ast.ContractType { } rbrace := p.expect(token.RBRACE) - return &ast.ContractType{Contract: pos, Params: params, Lbrace: lbrace, Constraints: constraints, Rbrace: rbrace} + return &ast.ContractType{Params: params, Lbrace: lbrace, Constraints: constraints, Rbrace: rbrace} } -// Constraint = TypeParam TypeConstraint | TypeParam MethodName Signature | ContractTypeName "(" [ TypeList [ "," ] ] ")" . +// Constraint = TypeParam Type | TypeParam MethodName Signature | ContractTypeName "(" [ TypeList [ "," ] ] ")" . // TypeParam = Ident . -// TypeConstraint = Type | "0" | "0.0" | "0i" | "==" | "!=" . // ContractTypeName = TypeName. func (p *parser) parseConstraint() *ast.Constraint { if p.trace { @@ -1461,37 +1328,16 @@ func (p *parser) parseConstraint() *ast.Constraint { param = &ast.Ident{NamePos: tname.Pos(), Name: "_"} } + // type constraint or method var mname *ast.Ident - var typ ast.Expr - switch p.tok { - case token.INT: - typ = p.wantLit("0") - - case token.FLOAT: - typ = p.wantLit("0.0") - - case token.IMAG: - typ = p.wantLit("0i") - - case token.EQL: - mname = &ast.Ident{NamePos: p.pos, Name: "=="} - p.next() - - case token.NEQ: - mname = &ast.Ident{NamePos: p.pos, Name: "!="} - p.next() - - default: - // type constraint or method - typ = p.parseType(false) - if ident, isIdent := typ.(*ast.Ident); isIdent && p.tok == token.LPAREN { - // method - mname = ident - scope := ast.NewScope(nil) // method scope - _, params := p.parseParameters(scope, false, true) - results := p.parseResult(scope, true) - typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results} - } + typ := p.parseType(false) + if ident, isIdent := typ.(*ast.Ident); isIdent && p.tok == token.LPAREN { + // method + mname = ident + scope := ast.NewScope(nil) // method scope + _, params := p.parseParameters(scope, false, true) + results := p.parseResult(scope, true) + typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results} } // param != nil @@ -1528,13 +1374,6 @@ func (p *parser) parseTypeInstance(typ ast.Expr) *ast.CallExpr { return &ast.CallExpr{Fun: typ, Lparen: opening, Args: list, Rparen: closing} } -func (p *parser) wantLit(lit string) *ast.BasicLit { - if p.lit != lit { - p.errorExpected(p.pos, lit) - } - return p.parseOperand(false).(*ast.BasicLit) -} - // If the result is an identifier, it is not resolved. // typeContext controls whether a trailing type parameter list (opening "(") // following a type is consumed. We need this to disambiguate an expression @@ -1546,7 +1385,11 @@ func (p *parser) tryIdentOrType(typeContext bool) ast.Expr { case token.IDENT: // TODO(gri) we need to be smarter about this to avoid problems with existing code if p.lit == "contract" { - return p.parseContractType() + pos := p.pos + p.next() + typ := p.parseContractType() + typ.Contract = pos // set keyword position + return typ } typ := p.parseTypeName(nil) if typeContext && (useBrackets && p.tok == token.LBRACK || p.tok == token.LPAREN) { @@ -2032,6 +1875,7 @@ L: x = p.parseCallOrConversion(p.checkExprOrType(x)) case token.LBRACE: // TODO(gri) currently this doesn't accept instantiated types + // Use code from cmd/compile/internal/syntax/parser.go. if isLiteralType(x) && (p.exprLev >= 0 || !isTypeName(x)) { if lhs { p.resolve(x) @@ -2919,17 +2763,18 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast. if p.tok == token.TYPE { // parametrized type p.next() - scope := ast.NewScope(nil) - tparams := p.parseTypeParams(scope, false) + p.openScope() + tparams := p.parseTypeParams(p.topScope) tparams.Lparen = lbrack tparams.Rparen = p.expect(token.RBRACK) - // TODO(gri) record scope and tparams with spec + spec.TPar = tparams if p.tok == token.ASSIGN { // type alias spec.Assign = p.pos p.next() } spec.Type = p.parseType(true) + p.closeScope() } else { // array type alen := p.parseArrayLen() @@ -2944,17 +2789,18 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast. if p.tok == token.TYPE { // parametrized type p.next() - scope := ast.NewScope(nil) - tparams := p.parseTypeParams(scope, false) + p.openScope() + tparams := p.parseTypeParams(p.topScope) tparams.Lparen = lparen tparams.Rparen = p.expect(token.RPAREN) - // TODO(gri) record scope and tparams with spec + spec.TPar = tparams if p.tok == token.ASSIGN { // type alias spec.Assign = p.pos p.next() } spec.Type = p.parseType(true) + p.closeScope() } else { // parenthesized type typ := p.parseType(true) @@ -2978,6 +2824,23 @@ 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 { + if p.trace { + defer un(trace(p, "ContractSpec")) + } + + // For now we represent a contract specification like a type representation. + // They cannot have "outer" type parameters, though. + ident := p.parseIdent() + typ := p.parseContractType() + 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 + spec.Comment = p.lineComment + + return spec +} + func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.GenDecl { if p.trace { defer un(trace(p, "GenDecl("+keyword.String()+")")) @@ -3087,6 +2950,14 @@ func (p *parser) parseDecl(sync map[token.Token]bool) ast.Decl { case token.FUNC: return p.parseFuncDecl() + case token.IDENT: + // TODO(gri) we need to be smarter about this to avoid problems with existing code + if p.lit == "contract" { + f = p.parseContractSpec + break + } + fallthrough + default: pos := p.pos p.errorExpected(pos, "declaration") diff --git a/src/go/parser/short_test.go b/src/go/parser/short_test.go index 0d0358d8d3..fe83adae13 100644 --- a/src/go/parser/short_test.go +++ b/src/go/parser/short_test.go @@ -50,35 +50,43 @@ var valids = []string{ `package p; type (T = p.T; _ = struct{}; x = *T)`, `package p; type T (*int)`, `package p; type T(type P) struct { P }`, + `package p; type T(type P comparable) struct { P }`, + `package p; type T(type P comparable(P)) struct { P }`, `package p; type T(type P1, P2) struct { P1; f []P2 }`, - `package p; type T[type] struct { P }`, - `package p; type T[type P] struct { P }`, - `package p; type T[type P1, P2] struct { P1; f []P2 }`, + // `package p; type T[type] struct { P }`, + // `package p; type T[type P] struct { P }`, + // `package p; type T[type P1, P2] struct { P1; f []P2 }`, `package p; var _ = [](T(int)){}`, `package p; var _ = func()T(nil)`, `package p; func _(type)()`, `package p; func _(type)()()`, - `package p; func _(T[P])`, + `package p; func _(T (P))`, + `package p; func _((T(P)))`, `package p; func _(T []E)`, `package p; func _(T [P]E)`, - `package p; func _(T[P1, P2, P3])`, + `package p; func _(x T(P1, P2, P3))`, + `package p; func _((T(P1, P2, P3)))`, `package p; func _(type A, B)(a A) B`, `package p; func _(type A, B C)(a A) B`, `package p; func _(type A, B C(A, B))(a A) B`, - `package p; type _ struct { T[P] }`, - `package p; type _ struct { T []E }`, - `package p; type _ struct { T [P]E }`, - `package p; type _ struct { imported.T[P] }`, - `package p; type _ struct { imported.T[P1, P2] }`, - `package p; func _[type]()`, - `package p; func _[type]()()`, - `package p; func _[type A, B](a A) B`, - `package p; func _[type A, B C](a A) B`, - `package p; func _[type A, B C(A, B)](a A) B`, + // `package p; type _ struct { T[P] }`, + // `package p; type _ struct { T []E }`, + // `package p; type _ struct { T [P]E }`, + // `package p; type _ struct { imported.T[P] }`, + // `package p; type _ struct { imported.T[P1, P2] }`, + // `package p; func _[type]()`, + // `package p; func _[type]()()`, + // `package p; func _[type A, B](a A) B`, + // `package p; func _[type A, B C](a A) B`, + // `package p; func _[type A, B C(A, B)](a A) B`, + `package p; contract C(){}`, + `package p; contract C(T, S, R,){}`, + `package p; contract C(T){ T (m(x, int)); }`, + `package p; contract (C1(){}; C2(){})`, `package p; type C contract(){}`, `package p; type C contract(T, S, R,){}`, `package p; type C contract(T){ T (m(x, int)); }`, - `package p; type C contract(T){ T int; T imported.T; T chan<-int; T 0.0; T ==; T m(x int) float64; C0(); imported.C1(int, T,) }`, + `package p; type C contract(T){ T int; T imported.T; T chan<-int; T m(x int) float64; C0(); imported.C1(int, T,) }`, } func TestValid(t *testing.T) { @@ -87,6 +95,12 @@ func TestValid(t *testing.T) { } } +// TestSingle is useful to track down a problem with a single short test program. +func TestSingle(t *testing.T) { + const src = `package p; func _((T(a, b,)))` + checkErrors(t, src, src) +} + var invalids = []string{ `foo /* ERROR "expected 'package'" */ !`, `package p; func f() { if { /* ERROR "missing condition" */ } };`, @@ -136,7 +150,6 @@ var invalids = []string{ //`package p; func f(x func(), u v func /* ERROR "missing ','" */ ()){}`, `package p; type C contract(T, T /* ERROR "T redeclared" */ ) {}`, `package p; type C contract(T) { imported /* ERROR "expected type parameter name" */ .T int }`, - `package p; type C contract(T) { T 00.i /* ERROR "expected 0i" */ }`, // issue 8656 `package p; func f() (a b string /* ERROR "missing ','" */ , ok bool)`, diff --git a/src/go/parser/testdata/map.go2 b/src/go/parser/testdata/map.go2 new file mode 100644 index 0000000000..036663c1c7 --- /dev/null +++ b/src/go/parser/testdata/map.go2 @@ -0,0 +1,111 @@ +// Package orderedmap provides an ordered map, implemented as a binary tree. +package orderedmap + +import "chans" + +// Map is an ordered map. +type Map(type K, V) struct { + root *node(K, V) + compare func(K, K) int +} + +// node is the type of a node in the binary tree. +type node(type K, V) struct { + key K + val V + left, right *node(K, V) +} + +// New returns a new map. +func New(type K, V)(compare func(K, K) int) *Map(K, V) { + // TODO + // return &Map(K, V){compare: compare} +} + +// find looks up key in the map, and returns either a pointer +// to the node holding key, or a pointer to the location where +// such a node would go. +func (m *Map(K, V)) find(key K) **node(K, V) { + pn := &m.root + for *pn != nil { + switch cmp := m.compare(key, (*pn).key); { + case cmp < 0: + pn = &(*pn).left + case cmp > 0: + pn = &(*pn).right + default: + return pn + } + } + return pn +} + +// Insert inserts a new key/value into the map. +// If the key is already present, the value is replaced. +// Returns true if this is a new key, false if already present. +func (m *Map(K, V)) Insert(key K, val V) bool { + pn := m.find(key) + if *pn != nil { + (*pn).val = val + return false + } + // TODO + // *pn = &node(K, V){key: key, val: val} + return true +} + +// Find returns the value associated with a key, or zero if not present. +// The found result reports whether the key was found. +func (m *Map(K, V)) Find(key K) (V, bool) { + pn := m.find(key) + if *pn == nil { + var zero V // see the discussion of zero values, above + return zero, false + } + return (*pn).val, true +} + +// keyValue is a pair of key and value used when iterating. +type keyValue(type K, V) struct { + key K + val V +} + +// InOrder returns an iterator that does an in-order traversal of the map. +func (m *Map(K, V)) InOrder() *Iterator(K, V) { + sender, receiver := chans.Ranger(keyValue(K, V))() + var f func(*node(K, V)) bool + f = func(n *node(K, V)) bool { + if n == nil { + return true + } + // Stop sending values if sender.Send returns false, + // meaning that nothing is listening at the receiver end. + return f(n.left) && + // TODO + // sender.Send(keyValue(K, V){n.key, n.val}) && + f(n.right) + } + go func() { + f(m.root) + sender.Close() + }() + return &Iterator{receiver} +} + +// Iterator is used to iterate over the map. +type Iterator(type K, V) struct { + r *chans.Receiver(keyValue(K, V)) +} + +// Next returns the next key and value pair, and a boolean indicating +// whether they are valid or whether we have reached the end. +func (it *Iterator(K, V)) Next() (K, V, bool) { + keyval, ok := it.r.Next() + if !ok { + var zerok K + var zerov V + return zerok, zerov, false + } + return keyval.key, keyval.val, true +} diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go index 87e47de4d4..a4a89559e1 100644 --- a/src/go/types/check_test.go +++ b/src/go/types/check_test.go @@ -99,7 +99,6 @@ var tests = [][]string{ {"testdata/issue28251.src"}, {"testdata/issue6977.src"}, {"testdata/typeparams.src"}, - {"testdata/typeparams2.src"}, } var fset = token.NewFileSet()