]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile: adding union support in types1
authorDan Scales <danscales@google.com>
Tue, 25 May 2021 03:36:42 +0000 (20:36 -0700)
committerDan Scales <danscales@google.com>
Wed, 26 May 2021 15:33:02 +0000 (15:33 +0000)
Add union support in types1, and allow exporting of unions, and
importing unions back into types1 and types2.

Added new test mincheck.go/mincheck.dir that tests that type lists (type
sets) are correctly exported/imported, so that types2 gives correct
errors that an instantiation doesn't fit the type list in the type param
constraint.

Change-Id: I8041c6c79289c870a95ed5a1b10e4c1c16985b12
Reviewed-on: https://go-review.googlesource.com/c/go/+/322609
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
13 files changed:
src/cmd/compile/internal/importer/iimport.go
src/cmd/compile/internal/noder/types.go
src/cmd/compile/internal/typecheck/iexport.go
src/cmd/compile/internal/typecheck/iimport.go
src/cmd/compile/internal/types/kind_string.go
src/cmd/compile/internal/types/size.go
src/cmd/compile/internal/types/type.go
src/cmd/compile/internal/types2/type.go
test/fixedbugs/bug195.go
test/fixedbugs/issue11614.go
test/typeparam/mincheck.dir/a.go [new file with mode: 0644]
test/typeparam/mincheck.dir/main.go [new file with mode: 0644]
test/typeparam/mincheck.go [new file with mode: 0644]

index 37e5113435c5692dad58f63cd2e256b33e830653..fd48bfc1796bb97cfbbf0bcbbcd5bc1c4d32209d 100644 (file)
@@ -68,6 +68,7 @@ const (
        interfaceType
        typeParamType
        instType
+       unionType
 )
 
 const io_SeekCurrent = 1 // io.SeekCurrent (not defined in Go 1.4)
@@ -660,6 +661,19 @@ func (r *importReader) doType(base *types2.Named) types2.Type {
                // we must always use the methods of the base (orig) type.
                t := types2.Instantiate(pos, baseType, targs)
                return t
+
+       case unionType:
+               if r.p.exportVersion < iexportVersionGenerics {
+                       errorf("unexpected instantiation type")
+               }
+               nt := int(r.uint64())
+               terms := make([]types2.Type, nt)
+               tildes := make([]bool, nt)
+               for i := range terms {
+                       terms[i] = r.typ()
+                       tildes[i] = r.bool()
+               }
+               return types2.NewUnion(terms, tildes)
        }
 }
 
index 16d664f5380f963280a8a78f7b2a749b7c201a19..c6e97d4206b15a718b2e6f7d80cbd15a1f93d6d2 100644 (file)
@@ -187,6 +187,9 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
                for i := range embeddeds {
                        // TODO(mdempsky): Get embedding position.
                        e := typ.EmbeddedType(i)
+
+                       // With Go 1.18, an embedded element can be any type, not
+                       // just an interface.
                        if t := types2.AsInterface(e); t != nil {
                                if t.IsComparable() {
                                        // Ignore predefined type 'comparable', since it
@@ -194,11 +197,9 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
                                        // relevant methods.
                                        continue
                                }
-                               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[j] = types.NewField(src.NoXPos, nil, g.typ1(e))
+                       j++
                }
                embeddeds = embeddeds[:j]
 
@@ -234,6 +235,16 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
                tp.SetBound(bound)
                return tp
 
+       case *types2.Union:
+               nt := typ.NumTerms()
+               tlist := make([]*types.Type, nt)
+               tildes := make([]bool, nt)
+               for i := range tlist {
+                       term, _ := typ.Term(i)
+                       tlist[i] = g.typ1(term)
+               }
+               return types.NewUnion(tlist, tildes)
+
        case *types2.Tuple:
                // Tuples are used for the type of a function call (i.e. the
                // return value of the function).
index 292bb2c409a638772c24a0ffcb9b4c280b72a32f..ea8e751852c61af80e11424ae42aa19a6287421d 100644 (file)
@@ -256,6 +256,7 @@ const (
        interfaceType
        typeParamType
        instType
+       unionType
 )
 
 const (
@@ -943,6 +944,18 @@ func (w *exportWriter) doTyp(t *types.Type) {
                        w.signature(f.Type)
                }
 
+       case types.TUNION:
+               // TODO(danscales): possibly put out the tilde bools in more
+               // compact form.
+               w.startType(unionType)
+               nt := t.NumTerms()
+               w.uint64(uint64(nt))
+               for i := 0; i < nt; i++ {
+                       t, b := t.Term(i)
+                       w.typ(t)
+                       w.bool(b)
+               }
+
        default:
                base.Fatalf("unexpected type: %v", t)
        }
index d5b549483d87df8694c20f01acac301f8d54f3b6..3fb675f824419bfaadfca328e1b16ce3572d36b9 100644 (file)
@@ -790,6 +790,19 @@ func (r *importReader) typ1() *types.Type {
                baseType := r.typ()
                t := Instantiate(pos, baseType, targs)
                return t
+
+       case unionType:
+               if r.p.exportVersion < iexportVersionGenerics {
+                       base.Fatalf("unexpected instantiation type")
+               }
+               nt := int(r.uint64())
+               terms := make([]*types.Type, nt)
+               tildes := make([]bool, nt)
+               for i := range terms {
+                       terms[i] = r.typ()
+                       tildes[i] = r.bool()
+               }
+               return types.NewUnion(terms, tildes)
        }
 }
 
