cmd/compile/internal/types2: record types for union subexpressions

This is a port of CL 371757 from go/types to types2, with
minor adjustments for different error handling and AST.

It also names the added API test cases more consistently.
The same renaming was applied to the respective go/types
file.

Updates #50093

Change-Id: Iaa132106a197a207f831525432e62e9d452b17c9
Reviewed-on: https://go-review.googlesource.com/c/go/+/372475
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer 2021-12-15 11:42:58 -08:00
parent 4cda05d41a
commit 07ed86c57b
4 changed files with 65 additions and 33 deletions

View File

@ -202,12 +202,6 @@ type Info struct {
// identifier z in a variable declaration 'var z int' is found // identifier z in a variable declaration 'var z int' is found
// only in the Defs map, and identifiers denoting packages in // only in the Defs map, and identifiers denoting packages in
// qualified identifiers are collected in the Uses map. // qualified identifiers are collected in the Uses map.
//
// For binary expressions representing unions in constraint
// position or type elements in interfaces, a union type is
// recorded for the top-level expression only. For instance,
// given the constraint a|b|c, the union type for (a|b)|c
// is recorded, but not the union type for a|b.
Types map[syntax.Expr]TypeAndValue Types map[syntax.Expr]TypeAndValue
// Instances maps identifiers denoting parameterized types or functions to // Instances maps identifiers denoting parameterized types or functions to

View File

@ -348,10 +348,25 @@ func TestTypesInfo(t *testing.T) {
{`package u1a; func _[_ interface{~int}]() {}`, `~int`, `~int`}, {`package u1a; func _[_ interface{~int}]() {}`, `~int`, `~int`},
{`package u2a; func _[_ interface{int|string}]() {}`, `int | string`, `int|string`}, {`package u2a; func _[_ interface{int|string}]() {}`, `int | string`, `int|string`},
{`package u3a; func _[_ interface{int|string|~bool}]() {}`, `int | string | ~bool`, `int|string|~bool`}, {`package u3a; func _[_ interface{int|string|~bool}]() {}`, `int | string | ~bool`, `int|string|~bool`},
{`package u3a; func _[_ interface{int|string|~bool}]() {}`, `int | string`, `int|string`},
{`package u3a; func _[_ interface{int|string|~bool}]() {}`, `~bool`, `~bool`},
{`package u3a; func _[_ interface{int|string|~float64|~bool}]() {}`, `int | string | ~float64`, `int|string|~float64`},
{`package u0b; func _[_ int]() {}`, `int`, `int`}, {`package u0b; func _[_ int]() {}`, `int`, `int`},
{`package u1b; func _[_ ~int]() {}`, `~int`, `~int`}, {`package u1b; func _[_ ~int]() {}`, `~int`, `~int`},
{`package u2b; func _[_ int|string]() {}`, `int | string`, `int|string`}, {`package u2b; func _[_ int|string]() {}`, `int | string`, `int|string`},
{`package u3b; func _[_ int|string|~bool]() {}`, `int | string | ~bool`, `int|string|~bool`}, {`package u3b; func _[_ int|string|~bool]() {}`, `int | string | ~bool`, `int|string|~bool`},
{`package u3b; func _[_ int|string|~bool]() {}`, `int | string`, `int|string`},
{`package u3b; func _[_ int|string|~bool]() {}`, `~bool`, `~bool`},
{`package u3b; func _[_ int|string|~float64|~bool]() {}`, `int | string | ~float64`, `int|string|~float64`},
{`package u0c; type _ interface{int}`, `int`, `int`},
{`package u1c; type _ interface{~int}`, `~int`, `~int`},
{`package u2c; type _ interface{int|string}`, `int | string`, `int|string`},
{`package u3c; type _ interface{int|string|~bool}`, `int | string | ~bool`, `int|string|~bool`},
{`package u3c; type _ interface{int|string|~bool}`, `int | string`, `int|string`},
{`package u3c; type _ interface{int|string|~bool}`, `~bool`, `~bool`},
{`package u3c; type _ interface{int|string|~float64|~bool}`, `int | string | ~float64`, `int|string|~float64`},
} }
for _, test := range tests { for _, test := range tests {

View File

@ -48,23 +48,37 @@ const maxTermCount = 100
// parseUnion parses uexpr as a union of expressions. // parseUnion parses uexpr as a union of expressions.
// The result is a Union type, or Typ[Invalid] for some errors. // The result is a Union type, or Typ[Invalid] for some errors.
func parseUnion(check *Checker, uexpr syntax.Expr) Type { func parseUnion(check *Checker, uexpr syntax.Expr) Type {
tlist := flattenUnion(nil, uexpr) blist, tlist := flattenUnion(nil, uexpr)
assert(len(blist) == len(tlist)-1)
var terms []*Term var terms []*Term
for _, x := range tlist {
tilde, typ := parseTilde(check, x) var u Type
if len(tlist) == 1 && !tilde { for i, x := range tlist {
term := parseTilde(check, x)
if len(tlist) == 1 && !term.tilde {
// Single type. Ok to return early because all relevant // Single type. Ok to return early because all relevant
// checks have been performed in parseTilde (no need to // checks have been performed in parseTilde (no need to
// run through term validity check below). // run through term validity check below).
return typ // typ already recorded through check.typ in parseTilde return term.typ // typ already recorded through check.typ in parseTilde
} }
if len(terms) >= maxTermCount { if len(terms) >= maxTermCount {
check.errorf(x, "cannot handle more than %d union terms (implementation limitation)", maxTermCount) if u != Typ[Invalid] {
check.recordTypeAndValue(uexpr, typexpr, Typ[Invalid], nil) check.errorf(x, "cannot handle more than %d union terms (implementation limitation)", maxTermCount)
return Typ[Invalid] u = Typ[Invalid]
}
} else {
terms = append(terms, term)
u = &Union{terms}
} }
terms = append(terms, NewTerm(tilde, typ))
if i > 0 {
check.recordTypeAndValue(blist[i-1], typexpr, u, nil)
}
}
if u == Typ[Invalid] {
return u
} }
// Check validity of terms. // Check validity of terms.
@ -106,17 +120,17 @@ func parseUnion(check *Checker, uexpr syntax.Expr) Type {
} }
}) })
u := &Union{terms}
check.recordTypeAndValue(uexpr, typexpr, u, nil)
return u return u
} }
func parseTilde(check *Checker, x syntax.Expr) (tilde bool, typ Type) { func parseTilde(check *Checker, tx syntax.Expr) *Term {
x := tx
var tilde bool
if op, _ := x.(*syntax.Operation); op != nil && op.Op == syntax.Tilde { if op, _ := x.(*syntax.Operation); op != nil && op.Op == syntax.Tilde {
x = op.X x = op.X
tilde = true tilde = true
} }
typ = check.typ(x) typ := check.typ(x)
// Embedding stand-alone type parameters is not permitted (issue #47127). // Embedding stand-alone type parameters is not permitted (issue #47127).
// We don't need this restriction anymore if we make the underlying type of a type // We don't need this restriction anymore if we make the underlying type of a type
// parameter its constraint interface: if we embed a lone type parameter, we will // parameter its constraint interface: if we embed a lone type parameter, we will
@ -126,7 +140,11 @@ func parseTilde(check *Checker, x syntax.Expr) (tilde bool, typ Type) {
check.error(x, "cannot embed a type parameter") check.error(x, "cannot embed a type parameter")
typ = Typ[Invalid] typ = Typ[Invalid]
} }
return term := NewTerm(tilde, typ)
if tilde {
check.recordTypeAndValue(tx, typexpr, &Union{[]*Term{term}}, nil)
}
return term
} }
// overlappingTerm reports the index of the term x in terms which is // overlappingTerm reports the index of the term x in terms which is
@ -147,10 +165,13 @@ func overlappingTerm(terms []*Term, y *Term) int {
return -1 return -1
} }
func flattenUnion(list []syntax.Expr, x syntax.Expr) []syntax.Expr { // flattenUnion walks a union type expression of the form A | B | C | ...,
// extracting both the binary exprs (blist) and leaf types (tlist).
func flattenUnion(list []syntax.Expr, x syntax.Expr) (blist, tlist []syntax.Expr) {
if o, _ := x.(*syntax.Operation); o != nil && o.Op == syntax.Or { if o, _ := x.(*syntax.Operation); o != nil && o.Op == syntax.Or {
list = flattenUnion(list, o.X) blist, tlist = flattenUnion(list, o.X)
blist = append(blist, o)
x = o.Y x = o.Y
} }
return append(list, x) return blist, append(tlist, x)
} }

