aboutsummaryrefslogtreecommitdiff
path: root/src/go/parser/parser.go
diff options
context:
space:
mode:
authorRob Findley <rfindley@google.com>2020-11-17 11:06:53 -0500
committerRobert Findley <rfindley@google.com>2020-11-24 19:44:06 +0000
commitb56762129e97b15587c15d85e18e2d719528657a (patch)
tree28826fbe7555078cb87695142a13883d12858cc1 /src/go/parser/parser.go
parenta324aebb7ddc38c8d52165df4db75bf7ea63480e (diff)
downloadgo-b56762129e97b15587c15d85e18e2d719528657a.tar.gz
go-b56762129e97b15587c15d85e18e2d719528657a.zip
[dev.typeparams] import go2go changes to parse type parameters
This CL imports changes on the go2go branch to support parsing type params, as well as the unsubmitted changes from CL 269300 to remove support for parenthesize type parameter syntax. Change-Id: I27ab942ce69eab62c2a1800f8f9661c4dcb233fe Reviewed-on: https://go-review.googlesource.com/c/go/+/270857 Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org> Trust: Robert Griesemer <gri@golang.org> Trust: Robert Findley <rfindley@google.com>
Diffstat (limited to 'src/go/parser/parser.go')
-rw-r--r--src/go/parser/parser.go764
1 files changed, 535 insertions, 229 deletions
diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go
index 31a73985bf..6d92373c33 100644
--- a/src/go/parser/parser.go
+++ b/src/go/parser/parser.go
@@ -34,7 +34,7 @@ type parser struct {
// Tracing/debugging
mode Mode // parsing mode
- trace bool // == (mode & Trace != 0)
+ trace bool // == (mode&Trace != 0)
indent int // indentation used for tracing output
// Comments
@@ -181,7 +181,7 @@ func (p *parser) tryResolve(x ast.Expr, collectUnresolved bool) {
if ident == nil {
return
}
- assert(ident.Obj == nil, "identifier already declared or resolved")
+ assert(ident.Obj == nil, fmt.Sprintf("identifier %s already declared or resolved", ident.Name))
if ident.Name == "_" {
return
}
@@ -352,6 +352,10 @@ func (p *parser) next() {
type bailout struct{}
func (p *parser) error(pos token.Pos, msg string) {
+ if p.trace {
+ defer un(trace(p, "error: "+msg))
+ }
+
epos := p.file.Position(pos)
// If AllErrors is not set, discard errors reported on the same line
@@ -594,9 +598,7 @@ func (p *parser) parseLhsList() []ast.Expr {
switch p.tok {
case token.DEFINE:
// lhs of a short variable declaration
- // but doesn't enter scope until later:
- // caller must call p.shortVarDecl(p.makeIdentList(list))
- // at appropriate time.
+ // but doesn't enter scope until later.
case token.COLON:
// lhs of a label declaration or a communication clause of a select
// statement (parseLhsList is not called when parsing the case clause
@@ -643,14 +645,29 @@ func (p *parser) parseType() ast.Expr {
return typ
}
+func (p *parser) parseQualifiedIdent(ident *ast.Ident) ast.Expr {
+ if p.trace {
+ defer un(trace(p, "QualifiedIdent"))
+ }
+
+ typ := p.parseTypeName(ident)
+ if p.tok == token.LBRACK {
+ typ = p.parseTypeInstance(typ)
+ }
+
+ return typ
+}
+
// If the result is an identifier, it is not resolved.
-func (p *parser) parseTypeName() ast.Expr {
+func (p *parser) parseTypeName(ident *ast.Ident) ast.Expr {
if p.trace {
defer un(trace(p, "TypeName"))
}
- ident := p.parseIdent()
- // don't resolve ident yet - it may be a parameter or field name
+ if ident == nil {
+ ident = p.parseIdent()
+ // don't resolve ident yet - it may be a parameter or field name
+ }
if p.tok == token.PERIOD {
// ident is a package name
@@ -663,12 +680,11 @@ func (p *parser) parseTypeName() ast.Expr {
return ident
}
-func (p *parser) parseArrayType() ast.Expr {
+func (p *parser) parseArrayLen() ast.Expr {
if p.trace {
- defer un(trace(p, "ArrayType"))
+ defer un(trace(p, "ArrayLen"))
}
- lbrack := p.expect(token.LBRACK)
p.exprLev++
var len ast.Expr
// always permit ellipsis for more fault-tolerant parsing
@@ -679,26 +695,47 @@ func (p *parser) parseArrayType() ast.Expr {
len = p.parseRhs()
}
p.exprLev--
- p.expect(token.RBRACK)
- elt := p.parseType()
- return &ast.ArrayType{Lbrack: lbrack, Len: len, Elt: elt}
+ return len
}
-func (p *parser) makeIdentList(list []ast.Expr) []*ast.Ident {
- idents := make([]*ast.Ident, len(list))
- for i, x := range list {
- ident, isIdent := x.(*ast.Ident)
- if !isIdent {
- if _, isBad := x.(*ast.BadExpr); !isBad {
- // only report error if it's a new one
- p.errorExpected(x.Pos(), "identifier")
- }
- ident = &ast.Ident{NamePos: x.Pos(), Name: "_"}
+func (p *parser) parseArrayFieldOrTypeInstance(x *ast.Ident) (*ast.Ident, ast.Expr) {
+ if p.trace {
+ defer un(trace(p, "ArrayFieldOrTypeInstance"))
+ }
+
+ // TODO(gri) Should we allow a trailing comma in a type argument
+ // list such as T[P,]? (We do in parseTypeInstance).
+ lbrack := p.expect(token.LBRACK)
+ var args []ast.Expr
+ if p.tok != token.RBRACK {
+ p.exprLev++
+ args = append(args, p.parseRhsOrType())
+ for p.tok == token.COMMA {
+ p.next()
+ args = append(args, p.parseRhsOrType())
}
- idents[i] = ident
+ p.exprLev--
+ }
+ rbrack := p.expect(token.RBRACK)
+
+ if len(args) == 0 {
+ // x []E
+ elt := p.parseType()
+ return x, &ast.ArrayType{Lbrack: lbrack, Elt: elt}
}
- return idents
+
+ // x [P]E or x[P]
+ if len(args) == 1 {
+ elt := p.tryType()
+ if elt != nil {
+ // x [P]E
+ return x, &ast.ArrayType{Lbrack: lbrack, Len: args[0], Elt: elt}
+ }
+ }
+
+ // x[P], x[P1, P2], ...
+ return nil, &ast.CallExpr{Fun: x, Lparen: lbrack, Args: args, Rparen: rbrack, Brackets: true}
}
func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
@@ -708,37 +745,44 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
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
+ var names []*ast.Ident
+ var typ ast.Expr
+ if p.tok == token.IDENT {
+ name := p.parseIdent()
+ if p.tok == token.PERIOD || p.tok == token.STRING || p.tok == token.SEMICOLON || p.tok == token.RBRACE {
+ // embedded type
+ typ = name
+ if p.tok == token.PERIOD {
+ typ = p.parseQualifiedIdent(name)
+ } else {
+ p.resolve(typ)
+ }
+ } else {
+ // name1, name2, ... T
+ names = []*ast.Ident{name}
+ for p.tok == token.COMMA {
+ p.next()
+ names = append(names, p.parseIdent())
+ }
+ // Careful dance: We don't know if we have an embedded instantiated
+ // type T[P1, P2, ...] or a field T of array type []E or [P]E.
+ if len(names) == 1 && p.tok == token.LBRACK {
+ name, typ = p.parseArrayFieldOrTypeInstance(name)
+ if name == nil {
+ names = nil
+ }
+ } else {
+ // T P
+ typ = p.parseType()
+ }
}
- 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())}
- }
+ // embedded, possibly generic type
+ // (using the enclosing parentheses to distinguish it from a named field declaration)
+ // TODO(gri) confirm that this doesn't allow parenthesized embedded type
+ typ = p.parseType()
}
- // Tag
var tag *ast.BasicLit
if p.tok == token.STRING {
tag = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}
@@ -747,10 +791,8 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
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)
-
+ field := &ast.Field{Doc: doc, Names: names, Type: typ, Tag: tag, Comment: p.lineComment}
+ p.declare(field, nil, scope, ast.Var, names...)
return field
}
@@ -792,107 +834,222 @@ func (p *parser) parsePointerType() *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() // 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}
+func (p *parser) parseDotsType() *ast.Ellipsis {
+ if p.trace {
+ defer un(trace(p, "DotsType"))
}
- return p.tryIdentOrType()
+
+ pos := p.expect(token.ELLIPSIS)
+ elt := p.parseType()
+
+ return &ast.Ellipsis{Ellipsis: pos, Elt: elt}
}
-// 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}
+type field struct {
+ name *ast.Ident
+ typ ast.Expr
+}
+
+func (p *parser) parseParamDecl(name *ast.Ident) (f field) {
+ if p.trace {
+ defer un(trace(p, "ParamDeclOrNil"))
}
- return typ
+
+ ptok := p.tok
+ if name != nil {
+ p.tok = token.IDENT // force token.IDENT case in switch below
+ }
+
+ switch p.tok {
+ case token.IDENT:
+ if name != nil {
+ f.name = name
+ p.tok = ptok
+ } else {
+ f.name = p.parseIdent()
+ }
+ switch p.tok {
+ case token.IDENT, token.MUL, token.ARROW, token.FUNC, token.CHAN, token.MAP, token.STRUCT, token.INTERFACE, token.LPAREN:
+ // name type
+ f.typ = p.parseType()
+
+ case token.LBRACK:
+ // name[type1, type2, ...] or name []type or name [len]type
+ f.name, f.typ = p.parseArrayFieldOrTypeInstance(f.name)
+
+ case token.ELLIPSIS:
+ // name ...type
+ f.typ = p.parseDotsType()
+
+ case token.PERIOD:
+ // qualified.typename
+ f.typ = p.parseQualifiedIdent(f.name)
+ f.name = nil
+ }
+
+ case token.MUL, token.ARROW, token.FUNC, token.LBRACK, token.CHAN, token.MAP, token.STRUCT, token.INTERFACE, token.LPAREN:
+ // type
+ f.typ = p.parseType()
+
+ case token.ELLIPSIS:
+ // ...type
+ // (always accepted)
+ f.typ = p.parseDotsType()
+
+ default:
+ p.errorExpected(p.pos, ")")
+ p.advance(exprEnd)
+ }
+
+ return
}
-func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params []*ast.Field) {
+func (p *parser) parseParameterList(scope *ast.Scope, name0 *ast.Ident, closing token.Token, parseParamDecl func(*ast.Ident) field, tparams 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
+ pos := p.pos
+ if name0 != nil {
+ pos = name0.Pos()
+ }
+
+ var list []field
+ var named int // number of parameters that have an explicit name and type
+
+ for name0 != nil || p.tok != closing && p.tok != token.EOF {
+ par := parseParamDecl(name0)
+ name0 = nil // 1st name was consumed if present
+ if par.name != nil || par.typ != nil {
+ list = append(list, par)
+ if par.name != nil && par.typ != nil {
+ named++
+ }
}
- p.next()
- if p.tok == token.RPAREN {
+ if !p.atComma("parameter list", closing) {
break
}
+ p.next()
}
- // 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
+ if len(list) == 0 {
+ return // not uncommon
+ }
+
+ // TODO(gri) parameter distribution and conversion to []*ast.Field
+ // can be combined and made more efficient
+
+ // distribute parameter types
+ if named == 0 {
+ // all unnamed => found names are type names
+ for i := 0; i < len(list); i++ {
+ par := &list[i]
+ if typ := par.name; typ != nil {
+ p.resolve(typ)
+ par.typ = typ
+ par.name = nil
+ }
}
- 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
+ if tparams {
+ p.error(pos, "all type parameters must be named")
+ }
+ } else if named != len(list) {
+ // some named => all must be named
+ ok := true
+ var typ ast.Expr
+ for i := len(list) - 1; i >= 0; i-- {
+ if par := &list[i]; par.typ != nil {
+ typ = par.typ
+ if par.name == nil {
+ ok = false
+ n := ast.NewIdent("_")
+ n.NamePos = typ.Pos() // correct position
+ par.name = n
+ }
+ } else if typ != nil {
+ par.typ = typ
+ } else {
+ // par.typ == nil && typ == nil => we only have a par.name
+ ok = false
+ par.typ = &ast.BadExpr{From: par.name.Pos(), To: p.pos}
+ }
+ }
+ if !ok {
+ if tparams {
+ p.error(pos, "all type parameters must be named")
+ } else {
+ p.error(pos, "mixed named and unnamed parameters")
}
- p.next()
+ }
+ }
+
+ // convert list []*ast.Field
+ if named == 0 {
+ // parameter list consists of types only
+ for _, par := range list {
+ assert(par.typ != nil, "nil type in unnamed parameter list")
+ params = append(params, &ast.Field{Type: par.typ})
}
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}
+ // parameter list consists of named parameters with types
+ var names []*ast.Ident
+ var typ ast.Expr
+ addParams := func() {
+ assert(typ != nil, "nil type in named parameter list")
+ field := &ast.Field{Names: names, Type: typ}
+ // 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, names...)
+ params = append(params, field)
+ names = nil
+ }
+ for _, par := range list {
+ if par.typ != typ {
+ if len(names) > 0 {
+ addParams()
+ }
+ typ = par.typ
+ }
+ names = append(names, par.name)
+ }
+ if len(names) > 0 {
+ addParams()
}
return
}
-func (p *parser) parseParameters(scope *ast.Scope, ellipsisOk bool) *ast.FieldList {
+func (p *parser) parseParameters(scope *ast.Scope, acceptTParams bool) (tparams, params *ast.FieldList) {
if p.trace {
defer un(trace(p, "Parameters"))
}
- var params []*ast.Field
- lparen := p.expect(token.LPAREN)
+ if acceptTParams && p.tok == token.LBRACK {
+ opening := p.pos
+ p.next()
+ // [T any](params) syntax
+ list := p.parseParameterList(scope, nil, token.RBRACK, p.parseParamDecl, true)
+ rbrack := p.expect(token.RBRACK)
+ tparams = &ast.FieldList{Opening: opening, List: list, Closing: rbrack}
+ // Type parameter lists must not be empty.
+ if tparams != nil && tparams.NumFields() == 0 {
+ p.error(tparams.Closing, "empty type parameter list")
+ tparams = nil // avoid follow-on errors
+ }
+ }
+
+ opening := p.expect(token.LPAREN)
+
+ var fields []*ast.Field
if p.tok != token.RPAREN {
- params = p.parseParameterList(scope, ellipsisOk)
+ fields = p.parseParameterList(scope, nil, token.RPAREN, p.parseParamDecl, false)
}
+
rparen := p.expect(token.RPAREN)
+ params = &ast.FieldList{Opening: opening, List: fields, Closing: rparen}
- return &ast.FieldList{Opening: lparen, List: params, Closing: rparen}
+ return
}
func (p *parser) parseResult(scope *ast.Scope) *ast.FieldList {
@@ -901,7 +1058,8 @@ func (p *parser) parseResult(scope *ast.Scope) *ast.FieldList {
}
if p.tok == token.LPAREN {
- return p.parseParameters(scope, false)
+ _, results := p.parseParameters(scope, false)
+ return results
}
typ := p.tryType()
@@ -914,17 +1072,6 @@ func (p *parser) parseResult(scope *ast.Scope) *ast.FieldList {
return nil
}
-func (p *parser) parseSignature(scope *ast.Scope) (params, results *ast.FieldList) {
- if p.trace {
- defer un(trace(p, "Signature"))
- }
-
- params = p.parseParameters(scope, true)
- results = p.parseResult(scope)
-
- return
-}
-
func (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) {
if p.trace {
defer un(trace(p, "FuncType"))
@@ -932,7 +1079,11 @@ func (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) {
pos := p.expect(token.FUNC)
scope := ast.NewScope(p.topScope) // function scope
- params, results := p.parseSignature(scope)
+ tparams, params := p.parseParameters(scope, true)
+ if tparams != nil {
+ p.error(tparams.Pos(), "function type cannot have type parameters")
+ }
+ results := p.parseResult(scope)
return &ast.FuncType{Func: pos, Params: params, Results: results}, scope
}
@@ -945,17 +1096,65 @@ func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field {
doc := p.leadComment
var idents []*ast.Ident
var typ ast.Expr
- x := p.parseTypeName()
- if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN {
- // method
- idents = []*ast.Ident{ident}
- scope := ast.NewScope(nil) // method scope
- params, results := p.parseSignature(scope)
- typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results}
+ x := p.parseTypeName(nil)
+ if ident, _ := x.(*ast.Ident); ident != nil {
+ switch p.tok {
+ case token.LBRACK:
+ // generic method or embedded instantiated type
+ lbrack := p.pos
+ p.next()
+ p.exprLev++
+ x := p.parseExpr(true) // we don't know yet if we're a lhs or rhs expr
+ p.exprLev--
+ if name0, _ := x.(*ast.Ident); name0 != nil && p.tok != token.COMMA && p.tok != token.RBRACK {
+ // generic method m[T any]
+ scope := ast.NewScope(nil) // method scope
+ list := p.parseParameterList(scope, name0, token.RBRACK, p.parseParamDecl, true)
+ rbrack := p.expect(token.RBRACK)
+ tparams := &ast.FieldList{Opening: lbrack, List: list, Closing: rbrack}
+ // TODO(rfindley) refactor to share code with parseFuncType.
+ _, params := p.parseParameters(scope, false)
+ results := p.parseResult(scope)
+ idents = []*ast.Ident{ident}
+ typ = &ast.FuncType{Func: token.NoPos, TParams: tparams, Params: params, Results: results}
+ } else {
+ // embedded instantiated type
+ // TODO(rfindley) should resolve all identifiers in x.
+ list := []ast.Expr{x}
+ if p.atComma("type argument list", token.RBRACK) {
+ p.exprLev++
+ for p.tok != token.RBRACK && p.tok != token.EOF {
+ list = append(list, p.parseType())
+ if !p.atComma("type argument list", token.RBRACK) {
+ break
+ }
+ p.next()
+ }
+ p.exprLev--
+ }
+ rbrack := p.expectClosing(token.RBRACK, "type argument list")
+ typ = &ast.CallExpr{Fun: ident, Lparen: lbrack, Args: list, Rparen: rbrack, Brackets: true}
+ }
+ case token.LPAREN:
+ // ordinary method
+ // TODO(rfindley) refactor to share code with parseFuncType.
+ scope := ast.NewScope(nil) // method scope
+ _, params := p.parseParameters(scope, false)
+ results := p.parseResult(scope)
+ idents = []*ast.Ident{ident}
+ typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results}
+ default:
+ // embedded type
+ typ = x
+ p.resolve(typ)
+ }
} else {
- // embedded interface
+ // embedded, possibly instantiated type
typ = x
- p.resolve(typ)
+ if p.tok == token.LBRACK {
+ // embedded instantiated interface
+ typ = p.parseTypeInstance(typ)
+ }
}
p.expectSemi() // call before accessing p.linecomment
@@ -974,8 +1173,24 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType {
lbrace := p.expect(token.LBRACE)
scope := ast.NewScope(nil) // interface scope
var list []*ast.Field
- for p.tok == token.IDENT {
- list = append(list, p.parseMethodSpec(scope))
+L:
+ for {
+ switch p.tok {
+ case token.IDENT, token.LPAREN:
+ list = append(list, p.parseMethodSpec(scope))
+ case token.TYPE:
+ // all types in a type list share the same field name "type"
+ // (since type is a keyword, a Go program cannot have that field name)
+ name := []*ast.Ident{{NamePos: p.pos, Name: "type"}}
+ p.next()
+ // add each type as a field named "type"
+ for _, typ := range p.parseTypeList() {
+ list = append(list, &ast.Field{Names: name, Type: typ})
+ }
+ p.expectSemi()
+ default:
+ break L
+ }
}
rbrace := p.expect(token.RBRACE)
@@ -1028,13 +1243,44 @@ func (p *parser) parseChanType() *ast.ChanType {
return &ast.ChanType{Begin: pos, Arrow: arrow, Dir: dir, Value: value}
}
+func (p *parser) parseTypeInstance(typ ast.Expr) ast.Expr {
+ if p.trace {
+ defer un(trace(p, "TypeInstance"))
+ }
+
+ opening := p.expect(token.LBRACK)
+
+ p.exprLev++
+ var list []ast.Expr
+ for p.tok != token.RBRACK && p.tok != token.EOF {
+ list = append(list, p.parseType())
+ if !p.atComma("type argument list", token.RBRACK) {
+ break
+ }
+ p.next()
+ }
+ p.exprLev--
+
+ closing := p.expectClosing(token.RBRACK, "type argument list")
+
+ return &ast.CallExpr{Fun: typ, Lparen: opening, Args: list, Rparen: closing, Brackets: true}
+}
+
// If the result is an identifier, it is not resolved.
func (p *parser) tryIdentOrType() ast.Expr {
switch p.tok {
case token.IDENT:
- return p.parseTypeName()
+ typ := p.parseTypeName(nil)
+ if p.tok == token.LBRACK {
+ typ = p.parseTypeInstance(typ)
+ }
+ return typ
case token.LBRACK:
- return p.parseArrayType()
+ lbrack := p.expect(token.LBRACK)
+ alen := p.parseArrayLen()
+ p.expect(token.RBRACK)
+ elt := p.parseType()
+ return &ast.ArrayType{Lbrack: lbrack, Len: alen, Elt: elt}
case token.STRUCT:
return p.parseStructType()
case token.MUL:
@@ -1169,7 +1415,7 @@ func (p *parser) parseOperand(lhs bool) ast.Expr {
return p.parseFuncTypeOrLit()
}
- if typ := p.tryIdentOrType(); typ != nil {
+ if typ := p.tryIdentOrType(); typ != nil { // do not consume trailing type parameters
// could be type for composite literal or conversion
_, isIdent := typ.(*ast.Ident)
assert(!isIdent, "type cannot be identifier")
@@ -1211,28 +1457,53 @@ func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr {
return &ast.TypeAssertExpr{X: x, Type: typ, Lparen: lparen, Rparen: rparen}
}
-func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {
+func (p *parser) parseIndexOrSliceOrInstance(x ast.Expr) ast.Expr {
if p.trace {
- defer un(trace(p, "IndexOrSlice"))
+ defer un(trace(p, "parseIndexOrSliceOrInstance"))
}
- const N = 3 // change the 3 to 2 to disable 3-index slices
lbrack := p.expect(token.LBRACK)
+ if p.tok == token.RBRACK {
+ // empty index, slice or index expressions are not permitted;
+ // accept them for parsing tolerance, but complain
+ p.errorExpected(p.pos, "operand")
+ p.next()
+ return x
+ }
p.exprLev++
+
+ const N = 3 // change the 3 to 2 to disable 3-index slices
+ var args []ast.Expr
var index [N]ast.Expr
var colons [N - 1]token.Pos
if p.tok != token.COLON {
- index[0] = p.parseRhs()
+ // We can't know if we have an index expression or a type instantiation;
+ // so even if we see a (named) type we are not going to be in type context.
+ index[0] = p.parseRhsOrType()
}
ncolons := 0
- for p.tok == token.COLON && ncolons < len(colons) {
- colons[ncolons] = p.pos
- ncolons++
- p.next()
- if p.tok != token.COLON && p.tok != token.RBRACK && p.tok != token.EOF {
- index[ncolons] = p.parseRhs()
+ switch p.tok {
+ case token.COLON:
+ // slice expression
+ for p.tok == token.COLON && ncolons < len(colons) {
+ colons[ncolons] = p.pos
+ ncolons++
+ p.next()
+ if p.tok != token.COLON && p.tok != token.RBRACK && p.tok != token.EOF {
+ index[ncolons] = p.parseRhs()
+ }
+ }
+ case token.COMMA:
+ // instance expression
+ args = append(args, index[0])
+ for p.tok == token.COMMA {
+ p.next()
+ if p.tok != token.RBRACK && p.tok != token.EOF {
+ args = append(args, p.parseType())
+ }
}
}
+
p.exprLev--
rbrack := p.expect(token.RBRACK)
@@ -1255,7 +1526,13 @@ func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {
return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: index[0], High: index[1], Max: index[2], Slice3: slice3, Rbrack: rbrack}
}
- return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack}
+ if len(args) == 0 {
+ // index expression
+ return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack}
+ }
+
+ // instance expression
+ return &ast.CallExpr{Fun: x, Lparen: lbrack, Args: args, Rparen: rbrack, Brackets: true}
}
func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
@@ -1404,45 +1681,6 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr {
return x
}
-// isTypeName reports whether x is a (qualified) TypeName.
-func isTypeName(x ast.Expr) bool {
- switch t := x.(type) {
- case *ast.BadExpr:
- case *ast.Ident:
- case *ast.SelectorExpr:
- _, isIdent := t.X.(*ast.Ident)
- return isIdent
- default:
- return false // all other nodes are not type names
- }
- return true
-}
-
-// isLiteralType reports whether x is a legal composite literal type.
-func isLiteralType(x ast.Expr) bool {
- switch t := x.(type) {
- case *ast.BadExpr:
- case *ast.Ident:
- case *ast.SelectorExpr:
- _, isIdent := t.X.(*ast.Ident)
- return isIdent
- case *ast.ArrayType:
- case *ast.StructType:
- case *ast.MapType:
- default:
- return false // all other nodes are not legal composite literal types
- }
- return true
-}
-
-// If x is of the form *T, deref returns T, otherwise it returns x.
-func deref(x ast.Expr) ast.Expr {
- if p, isPtr := x.(*ast.StarExpr); isPtr {
- x = p.X
- }
- return x
-}
-
// If x is of the form (T), unparen returns unparen(T), otherwise it returns x.
func unparen(x ast.Expr) ast.Expr {
if p, isParen := x.(*ast.ParenExpr); isParen {
@@ -1470,13 +1708,12 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
}
// If lhs is set and the result is an identifier, it is not resolved.
-func (p *parser) parsePrimaryExpr(lhs bool) ast.Expr {
+func (p *parser) parsePrimaryExpr(lhs bool) (x ast.Expr) {
if p.trace {
defer un(trace(p, "PrimaryExpr"))
}
- x := p.parseOperand(lhs)
-L:
+ x = p.parseOperand(lhs)
for {
switch p.tok {
case token.PERIOD:
@@ -1500,28 +1737,48 @@ L:
if lhs {
p.resolve(x)
}
- x = p.parseIndexOrSlice(p.checkExpr(x))
+ x = p.parseIndexOrSliceOrInstance(p.checkExpr(x))
case token.LPAREN:
if lhs {
p.resolve(x)
}
x = p.parseCallOrConversion(p.checkExprOrType(x))
case token.LBRACE:
- if isLiteralType(x) && (p.exprLev >= 0 || !isTypeName(x)) {
- if lhs {
- p.resolve(x)
+ // operand may have returned a parenthesized complit
+ // type; accept it but complain if we have a complit
+ t := unparen(x)
+ // determine if '{' belongs to a composite literal or a block statement
+ switch t := t.(type) {
+ case *ast.BadExpr, *ast.Ident, *ast.SelectorExpr:
+ if p.exprLev < 0 {
+ return
}
- x = p.parseLiteralValue(x)
- } else {
- break L
+ // x is possibly a composite literal type
+ case *ast.CallExpr:
+ if !t.Brackets || p.exprLev < 0 {
+ return
+ }
+ // x is possibly a composite literal type
+ case *ast.IndexExpr:
+ if p.exprLev < 0 {
+ return
+ }
+ // x is possibly a composite literal type
+ case *ast.ArrayType, *ast.StructType, *ast.MapType:
+ // x is a composite literal type
+ default:
+ return
+ }
+ if t != x {
+ p.error(t.Pos(), "cannot parenthesize type in composite literal")
+ // already progressed, no need to advance
}
+ x = p.parseLiteralValue(x)
default:
- break L
+ return
}
lhs = false // no need to try to resolve again
}
-
- return x
}
// If lhs is set and the result is an identifier, it is not resolved.
@@ -1846,14 +2103,14 @@ func (p *parser) parseIfHeader() (init ast.Stmt, cond ast.Expr) {
}
// p.tok != token.LBRACE
- outer := p.exprLev
+ prevLev := p.exprLev
p.exprLev = -1
if p.tok != token.SEMICOLON {
// accept potential variable declaration but complain
if p.tok == token.VAR {
p.next()
- p.error(p.pos, fmt.Sprintf("var declaration not allowed in 'IF' initializer"))
+ p.error(p.pos, "var declaration not allowed in 'IF' initializer")
}
init, _ = p.parseSimpleStmt(basic)
}
@@ -1894,7 +2151,7 @@ func (p *parser) parseIfHeader() (init ast.Stmt, cond ast.Expr) {
cond = &ast.BadExpr{From: p.pos, To: p.pos}
}
- p.exprLev = outer
+ p.exprLev = prevLev
return
}
@@ -2275,7 +2532,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"
@@ -2288,7 +2545,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"))
}
@@ -2327,7 +2584,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"))
}
@@ -2374,7 +2631,21 @@ 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) parseGenericType(spec *ast.TypeSpec, openPos token.Pos, name0 *ast.Ident, closeTok token.Token) {
+ p.openScope()
+ list := p.parseParameterList(p.topScope, name0, closeTok, p.parseParamDecl, true)
+ closePos := p.expect(closeTok)
+ spec.TParams = &ast.FieldList{Opening: openPos, List: list, Closing: closePos}
+ // Type alias cannot have type parameters. Accept them for robustness but complain.
+ if p.tok == token.ASSIGN {
+ p.error(p.pos, "generic type cannot be alias")
+ p.next()
+ }
+ spec.Type = p.parseType()
+ p.closeScope()
+}
+
+func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token, _ int) ast.Spec {
if p.trace {
defer un(trace(p, "TypeSpec"))
}
@@ -2387,11 +2658,44 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast.
// (Global identifiers are resolved in a separate phase after parsing.)
spec := &ast.TypeSpec{Doc: doc, Name: ident}
p.declare(spec, nil, p.topScope, ast.Typ, ident)
- if p.tok == token.ASSIGN {
- spec.Assign = p.pos
+
+ switch p.tok {
+ case token.LBRACK:
+ lbrack := p.pos
p.next()
+ if p.tok == token.IDENT {
+ // array type or generic type [T any]
+ p.exprLev++
+ x := p.parseExpr(true) // we don't know yet if we're a lhs or rhs expr
+ p.exprLev--
+ if name0, _ := x.(*ast.Ident); name0 != nil && p.tok != token.RBRACK {
+ // generic type [T any];
+ p.parseGenericType(spec, lbrack, name0, token.RBRACK)
+ } else {
+ // array type
+ // TODO(rfindley) should resolve all identifiers in x.
+ p.expect(token.RBRACK)
+ elt := p.parseType()
+ spec.Type = &ast.ArrayType{Lbrack: lbrack, Len: x, Elt: elt}
+ }
+ } else {
+ // array type
+ alen := p.parseArrayLen()
+ p.expect(token.RBRACK)
+ elt := p.parseType()
+ spec.Type = &ast.ArrayType{Lbrack: lbrack, Len: alen, Elt: elt}
+ }
+
+ default:
+ // no type parameters
+ if p.tok == token.ASSIGN {
+ // type alias
+ spec.Assign = p.pos
+ p.next()
+ }
+ spec.Type = p.parseType()
}
- spec.Type = p.parseType()
+
p.expectSemi() // call before accessing p.linecomment
spec.Comment = p.lineComment
@@ -2411,12 +2715,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{
@@ -2440,12 +2744,13 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
var recv *ast.FieldList
if p.tok == token.LPAREN {
- recv = p.parseParameters(scope, false)
+ _, recv = p.parseParameters(scope, false)
}
ident := p.parseIdent()
- params, results := p.parseSignature(scope)
+ tparams, params := p.parseParameters(scope, true)
+ results := p.parseResult(scope)
var body *ast.BlockStmt
if p.tok == token.LBRACE {
@@ -2469,6 +2774,7 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
Name: ident,
Type: &ast.FuncType{
Func: pos,
+ TParams: tparams,
Params: params,
Results: results,
},