index ae24a58b9219d483b710e69af65ce892bf7759e8..3e6a8bc064edf4cf0306d959eda9d07ca05346a5 100644 (file)
@@ -38,20 +38,21 @@ func _() {
        _ = x[TSTRING-27]
        _ = x[TUNSAFEPTR-28]
        _ = x[TTYPEPARAM-29]
-       _ = x[TIDEAL-30]
-       _ = x[TNIL-31]
-       _ = x[TBLANK-32]
-       _ = x[TFUNCARGS-33]
-       _ = x[TCHANARGS-34]
-       _ = x[TSSA-35]
-       _ = x[TTUPLE-36]
-       _ = x[TRESULTS-37]
-       _ = x[NTYPE-38]
+       _ = x[TUNION-30]
+       _ = x[TIDEAL-31]
+       _ = x[TNIL-32]
+       _ = x[TBLANK-33]
+       _ = x[TFUNCARGS-34]
+       _ = x[TCHANARGS-35]
+       _ = x[TSSA-36]
+       _ = x[TTUPLE-37]
+       _ = x[TRESULTS-38]
+       _ = x[NTYPE-39]
 }
 
-const _Kind_name = "xxxINT8UINT8INT16UINT16INT32UINT32INT64UINT64INTUINTUINTPTRCOMPLEX64COMPLEX128FLOAT32FLOAT64BOOLPTRFUNCSLICEARRAYSTRUCTCHANMAPINTERFORWANYSTRINGUNSAFEPTRTYPEPARAMIDEALNILBLANKFUNCARGSCHANARGSSSATUPLERESULTSNTYPE"
+const _Kind_name = "xxxINT8UINT8INT16UINT16INT32UINT32INT64UINT64INTUINTUINTPTRCOMPLEX64COMPLEX128FLOAT32FLOAT64BOOLPTRFUNCSLICEARRAYSTRUCTCHANMAPINTERFORWANYSTRINGUNSAFEPTRTYPEPARAMUNIONIDEALNILBLANKFUNCARGSCHANARGSSSATUPLERESULTSNTYPE"
 
