Skip to content

Commit 33a1a93

Browse files
committed
cmd/compile/internal/syntax: fix parsing of type parameter lists
The parser cannot distinguish a type parameter list of the form [P *T ] or [P (T)] where T is not a type literal from an array length specification P*T (product) or P(T) (constant-valued function call) and thus interprets these forms as the start of array types. This ambiguity must be resolved explicitly by placing *T inside an interface, adding a trailing comma, or by leaving parentheses away where possible. This CL adjusts the parser such that these forms are interpreted as (the beginning) of type parameter lists if the token after P*T or P(T) is a comma, or if T is a type literal. This CL also adjusts the printer to print a comma if necessary to avoid this ambiguity, and adds additional printer tests. Fixes #49482 Change-Id: I36328e2a7d9439c39ba0349837c445542549e84e Reviewed-on: https://go-review.googlesource.com/c/go/+/370774 Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Findley <rfindley@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
1 parent 0c24038 commit 33a1a93

File tree

4 files changed

+227
-85
lines changed

4 files changed

+227
-85
lines changed

src/cmd/compile/internal/syntax/parser.go

+116-33
Original file line numberDiff line numberDiff line change
@@ -588,44 +588,81 @@ func (p *parser) typeDecl(group *Group) Decl {
588588
d.Name = p.name()
589589
if p.allowGenerics() && p.tok == _Lbrack {
590590
// d.Name "[" ...
591-
// array/slice or type parameter list
591+
// array/slice type or type parameter list
592592
pos := p.pos()
593593
p.next()
594594
switch p.tok {
595595
case _Name:
596-
// d.Name "[" name ...
597-
// array or type parameter list
598-
name := p.name()
599-
// Index or slice expressions are never constant and thus invalid
600-
// array length expressions. Thus, if we see a "[" following name
601-
// we can safely assume that "[" name starts a type parameter list.
602-
var x Expr // x != nil means x is the array length expression
596+
// We may have an array type or a type parameter list.
597+
// In either case we expect an expression x (which may
598+
// just be a name, or a more complex expression) which
599+
// we can analyze further.
600+
//
601+
// A type parameter list may have a type bound starting
602+
// with a "[" as in: P []E. In that case, simply parsing
603+
// an expression would lead to an error: P[] is invalid.
604+
// But since index or slice expressions are never constant
605+
// and thus invalid array length expressions, if we see a
606+
// "[" following a name it must be the start of an array
607+
// or slice constraint. Only if we don't see a "[" do we
608+
// need to parse a full expression.
609+
var x Expr = p.name()
603610
if p.tok != _Lbrack {
604-
// d.Name "[" name ...
605-
// If we reach here, the next token is not a "[", and we need to
606-
// parse the expression starting with name. If that expression is
607-
// just that name, not followed by a "]" (in which case we might
608-
// have the array length "[" name "]"), we can also safely assume
609-
// a type parameter list.
611+
// To parse the expression starting with name, expand
612+
// the call sequence we would get by passing in name
613+
// to parser.expr, and pass in name to parser.pexpr.
610614
p.xnest++
611-
// To parse the expression starting with name, expand the call
612-
// sequence we would get by passing in name to parser.expr, and
613-
// pass in name to parser.pexpr.
614-
x = p.binaryExpr(p.pexpr(name, false), 0)
615+
x = p.binaryExpr(p.pexpr(x, false), 0)
615616
p.xnest--
616-
if x == name && p.tok != _Rbrack {
617-
x = nil
617+
}
618+
619+
// analyze the cases
620+
var pname *Name // pname != nil means pname is the type parameter name
621+
var ptype Expr // ptype != nil means ptype is the type parameter type; pname != nil in this case
622+
switch t := x.(type) {
623+
case *Name:
624+
// Unless we see a "]", we are at the start of a type parameter list.
625+
if p.tok != _Rbrack {
626+
// d.Name "[" name ...
627+
pname = t
628+
// no ptype
629+
}
630+
case *Operation:
631+
// If we have an expression of the form name*T, and T is a (possibly
632+
// parenthesized) type literal or the next token is a comma, we are
633+
// at the start of a type parameter list.
634+
if name, _ := t.X.(*Name); name != nil {
635+
if t.Op == Mul && (isTypeLit(t.Y) || p.tok == _Comma) {
636+
// d.Name "[" name "*" t.Y
637+
// d.Name "[" name "*" t.Y ","
638+
t.X, t.Y = t.Y, nil // convert t into unary *t.Y
639+
pname = name
640+
ptype = t
641+
}
642+
}
643+
case *CallExpr:
644+
// If we have an expression of the form name(T), and T is a (possibly
645+
// parenthesized) type literal or the next token is a comma, we are
646+
// at the start of a type parameter list.
647+
if name, _ := t.Fun.(*Name); name != nil {
648+
if len(t.ArgList) == 1 && !t.HasDots && (isTypeLit(t.ArgList[0]) || p.tok == _Comma) {
649+
// d.Name "[" name "(" t.ArgList[0] ")"
650+
// d.Name "[" name "(" t.ArgList[0] ")" ","
651+
pname = name
652+
ptype = t.ArgList[0]
653+
}
618654
}
619655
}
620-
if x == nil {
621-
// d.Name "[" name ...
622-
// type parameter list
623-
d.TParamList = p.paramList(name, _Rbrack, true)
656+
657+
if pname != nil {
658+
// d.Name "[" pname ...
659+
// d.Name "[" pname ptype ...
660+
// d.Name "[" pname ptype "," ...
661+
d.TParamList = p.paramList(pname, ptype, _Rbrack, true)
624662
d.Alias = p.gotAssign()
625663
d.Type = p.typeOrNil()
626664
} else {
627-
// d.Name "[" x "]" ...
628-
// x is the array length expression
665+
// d.Name "[" x ...
629666
d.Type = p.arrayType(pos, x)
630667
}
631668
case _Rbrack:
@@ -650,6 +687,21 @@ func (p *parser) typeDecl(group *Group) Decl {
650687
return d
651688
}
652689

690+
// isTypeLit reports whether x is a (possibly parenthesized) type literal.
691+
func isTypeLit(x Expr) bool {
692+
switch x := x.(type) {
693+
case *ArrayType, *StructType, *FuncType, *InterfaceType, *SliceType, *MapType, *ChanType:
694+
return true
695+
case *Operation:
696+
// *T may be a pointer dereferenciation.
697+
// Only consider *T as type literal if T is a type literal.
698+
return x.Op == Mul && x.Y == nil && isTypeLit(x.X)
699+
case *ParenExpr:
700+
return isTypeLit(x.X)
701+
}
702+
return false
703+
}
704+
653705
// VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
654706
func (p *parser) varDecl(group *Group) Decl {
655707
if trace {
@@ -689,7 +741,7 @@ func (p *parser) funcDeclOrNil() *FuncDecl {
689741
f.Pragma = p.takePragma()
690742

691743
if p.got(_Lparen) {
692-
rcvr := p.paramList(nil, _Rparen, false)
744+
rcvr := p.paramList(nil, nil, _Rparen, false)
693745
switch len(rcvr) {
694746
case 0:
695747
p.error("method has no receiver")
@@ -1369,12 +1421,12 @@ func (p *parser) funcType(context string) ([]*Field, *FuncType) {
13691421
p.syntaxError("empty type parameter list")
13701422
p.next()
13711423
} else {
1372-
tparamList = p.paramList(nil, _Rbrack, true)
1424+
tparamList = p.paramList(nil, nil, _Rbrack, true)
13731425
}
13741426
}
13751427

13761428
p.want(_Lparen)
1377-
typ.ParamList = p.paramList(nil, _Rparen, false)
1429+
typ.ParamList = p.paramList(nil, nil, _Rparen, false)
13781430
typ.ResultList = p.funcResult()
13791431

13801432
return tparamList, typ
@@ -1392,6 +1444,13 @@ func (p *parser) arrayType(pos Pos, len Expr) Expr {
13921444
len = p.expr()
13931445
p.xnest--
13941446
}
1447+
if p.tok == _Comma {
1448+
// Trailing commas are accepted in type parameter
1449+
// lists but not in array type declarations.
1450+
// Accept for better error handling but complain.
1451+
p.syntaxError("unexpected comma; expecting ]")
1452+
p.next()
1453+
}
13951454
p.want(_Rbrack)
13961455
t := new(ArrayType)
13971456
t.pos = pos
@@ -1516,7 +1575,7 @@ func (p *parser) funcResult() []*Field {
15161575
}
15171576

15181577
if p.got(_Lparen) {
1519-
return p.paramList(nil, _Rparen, false)
1578+
return p.paramList(nil, nil, _Rparen, false)
15201579
}
15211580

15221581
pos := p.pos()
@@ -1742,7 +1801,7 @@ func (p *parser) methodDecl() *Field {
17421801

17431802
// A type argument list looks like a parameter list with only
17441803
// types. Parse a parameter list and decide afterwards.
1745-
list := p.paramList(nil, _Rbrack, false)
1804+
list := p.paramList(nil, nil, _Rbrack, false)
17461805
if len(list) == 0 {
17471806
// The type parameter list is not [] but we got nothing
17481807
// due to other errors (reported by paramList). Treat
@@ -1948,17 +2007,41 @@ func (p *parser) paramDeclOrNil(name *Name, follow token) *Field {
19482007
// ParameterList = ParameterDecl { "," ParameterDecl } .
19492008
// "(" or "[" has already been consumed.
19502009
// If name != nil, it is the first name after "(" or "[".
2010+
// If typ != nil, name must be != nil, and (name, typ) is the first field in the list.
19512011
// In the result list, either all fields have a name, or no field has a name.
1952-
func (p *parser) paramList(name *Name, close token, requireNames bool) (list []*Field) {
2012+
func (p *parser) paramList(name *Name, typ Expr, close token, requireNames bool) (list []*Field) {
19532013
if trace {
19542014
defer p.trace("paramList")()
19552015
}
19562016

2017+
// p.list won't invoke its function argument if we're at the end of the
2018+
// parameter list. If we have a complete field, handle this case here.
2019+
if name != nil && typ != nil && p.tok == close {
2020+
p.next()
2021+
par := new(Field)
2022+
par.pos = name.pos
2023+
par.Name = name
2024+
par.Type = typ
2025+
return []*Field{par}
2026+
}
2027+
19572028
var named int // number of parameters that have an explicit name and type
19582029
var typed int // number of parameters that have an explicit type
19592030
end := p.list(_Comma, close, func() bool {
1960-
par := p.paramDeclOrNil(name, close)
2031+
var par *Field
2032+
if typ != nil {
2033+
if debug && name == nil {
2034+
panic("initial type provided without name")
2035+
}
2036+
par = new(Field)
2037+
par.pos = name.pos
2038+
par.Name = name
2039+
par.Type = typ
2040+
} else {
2041+
par = p.paramDeclOrNil(name, close)
2042+
}
19612043
name = nil // 1st name was consumed if present
2044+
typ = nil // 1st type was consumed if present
19622045
if par != nil {
19632046
if debug && par.Name == nil && par.Type == nil {
19642047
panic("parameter without name or type")

src/cmd/compile/internal/syntax/printer.go

+30-25
Original file line numberDiff line numberDiff line change
@@ -666,9 +666,7 @@ func (p *printer) printRawNode(n Node) {
666666
}
667667
p.print(n.Name)
668668
if n.TParamList != nil {
669-
p.print(_Lbrack)
670-
p.printFieldList(n.TParamList, nil, _Comma)
671-
p.print(_Rbrack)
669+
p.printParameterList(n.TParamList, true)
672670
}
673671
p.print(blank)
674672
if n.Alias {
@@ -700,9 +698,7 @@ func (p *printer) printRawNode(n Node) {
700698
}
701699
p.print(n.Name)
702700
if n.TParamList != nil {
703-
p.print(_Lbrack)
704-
p.printFieldList(n.TParamList, nil, _Comma)
705-
p.print(_Rbrack)
701+
p.printParameterList(n.TParamList, true)
706702
}
707703
p.printSignature(n.Type)
708704
if n.Body != nil {
@@ -887,38 +883,47 @@ func (p *printer) printDeclList(list []Decl) {
887883
}
888884

889885
func (p *printer) printSignature(sig *FuncType) {
890-
p.printParameterList(sig.ParamList)
886+
p.printParameterList(sig.ParamList, false)
891887
if list := sig.ResultList; list != nil {
892888
p.print(blank)
893889
if len(list) == 1 && list[0].Name == nil {
894890
p.printNode(list[0].Type)
895891
} else {
896-
p.printParameterList(list)
892+
p.printParameterList(list, false)
897893
}
898894
}
899895
}
900896

901-
func (p *printer) printParameterList(list []*Field) {
902-
p.print(_Lparen)
903-
if len(list) > 0 {
904-
for i, f := range list {
905-
if i > 0 {
906-
p.print(_Comma, blank)
907-
}
908-
if f.Name != nil {
909-
p.printNode(f.Name)
910-
if i+1 < len(list) {
911-
f1 := list[i+1]
912-
if f1.Name != nil && f1.Type == f.Type {
913-
continue // no need to print type
914-
}
897+
func (p *printer) printParameterList(list []*Field, types bool) {
898+
open, close := _Lparen, _Rparen
899+
if types {
900+
open, close = _Lbrack, _Rbrack
901+
}
902+
p.print(open)
903+
for i, f := range list {
904+
if i > 0 {
905+
p.print(_Comma, blank)
906+
}
907+
if f.Name != nil {
908+
p.printNode(f.Name)
909+
if i+1 < len(list) {
910+
f1 := list[i+1]
911+
if f1.Name != nil && f1.Type == f.Type {
912+
continue // no need to print type
915913
}
916-
p.print(blank)
917914
}
918-
p.printNode(f.Type)
915+
p.print(blank)
916+
}
917+
p.printNode(unparen(f.Type)) // no need for (extra) parentheses around parameter types
918+
}
919+
// A type parameter list [P *T] where T is not a type literal requires a comma as in [P *T,]
920+
// so that it's not parsed as [P*T].
921+
if types && len(list) == 1 {
922+
if t, _ := list[0].Type.(*Operation); t != nil && t.Op == Mul && t.Y == nil && !isTypeLit(t.X) {
923+
p.print(_Comma)
919924
}
920925
}
921-
p.print(_Rparen)
926+
p.print(close)
922927
}
923928

924929
func (p *printer) printStmtList(list []Stmt, braces bool) {

0 commit comments

Comments
 (0)