]> Cypherpunks.ru repositories - gostls13.git/commitdiff
go/types: record types for union subexpressions
authorRobert Findley <rfindley@google.com>
Tue, 14 Dec 2021 17:48:31 +0000 (12:48 -0500)
committerRobert Findley <rfindley@google.com>
Tue, 14 Dec 2021 23:20:01 +0000 (23:20 +0000)
Prior to unions, unary and binary expressions always had a recorded
type. Preserve this by recording a type for all unary and binary
expressions encountered while parsing a union type.

Updates #50093

Change-Id: I5ba20f37854760596350d91ea325dc98e67e115a
Reviewed-on: https://go-review.googlesource.com/c/go/+/371757
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>

src/go/types/api.go
src/go/types/api_test.go
src/go/types/union.go

index c4d81c1491e526b73bee1341f826428048145aa5..51d58c49aab1336795450882554a2b4ba44a4e4b 100644 (file)
@@ -197,12 +197,6 @@ type Info struct {
        // identifier z in a variable declaration 'var z int' is found
        // only in the Defs map, and identifiers denoting packages in
        // 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[ast.Expr]TypeAndValue
 
        // Instances maps identifiers denoting parameterized types or functions to
index 1ee9806fd0ad3852a31c66a4280f5994603fdc89..e55255d75e21221c522ca97770708ac1b9f214d3 100644 (file)
@@ -379,10 +379,23 @@ func TestTypesInfo(t *testing.T) {
                {genericPkg + `u1a; func _[_ interface{~int}]() {}`, `~int`, `~int`},
                {genericPkg + `u2a; func _[_ interface{int|string}]() {}`, `int | string`, `int|string`},
                {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 + `u3b; func _[_ interface{int|string|~bool}]() {}`, `~bool`, `~bool`},
+               {genericPkg + `u3b; func _[_ interface{int|string|~float64|~bool}]() {}`, `int | string | ~float64`, `int|string|~float64`},
                {genericPkg + `u0b; func _[_ int]() {}`, `int`, `int`},
                {genericPkg + `u1b; func _[_ ~int]() {}`, `~int`, `~int`},
                {genericPkg + `u2b; func _[_ int|string]() {}`, `int | string`, `int|string`},
                {genericPkg + `u3b; func _[_ int|string|~bool]() {}`, `int | string | ~bool`, `int|string|~bool`},
+               {genericPkg + `u3b; func _[_ int|string|~bool]() {}`, `int | string`, `int|string`},
+               {genericPkg + `u3b; func _[_ int|string|~bool]() {}`, `~bool`, `~bool`},
+               {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 + `u2b; type _ interface{int|string}`, `int | string`, `int|string`},
+               {genericPkg + `u3b; type _ interface{int|string|~bool}`, `int | string | ~bool`, `int|string|~bool`},
+               {genericPkg + `u3b; type _ interface{int|string|~bool}`, `int | string`, `int|string`},
+               {genericPkg + `u3b; type _ interface{int|string|~bool}`, `~bool`, `~bool`},
+               {genericPkg + `u3b; type _ interface{int|string|~float64|~bool}`, `int | string | ~float64`, `int|string|~float64`},
        }
 
        for _, test := range tests {
index 9dd67a0db4dc49b7b64b215a8b217e9cf6d8a8d4..7cd5b2a88be77d9024711aa01992581254a23fb2 100644 (file)
@@ -51,23 +51,37 @@ const maxTermCount = 100
 // parseUnion parses uexpr as a union of expressions.
 // The result is a Union type, or Typ[Invalid] for some errors.
 func parseUnion(check *Checker, uexpr ast.Expr) Type {
-       tlist := flattenUnion(nil, uexpr)
+       blist, tlist := flattenUnion(nil, uexpr)
+       assert(len(blist) == len(tlist)-1)
 
        var terms []*Term
-       for _, x := range tlist {
-               tilde, typ := parseTilde(check, x)
-               if len(tlist) == 1 && !tilde {
+
+       var u Type
+       for i, x := range tlist {
+               term := parseTilde(check, x)
+               if len(tlist) == 1 && !term.tilde {
                        // Single type. Ok to return early because all relevant
                        // checks have been performed in parseTilde (no need to
                        // 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 {
-                       check.errorf(x, _InvalidUnion, "cannot handle more than %d union terms (implementation limitation)", maxTermCount)
-                       check.recordTypeAndValue(uexpr, typexpr, Typ[Invalid], nil)
-                       return Typ[Invalid]
+                       if u != Typ[Invalid] {
+                               check.errorf(x, _InvalidUnion, "cannot handle more than %d union terms (implementation limitation)", maxTermCount)
+                               u = Typ[Invalid]
+                       }
+               } else {
+                       terms = append(terms, term)
+                       u = &Union{terms}
+               }
+
+               if i > 0 {
+                       check.recordTypeAndValue(blist[i-1], typexpr, u, nil)
                }
-               terms = append(terms, NewTerm(tilde, typ))
+       }
+
+       if u == Typ[Invalid] {
+               return u
        }
 
        // Check validity of terms.
@@ -109,17 +123,17 @@ func parseUnion(check *Checker, uexpr ast.Expr) Type {
                }
        })
 
-       u := &Union{terms}
-       check.recordTypeAndValue(uexpr, typexpr, u, nil)
        return u
 }
 
-func parseTilde(check *Checker, x ast.Expr) (tilde bool, typ Type) {
+func parseTilde(check *Checker, tx ast.Expr) *Term {
+       x := tx
+       var tilde bool
        if op, _ := x.(*ast.UnaryExpr); op != nil && op.Op == token.TILDE {
                x = op.X
                tilde = true
        }
-       typ = check.typ(x)
+       typ := check.typ(x)
        // 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
        // parameter its constraint interface: if we embed a lone type parameter, we will
@@ -129,7 +143,11 @@ func parseTilde(check *Checker, x ast.Expr) (tilde bool, typ Type) {
                check.error(x, _MisplacedTypeParam, "cannot embed a type parameter")
                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
@@ -150,10 +168,13 @@ func overlappingTerm(terms []*Term, y *Term) int {
        return -1
 }
 
-func flattenUnion(list []ast.Expr, x ast.Expr) []ast.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 []ast.Expr, x ast.Expr) (blist, tlist []ast.Expr) {
        if o, _ := x.(*ast.BinaryExpr); o != nil && o.Op == token.OR {
-               list = flattenUnion(list, o.X)
+               blist, tlist = flattenUnion(list, o.X)
+               blist = append(blist, o)
                x = o.Y
        }
-       return append(list, x)
+       return blist, append(tlist, x)
 }