View File

@ -379,9 +379,10 @@ func TestTypesInfo(t *testing.T) {
{genericPkg + `u1a; func _[_ interface{~int}]() {}`, `~int`, `~int`}, {genericPkg + `u1a; func _[_ interface{~int}]() {}`, `~int`, `~int`},
{genericPkg + `u2a; func _[_ interface{int|string}]() {}`, `int | string`, `int|string`}, {genericPkg + `u2a; func _[_ interface{int|string}]() {}`, `int | string`, `int|string`},
{genericPkg + `u3a; func _[_ interface{int|string|~bool}]() {}`, `int | string | ~bool`, `int|string|~bool`}, {genericPkg + `u3a; func _[_ interface{int|string|~bool}]() {}`, `int | string | ~bool`, `int|string|~bool`},
{genericPkg + `u3b; func _[_ interface{int|string|~bool}]() {}`, `int | string`, `int|string`}, {genericPkg + `u3a; func _[_ interface{int|string|~bool}]() {}`, `int | string`, `int|string`},
{genericPkg + `u3b; func _[_ interface{int|string|~bool}]() {}`, `~bool`, `~bool`}, {genericPkg + `u3a; func _[_ interface{int|string|~bool}]() {}`, `~bool`, `~bool`},
{genericPkg + `u3b; func _[_ interface{int|string|~float64|~bool}]() {}`, `int | string | ~float64`, `int|string|~float64`}, {genericPkg + `u3a; func _[_ interface{int|string|~float64|~bool}]() {}`, `int | string | ~float64`, `int|string|~float64`},
{genericPkg + `u0b; func _[_ int]() {}`, `int`, `int`}, {genericPkg + `u0b; func _[_ int]() {}`, `int`, `int`},
{genericPkg + `u1b; func _[_ ~int]() {}`, `~int`, `~int`}, {genericPkg + `u1b; func _[_ ~int]() {}`, `~int`, `~int`},
{genericPkg + `u2b; func _[_ int|string]() {}`, `int | string`, `int|string`}, {genericPkg + `u2b; func _[_ int|string]() {}`, `int | string`, `int|string`},
@ -389,13 +390,14 @@ func TestTypesInfo(t *testing.T) {
{genericPkg + `u3b; func _[_ int|string|~bool]() {}`, `int | string`, `int|string`}, {genericPkg + `u3b; func _[_ int|string|~bool]() {}`, `int | string`, `int|string`},
{genericPkg + `u3b; func _[_ int|string|~bool]() {}`, `~bool`, `~bool`}, {genericPkg + `u3b; func _[_ int|string|~bool]() {}`, `~bool`, `~bool`},
{genericPkg + `u3b; func _[_ int|string|~float64|~bool]() {}`, `int | string | ~float64`, `int|string|~float64`}, {genericPkg + `u3b; func _[_ int|string|~float64|~bool]() {}`, `int | string | ~float64`, `int|string|~float64`},
{genericPkg + `u0b; type _ interface{int}`, `int`, `int`},
{genericPkg + `u1b; type _ interface{~int}`, `~int`, `~int`}, {genericPkg + `u0c; type _ interface{int}`, `int`, `int`},
{genericPkg + `u2b; type _ interface{int|string}`, `int | string`, `int|string`}, {genericPkg + `u1c; type _ interface{~int}`, `~int`, `~int`},
{genericPkg + `u3b; type _ interface{int|string|~bool}`, `int | string | ~bool`, `int|string|~bool`}, {genericPkg + `u2c; type _ interface{int|string}`, `int | string`, `int|string`},
{genericPkg + `u3b; type _ interface{int|string|~bool}`, `int | string`, `int|string`}, {genericPkg + `u3c; type _ interface{int|string|~bool}`, `int | string | ~bool`, `int|string|~bool`},
{genericPkg + `u3b; type _ interface{int|string|~bool}`, `~bool`, `~bool`}, {genericPkg + `u3c; type _ interface{int|string|~bool}`, `int | string`, `int|string`},
{genericPkg + `u3b; type _ interface{int|string|~float64|~bool}`, `int | string | ~float64`, `int|string|~float64`}, {genericPkg + `u3c; type _ interface{int|string|~bool}`, `~bool`, `~bool`},
{genericPkg + `u3c; type _ interface{int|string|~float64|~bool}`, `int | string | ~float64`, `int|string|~float64`},
} }
for _, test := range tests { for _, test := range tests {