Skip to content

Commit 1afa432

Browse files
committed
go/types, types2: record (top-level) union types
Fixes #50093. Change-Id: Ibebeda542d2a81c979670f9098c4a6d2c3e73abb Reviewed-on: https://go-review.googlesource.com/c/go/+/371514 Trust: Robert Griesemer <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent 006d4e6 commit 1afa432

File tree

8 files changed

+70
-30
lines changed

8 files changed

+70
-30
lines changed

src/cmd/compile/internal/types2/api.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,12 @@ type Info struct {
202202
// identifier z in a variable declaration 'var z int' is found
203203
// only in the Defs map, and identifiers denoting packages in
204204
// qualified identifiers are collected in the Uses map.
205+
//
206+
// For binary expressions representing unions in constraint
207+
// position or type elements in interfaces, a union type is
208+
// recorded for the top-level expression only. For instance,
209+
// given the constraint a|b|c, the union type for (a|b)|c
210+
// is recorded, but not the union type for a|b.
205211
Types map[syntax.Expr]TypeAndValue
206212

207213
// Instances maps identifiers denoting parameterized types or functions to

src/cmd/compile/internal/types2/api_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,16 @@ func TestTypesInfo(t *testing.T) {
342342

343343
// issue 47895
344344
{`package p; import "unsafe"; type S struct { f int }; var s S; var _ = unsafe.Offsetof(s.f)`, `s.f`, `int`},
345+
346+
// issue 50093
347+
{`package u0a; func _[_ interface{int}]() {}`, `int`, `int`},
348+
{`package u1a; func _[_ interface{~int}]() {}`, `~int`, `~int`},
349+
{`package u2a; func _[_ interface{int|string}]() {}`, `int | string`, `int|string`},
350+
{`package u3a; func _[_ interface{int|string|~bool}]() {}`, `int | string | ~bool`, `int|string|~bool`},
351+
{`package u0b; func _[_ int]() {}`, `int`, `int`},
352+
{`package u1b; func _[_ ~int]() {}`, `~int`, `~int`},
353+
{`package u2b; func _[_ int|string]() {}`, `int | string`, `int|string`},
354+
{`package u3b; func _[_ int|string|~bool]() {}`, `int | string | ~bool`, `int|string|~bool`},
345355
}
346356

347357
for _, test := range tests {

src/cmd/compile/internal/types2/interface.go

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType
111111

112112
for _, f := range iface.MethodList {
113113
if f.Name == nil {
114-
addEmbedded(posFor(f.Type), parseUnion(check, flattenUnion(nil, f.Type)))
114+
addEmbedded(posFor(f.Type), parseUnion(check, f.Type))
115115
continue
116116
}
117117
// f.Name != nil
@@ -182,11 +182,3 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType
182182
ityp.check = nil
183183
}).describef(iface, "compute type set for %s", ityp)
184184
}
185-
186-
func flattenUnion(list []syntax.Expr, x syntax.Expr) []syntax.Expr {
187-
if o, _ := x.(*syntax.Operation); o != nil && o.Op == syntax.Or {
188-
list = flattenUnion(list, o.X)
189-
x = o.Y
190-
}
191-
return append(list, x)
192-
}

src/cmd/compile/internal/types2/union.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,23 @@ func (t *Term) String() string { return (*term)(t).String() }
4646
// Avoid excessive type-checking times due to quadratic termlist operations.
4747
const maxTermCount = 100
4848

49-
// parseUnion parses the given list of type expressions tlist as a union of
50-
// those expressions. The result is a Union type, or Typ[Invalid] for some
51-
// errors.
52-
func parseUnion(check *Checker, tlist []syntax.Expr) Type {
49+
// parseUnion parses uexpr as a union of expressions.
50+
// The result is a Union type, or Typ[Invalid] for some errors.
51+
func parseUnion(check *Checker, uexpr syntax.Expr) Type {
52+
tlist := flattenUnion(nil, uexpr)
53+
5354
var terms []*Term
5455
for _, x := range tlist {
5556
tilde, typ := parseTilde(check, x)
5657
if len(tlist) == 1 && !tilde {
5758
// Single type. Ok to return early because all relevant
5859
// checks have been performed in parseTilde (no need to
5960
// run through term validity check below).
60-
return typ
61+
return typ // typ already recorded through check.typ in parseTilde
6162
}
6263
if len(terms) >= maxTermCount {
6364
check.errorf(x, "cannot handle more than %d union terms (implementation limitation)", maxTermCount)
65+
check.recordTypeAndValue(uexpr, typexpr, Typ[Invalid], nil)
6466
return Typ[Invalid]
6567
}
6668
terms = append(terms, NewTerm(tilde, typ))
@@ -105,7 +107,9 @@ func parseUnion(check *Checker, tlist []syntax.Expr) Type {
105107
}
106108
})
107109

108-
return &Union{terms, nil}
110+
u := &Union{terms, nil}
111+
check.recordTypeAndValue(uexpr, typexpr, u, nil)
112+
return u
109113
}
110114

111115
func parseTilde(check *Checker, x syntax.Expr) (tilde bool, typ Type) {
@@ -143,3 +147,11 @@ func overlappingTerm(terms []*Term, y *Term) int {
143147
}
144148
return -1
145149
}
150+
151+
func flattenUnion(list []syntax.Expr, x syntax.Expr) []syntax.Expr {
152+
if o, _ := x.(*syntax.Operation); o != nil && o.Op == syntax.Or {
153+
list = flattenUnion(list, o.X)
154+
x = o.Y
155+
}
156+
return append(list, x)
157+
}

