]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: record (top-level) union types
authorRobert Griesemer <gri@golang.org>
Mon, 13 Dec 2021 23:04:43 +0000 (15:04 -0800)
committerRobert Griesemer <gri@golang.org>
Tue, 14 Dec 2021 01:48:22 +0000 (01:48 +0000)
Fixes #50093.

Change-Id: Ibebeda542d2a81c979670f9098c4a6d2c3e73abb
Reviewed-on: https://go-review.googlesource.com/c/go/+/371514
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
src/cmd/compile/internal/types2/api.go
src/cmd/compile/internal/types2/api_test.go
src/cmd/compile/internal/types2/interface.go
src/cmd/compile/internal/types2/union.go
src/go/types/api.go
src/go/types/api_test.go
src/go/types/interface.go
src/go/types/union.go

index 4ea3989c39577c4df32a72755e01a045bd3c3937..ed5bced643428dc852cb4eb6fc134132e7e1da2a 100644 (file)
@@ -202,6 +202,12 @@ 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[syntax.Expr]TypeAndValue
 
        // Instances maps identifiers denoting parameterized types or functions to
index 4227397df9f7a18eb5b9e2ec68ad64b7e6d46f64..fc8b5cd4ee01edbf48e13a1d8368e2f1ebbef273 100644 (file)
@@ -342,6 +342,16 @@ func TestTypesInfo(t *testing.T) {
 
                // issue 47895
                {`package p; import "unsafe"; type S struct { f int }; var s S; var _ = unsafe.Offsetof(s.f)`, `s.f`, `int`},
+
+               // issue 50093
+               {`package u0a; func _[_ interface{int}]() {}`, `int`, `int`},
+               {`package u1a; func _[_ interface{~int}]() {}`, `~int`, `~int`},
+               {`package u2a; func _[_ interface{int|string}]() {}`, `int | string`, `int|string`},
+               {`package u3a; func _[_ interface{int|string|~bool}]() {}`, `int | string | ~bool`, `int|string|~bool`},
+               {`package u0b; func _[_ int]() {}`, `int`, `int`},
+               {`package u1b; func _[_ ~int]() {}`, `~int`, `~int`},
+               {`package u2b; func _[_ int|string]() {}`, `int | string`, `int|string`},
+               {`package u3b; func _[_ int|string|~bool]() {}`, `int | string | ~bool`, `int|string|~bool`},
        }
 
        for _, test := range tests {
index 96c92ccaecd6b2f177338d22f6cd03635672a6c4..b048fdd9e25ba70c6687f695f15767b7e25b17fe 100644 (file)
@@ -111,7 +111,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType
 
        for _, f := range iface.MethodList {
                if f.Name == nil {
-                       addEmbedded(posFor(f.Type), parseUnion(check, flattenUnion(nil, f.Type)))
+                       addEmbedded(posFor(f.Type), parseUnion(check, f.Type))
                        continue
                }
                // f.Name != nil
@@ -182,11 +182,3 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType
                ityp.check = nil
        }).describef(iface, "compute type set for %s", ityp)
 }
-
-func flattenUnion(list []syntax.Expr, x syntax.Expr) []syntax.Expr {
-       if o, _ := x.(*syntax.Operation); o != nil && o.Op == syntax.Or {
-               list = flattenUnion(list, o.X)
-               x = o.Y
-       }
-       return append(list, x)
-}
index 2304b302801d2104d398aa71b2628d87f817cd3d..97581fe863083ef21449b08db081bd6ac1eb3705 100644 (file)
@@ -46,10 +46,11 @@ func (t *Term) String() string { return (*term)(t).String() }
 // Avoid excessive type-checking times due to quadratic termlist operations.
 const maxTermCount = 100
 
-// parseUnion parses the given list of type expressions tlist as a union of
-// those expressions. The result is a Union type, or Typ[Invalid] for some
-// errors.
-func parseUnion(check *Checker, tlist []syntax.Expr) Type {
+// 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 syntax.Expr) Type {
+       tlist := flattenUnion(nil, uexpr)
+
        var terms []*Term
        for _, x := range tlist {
                tilde, typ := parseTilde(check, x)
@@ -57,10 +58,11 @@ func parseUnion(check *Checker, tlist []syntax.Expr) Type {
                        // 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
+                       return typ // typ already recorded through check.typ in parseTilde
                }
                if len(terms) >= maxTermCount {
                        check.errorf(x, "cannot handle more than %d union terms (implementation limitation)", maxTermCount)
+                       check.recordTypeAndValue(uexpr, typexpr, Typ[Invalid], nil)
                        return Typ[Invalid]
                }
                terms = append(terms, NewTerm(tilde, typ))
@@ -105,7 +107,9 @@ func parseUnion(check *Checker, tlist []syntax.Expr) Type {
                }
        })
 
