// Tracing/debugging
mode Mode // parsing mode
- trace bool // == (mode & Trace != 0)
+ trace bool // == (mode&Trace != 0)
indent int // indentation used for tracing output
// Comments
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
}
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
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
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
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
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 {
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}
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
}
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 {
}
if p.tok == token.LPAREN {
- return p.parseParameters(scope, false)
+ _, results := p.parseParameters(scope, false)
+ return results
}
typ := p.tryType()
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"))
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
}
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
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)
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:
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")
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)
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 {
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 {
}
// 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:
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.
}
// 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)
}
cond = &ast.BadExpr{From: p.pos, To: p.pos}
}
- p.exprLev = outer
+ p.exprLev = prevLev
return
}
// ----------------------------------------------------------------------------
// 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"
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"))
}
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"))
}
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"))
}
// (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
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{
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 {
Name: ident,
Type: &ast.FuncType{
Func: pos,
+ TParams: tparams,
Params: params,
Results: results,
},
`package p; var _ = map[*P]int{&P{}:0, {}:1}`,
`package p; type T = int`,
`package p; type (T = p.T; _ = struct{}; x = *T)`,
+ `package p; type T (*int)`,
+
+ // structs with parameterized embedded fields (for symmetry with interfaces)
+ `package p; type _ struct{ ((int)) }`,
+ `package p; type _ struct{ (*(int)) }`,
+ `package p; type _ struct{ ([]byte) }`, // disallowed by type-checker
+
+ // type parameters
+ `package p; type T[P any] struct { P }`,
+ `package p; type T[P comparable] struct { P }`,
+ `package p; type T[P comparable[P]] struct { P }`,
+ `package p; type T[P1, P2 any] struct { P1; f []P2 }`,
+ `package p; type _ []T[int]`,
+
+ `package p; var _ = func()T(nil)`,
+ `package p; func _[T any]()`,
+ `package p; func _[T any]()()`,
+ `package p; func _(T (P))`,
+ `package p; func _(T []E)`,
+ `package p; func _(T [P]E)`,
+ `package p; func _(x T[P1, P2, P3])`,
+ `package p; func _(x p.T[Q])`,
+ `package p; func _(p.T[Q])`,
+
+ `package p; var _ T[chan int]`,
+ `package p; func f[A, B any](); func _() { _ = f[int, int] }`,
+
+ `package p; type _[A interface{},] struct{}`,
+ `package p; type _[A interface{}] struct{}`,
+ `package p; type _[A, B any,] struct{}`,
+ `package p; type _[A, B any] struct{}`,
+ `package p; type _[A any,] struct{}`,
+ `package p; type _ [A+B]struct{}`, // this is an array!
+ `package p; type _[A any]struct{}`,
+ `package p; type _[A any] struct{ A }`, // this is not an array!
+
+ `package p; func _[T any]()`,
+ `package p; func _[T any](x T)`,
+ `package p; func _[T1, T2 any](x T)`,
+
+ `package p; func (R) _()`,
+ `package p; func (R[P]) _[T any]()`,
+ `package p; func (_ R[P]) _[T any](x T)`,
+ `package p; func (_ R[P, Q]) _[T1, T2 any](x T)`,
+
+ `package p; var _ = []T[int]{}`,
+ `package p; var _ = [10]T[int]{}`,
+ `package p; var _ = func()T[int]{}`,
+ `package p; var _ = map[T[int]]T[int]{}`,
+ `package p; var _ = chan T[int](x)`,
+ `package p; func _(T[P])`,
+ `package p; func _(T[P1, P2, P3])`,
+ `package p; func _(T[P]) T[P]`,
+ `package p; func _(_ T[P], T P) T[P]`,
+
+ `package p; func _[A, B any](a A) B`,
+ `package p; func _[A, B C](a A) B`,
+ `package p; func _[A, B C[A, B]](a A) B`,
+
+ // method type parameters (if methodTypeParamsOk)
+ `package p; func (T) _[A, B any](a A) B`,
+ `package p; func (T) _[A, B C](a A) B`,
+ `package p; func (T) _[A, B C[A, B]](a A) B`,
+
+ // method type parameters are not permitted in interfaces.
+ `package p; type _[A, B any] interface { _(a A) B }`,
+ `package p; type _[A, B C[A, B]] interface { _(a A) B }`,
+
+ // type bounds
+ `package p; func _[T1, T2 interface{}](x T1) T2`,
+ `package p; func _[T1 interface{ m() }, T2, T3 interface{}](x T1, y T3) T2`,
+
+ // struct embedding
+ `package p; type _ struct{ T[P] }`,
+ `package p; type _ struct{ T[struct{a, b, c int}] }`,
+ `package p; type _ struct{ f [n]E }`,
+ `package p; type _ struct{ f [a+b+c+d]E }`,
+
+ // interfaces with type lists
+ `package p; type _ interface{type int}`,
+ `package p; type _ interface{type int, float32; type bool; m(); type string;}`,
+
+ // interface embedding
+ `package p; type I1 interface{}; type I2 interface{ I1 }`,
+ `package p; type I1[T any] interface{}; type I2 interface{ I1[int] }`,
+ `package p; type I1[T any] interface{}; type I2[T any] interface{ I1[T] }`,
}
func TestValid(t *testing.T) {
for _, src := range valids {
- checkErrors(t, src, src)
+ checkErrors(t, src, src, DeclarationErrors|AllErrors)
}
}
+// TestSingle is useful to track down a problem with a single short test program.
+func TestSingle(t *testing.T) {
+ const src = `package p; var _ = T[P]{}`
+ checkErrors(t, src, src, DeclarationErrors|AllErrors)
+}
+
var invalids = []string{
`foo /* ERROR "expected 'package'" */ !`,
`package p; func f() { if { /* ERROR "missing condition" */ } };`,
`package p; var a = chan /* ERROR "expected expression" */ int;`,
`package p; var a = []int{[ /* ERROR "expected expression" */ ]int};`,
`package p; var a = ( /* ERROR "expected expression" */ []int);`,
- `package p; var a = a[[ /* ERROR "expected expression" */ ]int:[]int];`,
`package p; var a = <- /* ERROR "expected expression" */ chan int;`,
`package p; func f() { select { case _ <- chan /* ERROR "expected expression" */ int: } };`,
`package p; func f() { _ = (<-<- /* ERROR "expected 'chan'" */ chan int)(nil) };`,
`package p; func f() { go f /* ERROR HERE "function must be invoked" */ }`,
`package p; func f() { defer func() {} /* ERROR HERE "function must be invoked" */ }`,
`package p; func f() { go func() { func() { f(x func /* ERROR "missing ','" */ (){}) } } }`,
- `package p; func f(x func(), u v func /* ERROR "missing ','" */ ()){}`,
+ //`package p; func f(x func(), u v func /* ERROR "missing ','" */ ()){}`,
+
+ // type parameters
+ `package p; var _ func[ /* ERROR "cannot have type parameters" */ T any](T)`,
+ `package p; func _() (type /* ERROR "found 'type'" */ T)(T)`,
+ `package p; func (type /* ERROR "found 'type'" */ T)(T) _()`,
+ `package p; type _[A+B, /* ERROR "expected ']'" */ ] int`,
+ `package p; type _[_ any] int; var _ = T[] /* ERROR "expected operand" */ {}`,
+ `package p; type T[P any] = /* ERROR "cannot be alias" */ T0`,
+ `package p; func _[]/* ERROR "empty type parameter list" */()`,
+
+ // errors that could be improved
+ `package p; var a = a[[]int:[ /* ERROR "expected expression" */ ]int];`, // TODO: should this be on the ':'?
+ `package p; type _[A/* ERROR "all type parameters must be named" */,] struct{ A }`, // TODO: a better location would be after the ']'
+ `package p; func _[type /* ERROR "all type parameters must be named" */P, *Q interface{}]()`, // TODO: this is confusing.
+ `package p; type I1 interface{}; type I2 interface{ (/* ERROR "expected 'IDENT'" */I1) }`, // TODO: compiler error is 'syntax error: cannot parenthesize embedded type'
// issue 8656
`package p; func f() (a b string /* ERROR "missing ','" */ , ok bool)`,
`package p; var _ = struct { x int, /* ERROR "expected ';', found ','" */ y float }{};`,
// issue 11611
- `package p; type _ struct { int, } /* ERROR "expected type, found '}'" */ ;`,
+ `package p; type _ struct { int, } /* ERROR "expected 'IDENT', found '}'" */ ;`,
`package p; type _ struct { int, float } /* ERROR "expected type, found '}'" */ ;`,
- `package p; type _ struct { ( /* ERROR "expected anonymous field" */ int) };`,
- `package p; func _()(x, y, z ... /* ERROR "expected '\)', found '...'" */ int){}`,
- `package p; func _()(... /* ERROR "expected type, found '...'" */ int){}`,
+ //`package p; type _ struct { ( /* ERROR "cannot parenthesize embedded type" */ int) };`,
+ //`package p; func _()(x, y, z ... /* ERROR "expected '\)', found '...'" */ int){}`,
+ //`package p; func _()(... /* ERROR "expected type, found '...'" */ int){}`,
// issue 13475
`package p; func f() { if true {} else ; /* ERROR "expected if statement or block" */ }`,
func TestInvalid(t *testing.T) {
for _, src := range invalids {
- checkErrors(t, src, src)
+ checkErrors(t, src, src, DeclarationErrors|AllErrors)
}
}