src/go/types/api.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,12 @@ type Info struct {
197197
// identifier z in a variable declaration 'var z int' is found
198198
// only in the Defs map, and identifiers denoting packages in
199199
// qualified identifiers are collected in the Uses map.
200+
//
201+
// For binary expressions representing unions in constraint
202+
// position or type elements in interfaces, a union type is
203+
// recorded for the top-level expression only. For instance,
204+
// given the constraint a|b|c, the union type for (a|b)|c
205+
// is recorded, but not the union type for a|b.
200206
Types map[ast.Expr]TypeAndValue
201207

202208
// Instances maps identifiers denoting parameterized types or functions to

src/go/types/api_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,16 @@ func TestTypesInfo(t *testing.T) {
373373

374374
// issue 47895
375375
{`package p; import "unsafe"; type S struct { f int }; var s S; var _ = unsafe.Offsetof(s.f)`, `s.f`, `int`},
376+
377+
// issue 50093
378+
{genericPkg + `u0a; func _[_ interface{int}]() {}`, `int`, `int`},
379+
{genericPkg + `u1a; func _[_ interface{~int}]() {}`, `~int`, `~int`},
380+
{genericPkg + `u2a; func _[_ interface{int|string}]() {}`, `int | string`, `int|string`},
381+
{genericPkg + `u3a; func _[_ interface{int|string|~bool}]() {}`, `int | string | ~bool`, `int|string|~bool`},
382+
{genericPkg + `u0b; func _[_ int]() {}`, `int`, `int`},
383+
{genericPkg + `u1b; func _[_ ~int]() {}`, `~int`, `~int`},
384+
{genericPkg + `u2b; func _[_ int|string]() {}`, `int | string`, `int|string`},
385+
{genericPkg + `u3b; func _[_ int|string|~bool]() {}`, `int | string | ~bool`, `int|string|~bool`},
376386
}
377387

378388
for _, test := range tests {

src/go/types/interface.go

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
152152

153153
for _, f := range iface.Methods.List {
154154
if len(f.Names) == 0 {
155-
addEmbedded(f.Type.Pos(), parseUnion(check, flattenUnion(nil, f.Type)))
155+
addEmbedded(f.Type.Pos(), parseUnion(check, f.Type))
156156
continue
157157
}
158158
// f.Name != nil
@@ -223,11 +223,3 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
223223
ityp.check = nil
224224
}).describef(iface, "compute type set for %s", ityp)
225225
}
226-
227-
func flattenUnion(list []ast.Expr, x ast.Expr) []ast.Expr {
228-
if o, _ := x.(*ast.BinaryExpr); o != nil && o.Op == token.OR {
229-
list = flattenUnion(list, o.X)
230-
x = o.Y
231-
}
232-
return append(list, x)
233-
}

src/go/types/union.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,21 +49,23 @@ func (t *Term) String() string { return (*term)(t).String() }
4949
// Avoid excessive type-checking times due to quadratic termlist operations.
5050
const maxTermCount = 100
5151

52-
// parseUnion parses the given list of type expressions tlist as a union of
53-
// those expressions. The result is a Union type, or Typ[Invalid] for some
54-
// errors.
55-
func parseUnion(check *Checker, tlist []ast.Expr) Type {
52+
// parseUnion parses uexpr as a union of expressions.
53+
// The result is a Union type, or Typ[Invalid] for some errors.
54+
func parseUnion(check *Checker, uexpr ast.Expr) Type {
55+
tlist := flattenUnion(nil, uexpr)
56+
5657
var terms []*Term
5758
for _, x := range tlist {
5859
tilde, typ := parseTilde(check, x)
5960
if len(tlist) == 1 && !tilde {
6061
// Single type. Ok to return early because all relevant
6162
// checks have been performed in parseTilde (no need to
6263
// run through term validity check below).
63-
return typ
64+
return typ // typ already recorded through check.typ in parseTilde
6465
}
6566
if len(terms) >= maxTermCount {
6667
check.errorf(x, _InvalidUnion, "cannot handle more than %d union terms (implementation limitation)", maxTermCount)
68+
check.recordTypeAndValue(uexpr, typexpr, Typ[Invalid], nil)
6769
return Typ[Invalid]
6870
}
6971
terms = append(terms, NewTerm(tilde, typ))
@@ -108,7 +110,9 @@ func parseUnion(check *Checker, tlist []ast.Expr) Type {
108110
}
109111
})
110112

111-
return &Union{terms, nil}
113+
u := &Union{terms, nil}
114+
check.recordTypeAndValue(uexpr, typexpr, u, nil)
115+
return u
112116
}
113117

114118
func parseTilde(check *Checker, x ast.Expr) (tilde bool, typ Type) {
@@ -146,3 +150,11 @@ func overlappingTerm(terms []*Term, y *Term) int {
146150
}
147151
return -1
148152
}
153+
154+
func flattenUnion(list []ast.Expr, x ast.Expr) []ast.Expr {
155+
if o, _ := x.(*ast.BinaryExpr); o != nil && o.Op == token.OR {
156+
list = flattenUnion(list, o.X)
157+
x = o.Y
158+
}
159+
return append(list, x)
160+
}

0 commit comments

Comments
 (0)