-       return &Union{terms, nil}
+       u := &Union{terms, nil}
+       check.recordTypeAndValue(uexpr, typexpr, u, nil)
+       return u
 }
 
 func parseTilde(check *Checker, x syntax.Expr) (tilde bool, typ Type) {
@@ -143,3 +147,11 @@ func overlappingTerm(terms []*Term, y *Term) int {
        }
        return -1
 }
+
+func flattenUnion(list []syntax.Expr, x syntax.Expr) []syntax.Expr {
+       if o, _ := x.(*syntax.Operation); o != nil && o.Op == syntax.Or {
+               list = flattenUnion(list, o.X)
+               x = o.Y
+       }
+       return append(list, x)
+}
index 51d58c49aab1336795450882554a2b4ba44a4e4b..c4d81c1491e526b73bee1341f826428048145aa5 100644 (file)
@@ -197,6 +197,12 @@ 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 7b7baa76042b8a9669f5e8257842ea5a7ba7b9eb..1ee9806fd0ad3852a31c66a4280f5994603fdc89 100644 (file)
@@ -373,6 +373,16 @@ func TestTypesInfo(t *testing.T) {
 
                // issue 47895
                {`package p; import "unsafe"; type S struct { f int }; var s S; var _ = unsafe.Offsetof(s.f)`, `s.f`, `int`},
+
+               // issue 50093
+               {genericPkg + `u0a; func _[_ interface{int}]() {}`, `int`, `int`},
+               {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 + `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`},
        }
 
        for _, test := range tests {
index ef65bc6b2bdf0fee7f27d8ba8b0945d57f94eb44..1ff90157804366acac2503a1996781b9468433ea 100644 (file)
@@ -152,7 +152,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
 
        for _, f := range iface.Methods.List {
                if len(f.Names) == 0 {
-                       addEmbedded(f.Type.Pos(), parseUnion(check, flattenUnion(nil, f.Type)))
+                       addEmbedded(f.Type.Pos(), parseUnion(check, f.Type))
                        continue
                }
                // f.Name != nil
@@ -223,11 +223,3 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
                ityp.check = nil
        }).describef(iface, "compute type set for %s", ityp)
 }
-
-func flattenUnion(list []ast.Expr, x ast.Expr) []ast.Expr {
-       if o, _ := x.(*ast.BinaryExpr); o != nil && o.Op == token.OR {
-               list = flattenUnion(list, o.X)
-               x = o.Y
-       }
-       return append(list, x)
-}
index 2a65ca4d8e0b531a83c7adf2239e3796c98529a4..1437bd4624a19af627427b306f134839bbfe7ece 100644 (file)
@@ -49,10 +49,11 @@ func (t *Term) String() string { return (*term)(t).String() }
 // Avoid excessive type-checking times due to quadratic termlist operations.
 const maxTermCount = 100
 
-// parseUnion parses the given list of type expressions tlist as a union of
-// those expressions. The result is a Union type, or Typ[Invalid] for some
-// errors.
-func parseUnion(check *Checker, tlist []ast.Expr) Type {
+// 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)
+
        var terms []*Term
        for _, x := range tlist {
                tilde, typ := parseTilde(check, x)
@@ -60,10 +61,11 @@ func parseUnion(check *Checker, tlist []ast.Expr) Type {
                        // 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
+                       return 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]
                }
                terms = append(terms, NewTerm(tilde, typ))
@@ -108,7 +110,9 @@ func parseUnion(check *Checker, tlist []ast.Expr) Type {
                }
        })
 
-       return &Union{terms, nil}
+       u := &Union{terms, nil}
+       check.recordTypeAndValue(uexpr, typexpr, u, nil)
+       return u
 }
 
 func parseTilde(check *Checker, x ast.Expr) (tilde bool, typ Type) {
@@ -146,3 +150,11 @@ func overlappingTerm(terms []*Term, y *Term) int {
        }
        return -1
 }
+
+func flattenUnion(list []ast.Expr, x ast.Expr) []ast.Expr {
+       if o, _ := x.(*ast.BinaryExpr); o != nil && o.Op == token.OR {
+               list = flattenUnion(list, o.X)
+               x = o.Y
+       }
+       return append(list, x)
+}