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>
// 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
// 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 {
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
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)
-}
// 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)
// 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))
}
})
- 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) {
}
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)
+}
// 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
// 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 {
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
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)
-}
// 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)
// 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))
}
})
- 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) {
}
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)
+}