-var _Kind_index = [...]uint8{0, 3, 7, 12, 17, 23, 28, 34, 39, 45, 48, 52, 59, 68, 78, 85, 92, 96, 99, 103, 108, 113, 119, 123, 126, 131, 135, 138, 144, 153, 162, 167, 170, 175, 183, 191, 194, 199, 206, 211}
+var _Kind_index = [...]uint8{0, 3, 7, 12, 17, 23, 28, 34, 39, 45, 48, 52, 59, 68, 78, 85, 92, 96, 99, 103, 108, 113, 119, 123, 126, 131, 135, 138, 144, 153, 162, 167, 172, 175, 180, 188, 196, 199, 204, 211, 216}
 
 func (i Kind) String() string {
        if i >= Kind(len(_Kind_index)-1) {
index f0e695ab964ac532dbeb3ad2bfdcae2e1e8479c2..7059eff398c008da653740ab68632a188a64714e 100644 (file)
@@ -104,8 +104,14 @@ func expandiface(t *Type) {
                        continue
                }
 
+               if m.Type.IsUnion() {
+                       continue
+               }
+
+               // Once we go to 1.18, then embedded types can be anything, but
+               // for now, just interfaces and unions.
                if !m.Type.IsInterface() {
-                       base.ErrorfAt(m.Pos, "interface contains embedded non-interface %v", m.Type)
+                       base.ErrorfAt(m.Pos, "interface contains embedded non-interface, non-union %v", m.Type)
                        m.SetBroke(true)
                        t.SetBroke(true)
                        // Add to fields so that error messages
@@ -405,6 +411,12 @@ func CalcSize(t *Type) {
                t.Align = uint8(PtrSize)
                expandiface(t)
 
+       case TUNION:
+               // Always part of an interface for now, so size/align don't matter.
+               // Pretend a union is represented like an interface.
+               w = 2 * int64(PtrSize)
+               t.Align = uint8(PtrSize)
+
        case TCHAN: // implemented as pointer
                w = int64(PtrSize)
 
index 3b0a9706f6580a51dbf65c6a0bea3c068f6ef4bf..e7831121bff1e19fe4b52b3fb802a59b7ef6393e 100644 (file)
@@ -73,6 +73,7 @@ const (
        TSTRING
        TUNSAFEPTR
        TTYPEPARAM
+       TUNION
 
        // pseudo-types for literals
        TIDEAL // untyped numeric constants
@@ -392,6 +393,12 @@ type Typeparam struct {
        bound *Type
 }
 
+// Union contains Type fields specific to union types.
+type Union struct {
+       terms  []*Type
+       tildes []bool // whether terms[i] is of form ~T
+}
+
 // Ptr contains Type fields specific to pointer types.
 type Ptr struct {
        Elem *Type // element type
@@ -574,6 +581,8 @@ func New(et Kind) *Type {
                t.Extra = new(Results)
        case TTYPEPARAM:
                t.Extra = new(Typeparam)
+       case TUNION:
+               t.Extra = new(Union)
        }
        return t
 }
@@ -1453,6 +1462,10 @@ func (t *Type) IsInterface() bool {
        return t.kind == TINTER
 }
 
+func (t *Type) IsUnion() bool {
+       return t.kind == TUNION
+}
+
 // IsEmptyInterface reports whether t is an empty interface type.
 func (t *Type) IsEmptyInterface() bool {
        return t.IsInterface() && t.AllMethods().Len() == 0
@@ -1811,6 +1824,32 @@ func (t *Type) Bound() *Type {
        return t.Extra.(*Typeparam).bound
 }
 
+// NewUnion returns a new union with the specified set of terms (types). If
+// tildes[i] is true, then terms[i] represents ~T, rather than just T.
+func NewUnion(terms []*Type, tildes []bool) *Type {
+       t := New(TUNION)
+       if len(terms) != len(tildes) {
+               base.Fatalf("Mismatched terms and tildes for NewUnion")
+       }
+       t.Extra.(*Union).terms = terms
+       t.Extra.(*Union).tildes = tildes
+       return t
+}
+
+// NumTerms returns the number of terms in a union type.
+func (t *Type) NumTerms() int {
+       t.wantEtype(TUNION)
+       return len(t.Extra.(*Union).terms)
+}
+
+// Term returns ith term of a union type as (term, tilde). If tilde is true, term
+// represents ~T, rather than just T.
+func (t *Type) Term(i int) (*Type, bool) {
+       t.wantEtype(TUNION)
+       u := t.Extra.(*Union)
+       return u.terms[i], u.tildes[i]
+}
+
 const BOGUS_FUNARG_OFFSET = -1000000000
 
 func unzeroFieldOffsets(f []*Field) {
index 79a8f3cd7f4b3f12580d2cbabb2778ab5d405ac5..2a93ca03885829369cf5c8b19892d38f60590b63 100644 (file)
@@ -368,9 +368,6 @@ func NewInterface(methods []*Func, embeddeds []*Named) *Interface {
 }
 
 // NewInterfaceType returns a new (incomplete) interface for the given methods and embedded types.
-// Each embedded type must have an underlying type of interface type (this property is not
-// verified for defined types, which may be in the process of being set up and which don't
-// have a valid underlying type yet).
 // NewInterfaceType takes ownership of the provided methods and may modify their types by setting
 // missing receivers. To compute the method set of the interface, Complete must be called.
 func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
@@ -386,16 +383,6 @@ func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
                }
        }
 
-       // All embedded types should be interfaces; however, defined types
-       // may not yet be fully resolved. Only verify that non-defined types
-       // are interfaces. This matches the behavior of the code before the
-       // fix for #25301 (issue #25596).
-       for _, t := range embeddeds {
-               if _, ok := t.(*Named); !ok && !IsInterface(t) {
-                       panic("embedded type is not an interface")
-               }
-       }
-
        // sort for API stability
        sortMethods(methods)
        sortTypes(embeddeds)
index 94f61fff7f1cb0a2b030b6ba7273b12e7f7b47b4..6d8578d6cb06049eaf2bdc115f0fae0832110394 100644 (file)
@@ -1,4 +1,4 @@
-// errorcheck
+// errorcheck -lang=go1.17
 
 // Copyright 2009 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
index de15f9827ffb28d1c18166c762349d0834de0422..6ea463b7fe92ffbbe0ebfc5d7fe8500caacec2a7 100644 (file)
@@ -1,4 +1,4 @@
-// errorcheck
+// errorcheck -lang=go1.17
 
 // Copyright 2015 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
diff --git a/test/typeparam/mincheck.dir/a.go b/test/typeparam/mincheck.dir/a.go
new file mode 100644 (file)
index 0000000..f1844bb
--- /dev/null
@@ -0,0 +1,16 @@
+// 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 a
+
+type Ordered interface {
+        type int, int64, float64
+}
+
+func Min[T Ordered](x, y T) T {
+        if x < y {
+                return x
+        }
+        return y
+}
diff --git a/test/typeparam/mincheck.dir/main.go b/test/typeparam/mincheck.dir/main.go
new file mode 100644 (file)
index 0000000..72d8eff
--- /dev/null
@@ -0,0 +1,38 @@
+// 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 main
+
+import (
+       "a"
+       "fmt"
+)
+
+func main() {
+       const want = 2
+       if got := a.Min[int](2, 3); got != want {
+               panic(fmt.Sprintf("got %d, want %d", got, want))
+       }
+
+       if got := a.Min(2, 3); got != want {
+               panic(fmt.Sprintf("want %d, got %d", want, got))
+       }
+
+       if got := a.Min[float64](3.5, 2.0); got != want {
+               panic(fmt.Sprintf("got %d, want %d", got, want))
+       }
+
+       if got := a.Min(3.5, 2.0); got != want {
+               panic(fmt.Sprintf("got %d, want %d", got, want))
+       }
+
+       const want2 = "ay"
+       if got := a.Min[string]("bb", "ay"); got != want2 { // ERROR "string does not satisfy interface{int|int64|float64}"
+               panic(fmt.Sprintf("got %d, want %d", got, want2))
+       }
+
+       if got := a.Min("bb", "ay"); got != want2 { // ERROR "string does not satisfy interface{int|int64|float64}"
+               panic(fmt.Sprintf("got %d, want %d", got, want2))
+       }
+}
diff --git a/test/typeparam/mincheck.go b/test/typeparam/mincheck.go
new file mode 100644 (file)
index 0000000..32cf4b8
--- /dev/null
@@ -0,0 +1,7 @@
+// errorcheckdir -G=3
+
+// 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 ignored