]> Cypherpunks.ru repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile/internal/types2: accept embedded interface elements
authorRobert Griesemer <gri@golang.org>
Thu, 20 May 2021 22:13:04 +0000 (15:13 -0700)
committerRobert Griesemer <gri@golang.org>
Mon, 24 May 2021 23:16:07 +0000 (23:16 +0000)
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>
24 files changed:
src/cmd/compile/internal/noder/types.go
src/cmd/compile/internal/types2/builtins.go
src/cmd/compile/internal/types2/index.go
src/cmd/compile/internal/types2/infer.go
src/cmd/compile/internal/types2/interface.go
src/cmd/compile/internal/types2/predicates.go
src/cmd/compile/internal/types2/sanitize.go
src/cmd/compile/internal/types2/sizeof_test.go
src/cmd/compile/internal/types2/sizes.go
src/cmd/compile/internal/types2/subst.go
src/cmd/compile/internal/types2/testdata/check/decls0.src
src/cmd/compile/internal/types2/testdata/check/issues.src
src/cmd/compile/internal/types2/testdata/check/typeinst2.go2
src/cmd/compile/internal/types2/testdata/examples/constraints.go2 [new file with mode: 0644]
src/cmd/compile/internal/types2/testdata/fixedbugs/issue39634.go2
src/cmd/compile/internal/types2/testdata/fixedbugs/issue39693.go2
src/cmd/compile/internal/types2/testdata/fixedbugs/issue39711.go2
src/cmd/compile/internal/types2/testdata/fixedbugs/issue39723.go2
src/cmd/compile/internal/types2/testdata/fixedbugs/issue39948.go2
src/cmd/compile/internal/types2/type.go
src/cmd/compile/internal/types2/typestring.go
src/cmd/compile/internal/types2/typestring_test.go
src/cmd/compile/internal/types2/unify.go
src/cmd/compile/internal/types2/union.go [new file with mode: 0644]

index 7fdad29e163e1e54c57b35927c92bca51217b43b..16d664f5380f963280a8a78f7b2a749b7c201a19 100644 (file)
@@ -187,14 +187,18 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
                for i := range embeddeds {
                        // TODO(mdempsky): Get embedding position.
                        e := typ.EmbeddedType(i)
-                       if t := types2.AsInterface(e); t != nil && t.IsComparable() {
-                               // Ignore predefined type 'comparable', since it
-                               // doesn't resolve and it doesn't have any
-                               // relevant methods.
-                               continue
+                       if t := types2.AsInterface(e); t != nil {
+                               if t.IsComparable() {
+                                       // Ignore predefined type 'comparable', since it
+                                       // doesn't resolve and it doesn't have any
+                                       // relevant methods.
+                                       continue
+                               }
+                               embeddeds[j] = types.NewField(src.NoXPos, nil, g.typ1(e))
+                               j++
                        }
-                       embeddeds[j] = types.NewField(src.NoXPos, nil, g.typ1(e))
-                       j++
+                       // Ignore embedded non-interface types - they correspond
+                       // to type lists which we currently don't handle here.
                }
                embeddeds = embeddeds[:j]
 
index b9e178dd576c708980bdfe1dc59f56e695aea262..94fb506d8012100ba94adc437701928e4fe0d8f3 100644 (file)
@@ -769,7 +769,7 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
                tpar := NewTypeName(nopos, nil /* = Universe pkg */, "<type parameter>", nil)
                ptyp := check.NewTypeParam(tpar, 0, &emptyInterface) // assigns type to tpar as a side-effect
                tsum := NewSum(rtypes)
-               ptyp.bound = &Interface{types: tsum, allMethods: markComplete, allTypes: tsum}
+               ptyp.bound = &Interface{allMethods: markComplete, allTypes: tsum}
 
                return ptyp
        }
index c94017a8fb9b98c29d55707f867b0c15bcbb3e50..33e79aac3eb5b6d2da878cc94755cd5bb5bbc175 100644 (file)
@@ -126,7 +126,7 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo
                        case *TypeParam:
                                check.errorf(x, "type of %s contains a type parameter - cannot index (implementation restriction)", x)
                        case *instance:
-                               panic("unimplemented")
+                               unimplemented()
                        }
                        if e == nil || telem != nil && !Identical(e, telem) {
                                return false
index f37d7f6477e96ab29bb8d0e6705cb65b0dd8cb37..d8865784a5c3fab757580f92c1cf672ad59558bb 100644 (file)
@@ -320,6 +320,9 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
                // Thus, we only need to look at the input and result parameters.
                return w.isParameterized(t.params) || w.isParameterized(t.results)
 
+       case *Union:
+               unimplemented()
+
        case *Interface:
                if t.allMethods != nil {
                        // interface is complete - quick test
@@ -337,7 +340,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
                                        return true
                                }
                        }
-                       return w.isParameterizedList(unpack(t.types))
+                       return w.isParameterizedList(t.embeddeds)
                }, nil)
 
        case *Map:
index 21968b34aa7a220b21dbbdc277915eab6c4d6d94..d590066ad606692a53f93386bbecd773d44494cd 100644 (file)
@@ -11,72 +11,84 @@ import (
 )
 
 func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType, def *Named) {
-       var tname *syntax.Name // most recent "type" name
-       var types []syntax.Expr
+       var tlist []syntax.Expr // types collected from all type lists
+       var tname *syntax.Name  // most recent "type" name
+
        for _, f := range iface.MethodList {
-               if f.Name != nil {
-                       // We have a method with name f.Name, or a type
-                       // of a type list (f.Name.Value == "type").
-                       name := f.Name.Value
-                       if name == "_" {
-                               if check.conf.CompilerErrorMessages {
-                                       check.error(f.Name, "methods must have a unique non-blank name")
-                               } else {
-                                       check.error(f.Name, "invalid method name _")
-                               }
-                               continue // ignore
+               if f.Name == nil {
+                       // We have an embedded type; possibly a union of types.
+                       ityp.embeddeds = append(ityp.embeddeds, parseUnion(check, flattenUnion(nil, f.Type)))
+                       check.posMap[ityp] = append(check.posMap[ityp], f.Type.Pos())
+                       continue
+               }
+               // f.Name != nil
+
+               // We have a method with name f.Name, or a type of a type list (f.Name.Value == "type").
+               name := f.Name.Value
+               if name == "_" {
+                       if check.conf.CompilerErrorMessages {
+                               check.error(f.Name, "methods must have a unique non-blank name")
+                       } else {
+                               check.error(f.Name, "invalid method name _")
                        }
+                       continue // ignore
+               }
 
-                       if name == "type" {
-                               // Always collect all type list entries, even from
-                               // different type lists, under the assumption that
-                               // the author intended to include all types.
-                               types = append(types, f.Type)
-                               if tname != nil && tname != f.Name {
-                                       check.error(f.Name, "cannot have multiple type lists in an interface")
-                               }
-                               tname = f.Name
-                               continue
+               if name == "type" {
+                       // For now, collect all type list entries as if it
+                       // were a single union, where each union element is
+                       // of the form ~T.
+                       // TODO(gri) remove once we disallow type lists
+                       op := new(syntax.Operation)
+                       // We should also set the position (but there is no setter);
+                       // we don't care because this code will eventually go away.
+                       op.Op = syntax.Tilde
+                       op.X = f.Type
+                       tlist = append(tlist, op)
+                       if tname != nil && tname != f.Name {
+                               check.error(f.Name, "cannot have multiple type lists in an interface")
                        }
+                       tname = f.Name
+                       continue
+               }
 
-                       typ := check.typ(f.Type)
-                       sig, _ := typ.(*Signature)
-                       if sig == nil {
-                               if typ != Typ[Invalid] {
-                                       check.errorf(f.Type, invalidAST+"%s is not a method signature", typ)
-                               }
-                               continue // ignore
+               typ := check.typ(f.Type)
+               sig, _ := typ.(*Signature)
+               if sig == nil {
+                       if typ != Typ[Invalid] {
+                               check.errorf(f.Type, invalidAST+"%s is not a method signature", typ)
                        }
+                       continue // ignore
+               }
 
-                       // Always type-check method type parameters but complain if they are not enabled.
-                       // (This extra check is needed here because interface method signatures don't have
-                       // a receiver specification.)
-                       if sig.tparams != nil && !acceptMethodTypeParams {
-                               check.error(f.Type, "methods cannot have type parameters")
-                       }
+               // Always type-check method type parameters but complain if they are not enabled.
+               // (This extra check is needed here because interface method signatures don't have
+               // a receiver specification.)
+               if sig.tparams != nil && !acceptMethodTypeParams {
+                       check.error(f.Type, "methods cannot have type parameters")
+               }
 
-                       // use named receiver type if available (for better error messages)
-                       var recvTyp Type = ityp
-                       if def != nil {
-                               recvTyp = def
-                       }
-                       sig.recv = NewVar(f.Name.Pos(), check.pkg, "", recvTyp)
-
-                       m := NewFunc(f.Name.Pos(), check.pkg, name, sig)
-                       check.recordDef(f.Name, m)
-                       ityp.methods = append(ityp.methods, m)
-               } else {
-                       // We have an embedded type. completeInterface will
-                       // eventually verify that we have an interface.
-                       ityp.embeddeds = append(ityp.embeddeds, check.typ(f.Type))
-                       check.posMap[ityp] = append(check.posMap[ityp], f.Type.Pos())
+               // use named receiver type if available (for better error messages)
+               var recvTyp Type = ityp
+               if def != nil {
+                       recvTyp = def
                }
+               sig.recv = NewVar(f.Name.Pos(), check.pkg, "", recvTyp)
+
+               m := NewFunc(f.Name.Pos(), check.pkg, name, sig)
+               check.recordDef(f.Name, m)
+               ityp.methods = append(ityp.methods, m)
        }
 
-       // type constraints
-       ityp.types = NewSum(check.collectTypeConstraints(iface.Pos(), types))
+       // If we saw a type list, add it like an embedded union.
+       if tlist != nil {
+               ityp.embeddeds = append(ityp.embeddeds, parseUnion(check, tlist))
+               // Types T in a type list are added as ~T expressions but we don't
+               // have the position of the '~'. Use the first type position instead.
+               check.posMap[ityp] = append(check.posMap[ityp], tlist[0].(*syntax.Operation).X.Pos())
+       }
 
-       if len(ityp.methods) == 0 && ityp.types == nil && len(ityp.embeddeds) == 0 {
+       if len(ityp.methods) == 0 && len(ityp.embeddeds) == 0 {
                // empty interface
                ityp.allMethods = markComplete
                return
@@ -89,32 +101,12 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType
        check.later(func() { check.completeInterface(iface.Pos(), ityp) })
 }
 
-func (check *Checker) collectTypeConstraints(pos syntax.Pos, types []syntax.Expr) []Type {
-       list := make([]Type, 0, len(types)) // assume all types are correct
-       for _, texpr := range types {
-               if texpr == nil {
-                       check.error(pos, invalidAST+"missing type constraint")
-                       continue
-               }
-               list = append(list, check.varType(texpr))
+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
        }
-
-       // Ensure that each type is only present once in the type list.  Types may be
-       // interfaces, which may not be complete yet. 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 type lists tend to be short.
-       check.later(func() {
-               for i, t := range list {
-                       if t := asInterface(t); t != nil {
-                               check.completeInterface(types[i].Pos(), t)
-                       }
-                       if includes(list[:i], t) {
-                               check.softErrorf(types[i], "duplicate type %s in type list", t)
-                       }
-               }
-       })
-
-       return list
+       return append(list, x)
 }
 
 // includes reports whether typ is in list
@@ -143,6 +135,7 @@ func (check *Checker) completeInterface(pos syntax.Pos, ityp *Interface) {
        completeInterface(check, pos, ityp)
 }
 
+// completeInterface may be called with check == nil.
 func completeInterface(check *Checker, pos syntax.Pos, ityp *Interface) {
        assert(ityp.allMethods == nil)
 
@@ -195,6 +188,7 @@ func completeInterface(check *Checker, pos syntax.Pos, ityp *Interface) {
                        if check == nil {
                                panic(fmt.Sprintf("%s: duplicate method %s", m.pos, m.name))
                        }
+                       // check != nil
                        var err error_
                        err.errorf(pos, "duplicate method %s", m.name)
                        err.errorf(mpos[other.(*Func)], "other declaration of %s", m.name)
@@ -210,6 +204,7 @@ func completeInterface(check *Checker, pos syntax.Pos, ityp *Interface) {
                                todo = append(todo, m, other.(*Func))
                                break
                        }
+                       // check != nil
                        check.later(func() {
                                if !check.allowVersion(m.pkg, 1, 14) || !check.identical(m.typ, other.Type()) {
                                        var err error_
@@ -225,9 +220,8 @@ func completeInterface(check *Checker, pos syntax.Pos, ityp *Interface) {
                addMethod(m.pos, m, true)
        }
 
-       // collect types
-       allTypes := ityp.types
-
+       // collect embedded elements
+       var allTypes Type
        var posList []syntax.Pos
        if check != nil {
                posList = check.posMap[ityp]
@@ -237,31 +231,36 @@ func completeInterface(check *Checker, pos syntax.Pos, ityp *Interface) {
                if posList != nil {
                        pos = posList[i]
                }
-               utyp := under(typ)
-               etyp := asInterface(utyp)
-               if etyp == nil {
-                       if utyp != Typ[Invalid] {
-                               var format string
-                               if _, ok := utyp.(*TypeParam); ok {
-                                       format = "%s is a type parameter, not an interface"
-                               } else {
-                                       format = "%s is not an interface"
-                               }
-                               if check != nil {
-                                       check.errorf(pos, format, typ)
-                               } else {
-                                       panic(fmt.Sprintf("%s: "+format, pos, typ))
-                               }
+               var types Type
+               switch t := under(typ).(type) {
+               case *Interface:
+                       if t.allMethods == nil {
+                               completeInterface(check, pos, t)
                        }
-                       continue
-               }
-               if etyp.allMethods == nil {
-                       completeInterface(check, pos, etyp)
-               }
-               for _, m := range etyp.allMethods {
-                       addMethod(pos, m, false) // use embedding position pos rather than m.pos
+                       for _, m := range t.allMethods {
+                               addMethod(pos, m, false) // use embedding position pos rather than m.pos
+                       }
+                       types = t.allTypes
+               case *Union:
+                       types = NewSum(t.terms)
+                       // TODO(gri) don't ignore tilde information
+               case *TypeParam:
+                       if check != nil && !check.allowVersion(check.pkg, 1, 18) {
+                               check.errorf(pos, "%s is a type parameter, not an interface", typ)
+                               continue
+                       }
+                       types = t
+               default:
+                       if t == Typ[Invalid] {
+                               continue
+                       }
+                       if check != nil && !check.allowVersion(check.pkg, 1, 18) {
+                               check.errorf(pos, "%s is not an interface", typ)
+                               continue
+                       }
+                       types = t
                }
-               allTypes = intersect(allTypes, etyp.allTypes)
+               allTypes = intersect(allTypes, types)
        }
 
        // process todo's (this only happens if check == nil)
@@ -281,7 +280,7 @@ func completeInterface(check *Checker, pos syntax.Pos, ityp *Interface) {
 }
 
 // intersect computes the intersection of the types x and y.
-// Note: A incomming nil type stands for the top type. A top
+// Note: An incomming nil type stands for the top type. A top
 // type result is returned as nil.
 func intersect(x, y Type) (r Type) {
        defer func() {
index ae186a0b5d1efd402335101d6caa125ff277c3a2..ab0a4572767a6485ad65a9d32beb192272fac3d2 100644 (file)
@@ -284,6 +284,9 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
                        return true
                }
 
+       case *Union:
+               unimplemented()
+
        case *Interface:
                // Two interface types are identical if they have the same set of methods with
                // the same names and identical function types. Lower-case method names from
index 64a2dedc7d83d9972af155599757891b41e8a549..9fad52e22423ef5cb190a1e5d8e711294ddfe99f 100644 (file)
@@ -109,11 +109,11 @@ func (s sanitizer) typ(typ Type) Type {
        case *Sum:
                s.typeList(t.types)
 
+       case *Union:
+               s.typeList(t.terms)
+
        case *Interface:
                s.funcList(t.methods)
-               if types := s.typ(t.types); types != t.types {
-                       t.types = types
-               }
                s.typeList(t.embeddeds)
                s.funcList(t.allMethods)
                if allTypes := s.typ(t.allTypes); allTypes != t.allTypes {
@@ -153,7 +153,7 @@ func (s sanitizer) typ(typ Type) Type {
                s[t] = typ
 
        default:
-               panic("unimplemented")
+               unimplemented()
        }
 
        return typ
index 236feb0404ebbd84d894690d7227a352a29ba896..552f3488cd86ef4b03a719e4341f4f4f488d6235 100644 (file)
@@ -28,7 +28,8 @@ func TestSizeof(t *testing.T) {
                {Tuple{}, 12, 24},
                {Signature{}, 44, 88},
                {Sum{}, 12, 24},
-               {Interface{}, 60, 120},
+               {Union{}, 24, 48},
+               {Interface{}, 52, 104},
                {Map{}, 16, 32},
                {Chan{}, 12, 24},
                {Named{}, 68, 136},
index aa0fbf40fced43115bd43e899920214bd11e5b47..c6b807cd06599d959af1f27fb58eda02f0c0017a 100644 (file)
@@ -150,6 +150,8 @@ func (s *StdSizes) Sizeof(T Type) int64 {
                return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
        case *Sum:
                panic("Sizeof unimplemented for type sum")
+       case *Union:
+               unimplemented()
        case *Interface:
                return s.WordSize * 2
        }
index c8e428c1832311bed33fa8abf7965a01c3dce2a4..04a3527d6d7592b8f22f22985a339e4b75ba98e6 100644 (file)
@@ -299,15 +299,19 @@ func (subst *subster) typ(typ Type) Type {
                        return NewSum(types)
                }
 
+       case *Union:
+               terms, copied := subst.typeList(t.terms)
+               if copied {
+                       // TODO(gri) Do we need to remove duplicates that may have
+                       //           crept in after substitution? It may not matter.
+                       return newUnion(terms, t.tilde)
+               }
+
        case *Interface:
                methods, mcopied := subst.funcList(t.methods)
-               types := t.types
-               if t.types != nil {
-                       types = subst.typ(t.types)
-               }
                embeddeds, ecopied := subst.typeList(t.embeddeds)
-               if mcopied || types != t.types || ecopied {
-                       iface := &Interface{methods: methods, types: types, embeddeds: embeddeds}
+               if mcopied || ecopied {
+                       iface := &Interface{methods: methods, embeddeds: embeddeds}
                        if subst.check == nil {
                                panic("internal error: cannot instantiate interfaces yet")
                        }
index 80bf4ebb3dbfeda3ec8e897b244fe4b869d93d09..f051a4f2acb812bd511661ee35a90a4c0f8f69c9 100644 (file)
@@ -4,7 +4,7 @@
 
 // type declarations
 
-package decls0
+package go1_17 // don't permit non-interface elements in interfaces
 
 import "unsafe"
 
index 21aa208cc769d2b872dbf1c46cacf1e7ae9d2382..60d23b3c3bc51ec5b2a09d27ceb5ade9fef45dd7 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package issues
+package go1_17 // don't permit non-interface elements in interfaces
 
 import (
        "fmt"
index 6e2104a5150da8fead95d91dc7cdad447fc59f8d..1096bb42eb776ff8588af2af41da39888df2a684 100644 (file)
@@ -164,12 +164,12 @@ type _ interface {
 // for them to be all in a single list, and we report the error
 // as well.)
 type _ interface {
-       type int, int /* ERROR duplicate type int */
-       type /* ERROR multiple type lists */ int /* ERROR duplicate type int */
+       type int, int /* ERROR duplicate term int */
+       type /* ERROR multiple type lists */ int /* ERROR duplicate term int */
 }
 
 type _ interface {
-       type struct{f int}, struct{g int}, struct /* ERROR duplicate type */ {f int}
+       type struct{f int}, struct{g int}, struct /* ERROR duplicate term */ {f int}
 }
 
 // Interface type lists can contain any type, incl. *Named types.
diff --git a/src/cmd/compile/internal/types2/testdata/examples/constraints.go2 b/src/cmd/compile/internal/types2/testdata/examples/constraints.go2
new file mode 100644 (file)
index 0000000..e8b3912
--- /dev/null
@@ -0,0 +1,25 @@
+// 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.
+
+// This file shows some examples of generic constraint interfaces.
+
+package p
+
+type (
+       // Arbitrary types may be embedded like interfaces.
+       _ interface{int}
+       _ interface{~int}
+
+       // Types may be combined into a union.
+       _ interface{int|~string}
+
+       // Union terms must be unique independent of whether they are ~ or not.
+       _ interface{int|int /* ERROR duplicate term int */ }
+       _ interface{int|~ /* ERROR duplicate term int */ int }
+       _ interface{~int|~ /* ERROR duplicate term int */ int }
+
+       // For now we do not permit interfaces with ~ or in unions.
+       _ interface{~ /* ERROR cannot use interface */ interface{}}
+       _ interface{int|interface /* ERROR cannot use interface */ {}}
+)
index 2c1299feb09519d7499d09d937abf4abfb3f00bd..92ea305479572f88947c1bcfd7ee0f0930a98f2a 100644 (file)
@@ -36,7 +36,7 @@ func bar8[A foo8[A]](a A) {}
 func main8() {}
 
 // crash 9
-type foo9[A any] interface { type foo9 /* ERROR interface contains type constraints */ [A] }
+type foo9[A any] interface { type foo9 /* ERROR cannot use interface */ [A] }
 func _() { var _ = new(foo9 /* ERROR interface contains type constraints */ [int]) }
 
 // crash 12
index 316ab1982e89cf3635b1b1b16b3356f70a4d2b4f..301c13be41cce2b5fce7cec228cac0d9f1504f63 100644 (file)
@@ -4,11 +4,20 @@
 
 package p
 
-type Number interface {
-       int     /* ERROR int is not an interface */
-       float64 /* ERROR float64 is not an interface */
+type Number1 interface {
+       // embedding non-interface types is permitted
+       int
+       float64
 }
 
-func Add[T Number](a, b T) T {
+func Add1[T Number1](a, b T) T {
        return a /* ERROR not defined */ + b
 }
+
+type Number2 interface {
+       int|float64
+}
+
+func Add2[T Number2](a, b T) T {
+       return a + b
+}
index df621a4c1730db66648dd23c898a1238bdfce332..85eb0a78fe284a21b464b612bc7efafa8820b3ed 100644 (file)
@@ -7,5 +7,7 @@ package p
 // Do not report a duplicate type error for this type list.
 // (Check types after interfaces have been completed.)
 type _ interface {
-       type interface{ Error() string }, interface{ String() string }
+       // TODO(gri) Once we have full type sets we can enable this again.
+       // Fow now we don't permit interfaces in type lists.
+       // type interface{ Error() string }, interface{ String() string }
 }
index 55464e6b7759f09016050e541868a72b50e1dca1..61bc60678923428c6a51193510d32c9baae61885 100644 (file)
@@ -6,4 +6,4 @@ package p
 
 // A constraint must be an interface; it cannot
 // be a type parameter, for instance.
-func _[A interface{ type interface{} }, B A /* ERROR not an interface */ ]()
+func _[A interface{ type int }, B A /* ERROR not an interface */ ]()
index c2b460902cc643b5537834e489f3213f551fe6f7..6372397ed92392f6515744de114d3bb76145f967 100644 (file)
@@ -2,7 +2,13 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package p
+// TODO(gri) Eventually, once we disallow type lists, we need to
+//           adjust this code: for 1.17 we don't accept type parameters,
+//           and for 1.18 this code is valid.
+//           Leaving for now so we can see that existing errors
+//           are being reported.
+
+package go1_17 // don't permit non-interface elements in interfaces
 
 type T[P any] interface{
        P // ERROR P is a type parameter, not an interface
index e54f7601bee64ac5fac67889bd0a1cc247e181f7..79a8f3cd7f4b3f12580d2cbabb2778ab5d405ac5 100644 (file)
@@ -311,7 +311,6 @@ func (s *Sum) is(pred func(Type) bool) bool {
 // An Interface represents an interface type.
 type Interface struct {
        methods   []*Func // ordered list of explicitly declared methods
-       types     Type    // (possibly a Sum) type declared with a type list (TODO(gri) need better field name)
        embeddeds []Type  // ordered list of explicitly embedded types
 
        allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset)
index c534b041308e01f0d136f0fed89dfbebd94c9ebc..55858b7b42a26d1cb05c560ad6be17fbe6e35c5f 100644 (file)
@@ -158,11 +158,17 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
                writeSignature(buf, t, qf, visited)
 
        case *Sum:
-               for i, t := range t.types {
+               writeTypeList(buf, t.types, qf, visited)
+
+       case *Union:
+               for i, e := range t.terms {
                        if i > 0 {
-                               buf.WriteString("")
+                               buf.WriteString("|")
                        }
-                       writeType(buf, t, qf, visited)
+                       if t.tilde[i] {
+                               buf.WriteByte('~')
+                       }
+                       writeType(buf, e, qf, visited)
                }
 
        case *Interface:
@@ -207,14 +213,6 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
                                writeSignature(buf, m.typ.(*Signature), qf, visited)
                                empty = false
                        }
-                       if !empty && t.types != nil {
-                               buf.WriteString("; ")
-                       }
-                       if t.types != nil {
-                               buf.WriteString("type ")
-                               writeType(buf, t.types, qf, visited)
-                               empty = false
-                       }
                        if !empty && len(t.embeddeds) > 0 {
                                buf.WriteString("; ")
                        }
@@ -307,6 +305,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
 
        default:
                // For externally defined implementations of Type.
+               // Note: In this case cycles won't be caught.
                buf.WriteString(t.String())
        }
 }
index 618fdc0757d74334dd065f56b700fb89b92820ad..8d0ca760bfce8b53671670fc9d95f88707347e03 100644 (file)
@@ -91,7 +91,9 @@ var independentTestTypes = []testEntry{
        dup("interface{}"),
        dup("interface{m()}"),
        dup(`interface{String() string; m(int) float32}`),
-       dup(`interface{type int, float32, complex128}`),
+       {"interface{type int, float32, complex128}", "interface{~int|~float32|~complex128}"},
+       dup("interface{int|float32|complex128}"),
+       dup("interface{int|~float32|~complex128}"),
 
        // maps
        dup("map[string]int"),
index e1832bbb2a36232cd00a04e2e195ae51711938fe..f1630b75d0ed437ae21bc8e85471ec1a4cfe871f 100644 (file)
@@ -356,6 +356,9 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
                // This should not happen with the current internal use of sum types.
                panic("type inference across sum types not implemented")
 
+       case *Union:
+               unimplemented()
+
        case *Interface:
                // Two interface types are identical if they have the same set of methods with
                // the same names and identical function types. Lower-case method names from
diff --git a/src/cmd/compile/internal/types2/union.go b/src/cmd/compile/internal/types2/union.go
new file mode 100644 (file)
index 0000000..70dc3bc
--- /dev/null
@@ -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
+}