mirror of
https://github.com/golang/go
synced 2024-09-04 23:44:16 +00:00
[dev.typeparams] cmd/compile: adding union support in types1
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>
This commit is contained in:
parent
6c9e1c58bc
commit
fd54ae8b0c
|
@ -68,6 +68,7 @@ const (
|
||||||
interfaceType
|
interfaceType
|
||||||
typeParamType
|
typeParamType
|
||||||
instType
|
instType
|
||||||
|
unionType
|
||||||
)
|
)
|
||||||
|
|
||||||
const io_SeekCurrent = 1 // io.SeekCurrent (not defined in Go 1.4)
|
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.
|
// we must always use the methods of the base (orig) type.
|
||||||
t := types2.Instantiate(pos, baseType, targs)
|
t := types2.Instantiate(pos, baseType, targs)
|
||||||
return t
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -187,6 +187,9 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
|
||||||
for i := range embeddeds {
|
for i := range embeddeds {
|
||||||
// TODO(mdempsky): Get embedding position.
|
// TODO(mdempsky): Get embedding position.
|
||||||
e := typ.EmbeddedType(i)
|
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 := types2.AsInterface(e); t != nil {
|
||||||
if t.IsComparable() {
|
if t.IsComparable() {
|
||||||
// Ignore predefined type 'comparable', since it
|
// Ignore predefined type 'comparable', since it
|
||||||
|
@ -194,11 +197,9 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
|
||||||
// relevant methods.
|
// relevant methods.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
embeddeds[j] = types.NewField(src.NoXPos, nil, g.typ1(e))
|
|
||||||
j++
|
|
||||||
}
|
}
|
||||||
// Ignore embedded non-interface types - they correspond
|
embeddeds[j] = types.NewField(src.NoXPos, nil, g.typ1(e))
|
||||||
// to type lists which we currently don't handle here.
|
j++
|
||||||
}
|
}
|
||||||
embeddeds = embeddeds[:j]
|
embeddeds = embeddeds[:j]
|
||||||
|
|
||||||
|
@ -234,6 +235,16 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
|
||||||
tp.SetBound(bound)
|
tp.SetBound(bound)
|
||||||
return tp
|
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:
|
case *types2.Tuple:
|
||||||
// Tuples are used for the type of a function call (i.e. the
|
// Tuples are used for the type of a function call (i.e. the
|
||||||
// return value of the function).
|
// return value of the function).
|
||||||
|
|
|
@ -256,6 +256,7 @@ const (
|
||||||
interfaceType
|
interfaceType
|
||||||
typeParamType
|
typeParamType
|
||||||
instType
|
instType
|
||||||
|
unionType
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -943,6 +944,18 @@ func (w *exportWriter) doTyp(t *types.Type) {
|
||||||
w.signature(f.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:
|
default:
|
||||||
base.Fatalf("unexpected type: %v", t)
|
base.Fatalf("unexpected type: %v", t)
|
||||||
}
|
}
|
||||||
|
|
|
@ -790,6 +790,19 @@ func (r *importReader) typ1() *types.Type {
|
||||||
baseType := r.typ()
|
baseType := r.typ()
|
||||||
t := Instantiate(pos, baseType, targs)
|
t := Instantiate(pos, baseType, targs)
|
||||||
return t
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,20 +38,21 @@ func _() {
|
||||||
_ = x[TSTRING-27]
|
_ = x[TSTRING-27]
|
||||||
_ = x[TUNSAFEPTR-28]
|
_ = x[TUNSAFEPTR-28]
|
||||||
_ = x[TTYPEPARAM-29]
|
_ = x[TTYPEPARAM-29]
|
||||||
_ = x[TIDEAL-30]
|
_ = x[TUNION-30]
|
||||||
_ = x[TNIL-31]
|
_ = x[TIDEAL-31]
|
||||||
_ = x[TBLANK-32]
|
_ = x[TNIL-32]
|
||||||
_ = x[TFUNCARGS-33]
|
_ = x[TBLANK-33]
|
||||||
_ = x[TCHANARGS-34]
|
_ = x[TFUNCARGS-34]
|
||||||
_ = x[TSSA-35]
|
_ = x[TCHANARGS-35]
|
||||||
_ = x[TTUPLE-36]
|
_ = x[TSSA-36]
|
||||||
_ = x[TRESULTS-37]
|
_ = x[TTUPLE-37]
|
||||||
_ = x[NTYPE-38]
|
_ = 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 {
|
func (i Kind) String() string {
|
||||||
if i >= Kind(len(_Kind_index)-1) {
|
if i >= Kind(len(_Kind_index)-1) {
|
||||||
|
|
|
@ -104,8 +104,14 @@ func expandiface(t *Type) {
|
||||||
continue
|
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() {
|
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)
|
m.SetBroke(true)
|
||||||
t.SetBroke(true)
|
t.SetBroke(true)
|
||||||
// Add to fields so that error messages
|
// Add to fields so that error messages
|
||||||
|
@ -405,6 +411,12 @@ func CalcSize(t *Type) {
|
||||||
t.Align = uint8(PtrSize)
|
t.Align = uint8(PtrSize)
|
||||||
expandiface(t)
|
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
|
case TCHAN: // implemented as pointer
|
||||||
w = int64(PtrSize)
|
w = int64(PtrSize)
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,7 @@ const (
|
||||||
TSTRING
|
TSTRING
|
||||||
TUNSAFEPTR
|
TUNSAFEPTR
|
||||||
TTYPEPARAM
|
TTYPEPARAM
|
||||||
|
TUNION
|
||||||
|
|
||||||
// pseudo-types for literals
|
// pseudo-types for literals
|
||||||
TIDEAL // untyped numeric constants
|
TIDEAL // untyped numeric constants
|
||||||
|
@ -392,6 +393,12 @@ type Typeparam struct {
|
||||||
bound *Type
|
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.
|
// Ptr contains Type fields specific to pointer types.
|
||||||
type Ptr struct {
|
type Ptr struct {
|
||||||
Elem *Type // element type
|
Elem *Type // element type
|
||||||
|
@ -574,6 +581,8 @@ func New(et Kind) *Type {
|
||||||
t.Extra = new(Results)
|
t.Extra = new(Results)
|
||||||
case TTYPEPARAM:
|
case TTYPEPARAM:
|
||||||
t.Extra = new(Typeparam)
|
t.Extra = new(Typeparam)
|
||||||
|
case TUNION:
|
||||||
|
t.Extra = new(Union)
|
||||||
}
|
}
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
@ -1453,6 +1462,10 @@ func (t *Type) IsInterface() bool {
|
||||||
return t.kind == TINTER
|
return t.kind == TINTER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Type) IsUnion() bool {
|
||||||
|
return t.kind == TUNION
|
||||||
|
}
|
||||||
|
|
||||||
// IsEmptyInterface reports whether t is an empty interface type.
|
// IsEmptyInterface reports whether t is an empty interface type.
|
||||||
func (t *Type) IsEmptyInterface() bool {
|
func (t *Type) IsEmptyInterface() bool {
|
||||||
return t.IsInterface() && t.AllMethods().Len() == 0
|
return t.IsInterface() && t.AllMethods().Len() == 0
|
||||||
|
@ -1811,6 +1824,32 @@ func (t *Type) Bound() *Type {
|
||||||
return t.Extra.(*Typeparam).bound
|
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
|
const BOGUS_FUNARG_OFFSET = -1000000000
|
||||||
|
|
||||||
func unzeroFieldOffsets(f []*Field) {
|
func unzeroFieldOffsets(f []*Field) {
|
||||||
|
|
|
@ -368,9 +368,6 @@ func NewInterface(methods []*Func, embeddeds []*Named) *Interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInterfaceType returns a new (incomplete) interface for the given methods and embedded types.
|
// 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
|
// 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.
|
// missing receivers. To compute the method set of the interface, Complete must be called.
|
||||||
func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
|
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
|
// sort for API stability
|
||||||
sortMethods(methods)
|
sortMethods(methods)
|
||||||
sortTypes(embeddeds)
|
sortTypes(embeddeds)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// errorcheck
|
// errorcheck -lang=go1.17
|
||||||
|
|
||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// errorcheck
|
// errorcheck -lang=go1.17
|
||||||
|
|
||||||
// Copyright 2015 The Go Authors. All rights reserved.
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
|
|
16
test/typeparam/mincheck.dir/a.go
Normal file
16
test/typeparam/mincheck.dir/a.go
Normal file
|
@ -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
|
||||||
|
}
|
38
test/typeparam/mincheck.dir/main.go
Normal file
38
test/typeparam/mincheck.dir/main.go
Normal file
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
7
test/typeparam/mincheck.go
Normal file
7
test/typeparam/mincheck.go
Normal file
|
@ -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
|
Loading…
Reference in a new issue