aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/types2/union.go
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2021-05-20 15:13:04 -0700
committerRobert Griesemer <gri@golang.org>2021-05-24 23:16:07 +0000
commit5770d7a63743ddfd0e78877f162cbbf18ffb9c1d (patch)
treeee9d3626479a6ac8589dc7b446c24365281456bc /src/cmd/compile/internal/types2/union.go
parentcc7ceea5859beb5569d1a278e389ae7dd7d13f8b (diff)
downloadgo-5770d7a63743ddfd0e78877f162cbbf18ffb9c1d.tar.gz
go-5770d7a63743ddfd0e78877f162cbbf18ffb9c1d.zip
[dev.typeparams] cmd/compile/internal/types2: accept embedded interface elements
Accept embedded interface elements of the form ~T or A|B and treat them like type lists: for now the elements of a union cannot be interfaces. Also, translate existing style "type"- lists in interfaces into interface elements: "type a, b, c" becomes a union element "~a|~b|~c" which in turn is handled internally like a type list. For now, "~" is still ignored and type lists are mapped to Sum types as before, thus ensuring that all existing tests work as before (with some minor adjustments). Introduced a new Union type to represent union elements. For now they don't make it past interface completion where they are represented as a Sum type. Thus, except for printing (and the respective tests) and substitution for interfaces, the various type switches ignore Union types. In a next step, we'll replace Sum types with union types and then consider the ~ functionality as well. Because union elements are no different from embedded interfaces we don't need a separate Interface.types field anymore. Removed. For #45346. Change-Id: I98ac3286aea9d706e98aee80241d4712ed99af08 Reviewed-on: https://go-review.googlesource.com/c/go/+/321689 Trust: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
Diffstat (limited to 'src/cmd/compile/internal/types2/union.go')
-rw-r--r--src/cmd/compile/internal/types2/union.go105
1 files changed, 105 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/types2/union.go b/src/cmd/compile/internal/types2/union.go
new file mode 100644
index 0000000000..70dc3bc360
--- /dev/null
+++ b/src/cmd/compile/internal/types2/union.go
@@ -0,0 +1,105 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types2
+
+import "cmd/compile/internal/syntax"
+
+// ----------------------------------------------------------------------------
+// API
+
+// A Union represents a union of terms.
+// A term is a type, possibly with a ~ (tilde) indication.
+type Union struct {
+ terms []Type // terms are unique
+ tilde []bool // if tilde[i] is set, terms[i] is of the form ~T
+}
+
+func NewUnion(terms []Type, tilde []bool) Type { return newUnion(terms, tilde) }
+
+func (u *Union) NumTerms() int { return len(u.terms) }
+func (u *Union) Term(i int) (Type, bool) { return u.terms[i], u.tilde[i] }
+
+func (u *Union) Underlying() Type { return u }
+func (u *Union) String() string { return TypeString(u, nil) }
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+func newUnion(terms []Type, tilde []bool) Type {
+ assert(len(terms) == len(tilde))
+ if terms == nil {
+ return nil
+ }
+ t := new(Union)
+ t.terms = terms
+ t.tilde = tilde
+ return t
+}
+
+func parseUnion(check *Checker, tlist []syntax.Expr) Type {
+ var terms []Type
+ var tilde []bool
+ for _, x := range tlist {
+ t, d := parseTilde(check, x)
+ if len(tlist) == 1 && !d {
+ return t // single type
+ }
+ terms = append(terms, t)
+ tilde = append(tilde, d)
+ }
+
+ // Ensure that each type is only present once in the type list.
+ // It's ok to do this check at the end because it's not a requirement
+ // for correctness of the code.
+ // Note: This is a quadratic algorithm, but unions tend to be short.
+ check.later(func() {
+ for i, t := range terms {
+ t := expand(t)
+ if t == Typ[Invalid] {
+ continue
+ }
+
+ x := tlist[i]
+ pos := syntax.StartPos(x)
+ // We may not know the position of x if it was a typechecker-
+ // introduced ~T type of a type list entry T. Use the position
+ // of T instead.
+ // TODO(gri) remove this test once we don't support type lists anymore
+ if !pos.IsKnown() {
+ if op, _ := x.(*syntax.Operation); op != nil {
+ pos = syntax.StartPos(op.X)
+ }
+ }
+
+ u := under(t)
+ if tilde[i] {
+ // TODO(gri) enable this check once we have converted tests
+ // if !Identical(u, t) {
+ // check.errorf(x, "invalid use of ~ (underlying type of %s is %s)", t, u)
+ // }
+ }
+ if _, ok := u.(*Interface); ok {
+ check.errorf(pos, "cannot use interface %s with ~ or inside a union (implementation restriction)", t)
+ }
+
+ // Complain about duplicate entries a|a, but also a|~a, and ~a|~a.
+ if includes(terms[:i], t) {
+ // TODO(gri) this currently doesn't print the ~ if present
+ check.softErrorf(pos, "duplicate term %s in union element", t)
+ }
+ }
+ })
+
+ return newUnion(terms, tilde)
+}
+
+func parseTilde(check *Checker, x syntax.Expr) (Type, bool) {
+ tilde := false
+ if op, _ := x.(*syntax.Operation); op != nil && op.Op == syntax.Tilde {
+ x = op.X
+ tilde = true
+ }
+ return check.anyType(x), tilde
+}