1
0
mirror of https://github.com/golang/go synced 2024-11-11 23:50:22 -07:00

[dev.typeparams] cmd/compile: implement generic type switches

Add a new dynamicType node, which is used as a case entry when
the type being switched to is generic.

Change-Id: Ice77c6f224b8fdd3ff574fdf4a8ea5f6c7ddbe75
Reviewed-on: https://go-review.googlesource.com/c/go/+/339429
Trust: Keith Randall <khr@golang.org>
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Keith Randall <khr@golang.org>
Reviewed-by: Dan Scales <danscales@google.com>
This commit is contained in:
Keith Randall 2021-08-03 08:10:17 -07:00
parent 57668b84ff
commit ca3c6985cd
21 changed files with 342 additions and 18 deletions

View File

@ -262,6 +262,9 @@ func (e *escape) exprSkipInit(k hole, n ir.Node) {
// Arguments of OADDSTR never escape;
// runtime.concatstrings makes sure of that.
e.discards(n.List)
case ir.ODYNAMICTYPE:
// Nothing to do - argument is a *runtime._type (+ maybe a *runtime.itab) pointing to static data section
}
}

View File

@ -700,6 +700,15 @@ func NewDynamicTypeAssertExpr(pos src.XPos, op Op, x, t Node) *DynamicTypeAssert
return n
}
func (n *DynamicTypeAssertExpr) SetOp(op Op) {
switch op {
default:
panic(n.no("SetOp " + op.String()))
case ODYNAMICDOTTYPE, ODYNAMICDOTTYPE2:
n.op = op
}
}
// A UnaryExpr is a unary expression Op X,
// or Op(X) for a builtin function that does not end up being a call.
type UnaryExpr struct {

View File

@ -258,7 +258,8 @@ const (
OBREAK // break [Label]
// OCASE: case List: Body (List==nil means default)
// For OTYPESW, List is a OTYPE node for the specified type (or OLITERAL
// for nil), and, if a type-switch variable is specified, Rlist is an
// for nil) or an ODYNAMICTYPE indicating a runtime type for generics.
// If a type-switch variable is specified, Var is an
// ONAME for the version of the type-switch variable with the specified
// type.
OCASE
@ -320,8 +321,9 @@ const (
OLINKSYMOFFSET // offset within a name
// opcodes for generics
ODYNAMICDOTTYPE
ODYNAMICDOTTYPE2
ODYNAMICDOTTYPE // x = i.(T) where T is a type parameter (or derived from a type parameter)
ODYNAMICDOTTYPE2 // x, ok = i.(T) where T is a type parameter (or derived from a type parameter)
ODYNAMICTYPE // a type node for type switches (represents a dynamic target type for a type switch)
// arch-specific opcodes
OTAILCALL // tail call to another function

View File

@ -463,6 +463,34 @@ func (n *Decl) editChildren(edit func(Node) Node) {
}
}
func (n *DynamicType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
func (n *DynamicType) copy() Node {
c := *n
c.init = copyNodes(c.init)
return &c
}
func (n *DynamicType) doChildren(do func(Node) bool) bool {
if doNodes(n.init, do) {
return true
}
if n.X != nil && do(n.X) {
return true
}
if n.ITab != nil && do(n.ITab) {
return true
}
return false
}
func (n *DynamicType) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
if n.X != nil {
n.X = edit(n.X).(Node)
}
if n.ITab != nil {
n.ITab = edit(n.ITab).(Node)
}
}
func (n *DynamicTypeAssertExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
func (n *DynamicTypeAssertExpr) copy() Node {
c := *n

View File

@ -164,16 +164,17 @@ func _() {
_ = x[OLINKSYMOFFSET-153]
_ = x[ODYNAMICDOTTYPE-154]
_ = x[ODYNAMICDOTTYPE2-155]
_ = x[OTAILCALL-156]
_ = x[OGETG-157]
_ = x[OGETCALLERPC-158]
_ = x[OGETCALLERSP-159]
_ = x[OEND-160]
_ = x[ODYNAMICTYPE-156]
_ = x[OTAILCALL-157]
_ = x[OGETG-158]
_ = x[OGETCALLERPC-159]
_ = x[OGETCALLERSP-160]
_ = x[OEND-161]
}
const _Op_name = "XXXNAMENONAMETYPEPACKLITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVIDATACONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECOVERFPRECVRUNESTRSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFUNSAFEADDUNSAFESLICEMETHEXPRMETHVALUEBLOCKBREAKCASECONTINUEDEFERFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTTCHANTMAPTSTRUCTTINTERTFUNCTARRAYTSLICEINLCALLEFACEITABIDATASPTRCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKLINKSYMOFFSETDYNAMICDOTTYPEDYNAMICDOTTYPE2TAILCALLGETGGETCALLERPCGETCALLERSPEND"
const _Op_name = "XXXNAMENONAMETYPEPACKLITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVIDATACONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECOVERFPRECVRUNESTRSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFUNSAFEADDUNSAFESLICEMETHEXPRMETHVALUEBLOCKBREAKCASECONTINUEDEFERFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTTCHANTMAPTSTRUCTTINTERTFUNCTARRAYTSLICEINLCALLEFACEITABIDATASPTRCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKLINKSYMOFFSETDYNAMICDOTTYPEDYNAMICDOTTYPE2DYNAMICTYPETAILCALLGETGGETCALLERPCGETCALLERSPEND"
var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 37, 39, 42, 48, 52, 58, 64, 73, 85, 94, 103, 115, 124, 136, 138, 141, 151, 158, 165, 172, 176, 180, 188, 196, 205, 208, 213, 220, 227, 233, 242, 250, 258, 264, 268, 277, 286, 293, 297, 300, 307, 315, 322, 328, 331, 337, 344, 352, 356, 363, 371, 373, 375, 377, 379, 381, 383, 388, 393, 401, 404, 413, 416, 420, 428, 435, 444, 457, 460, 463, 466, 469, 472, 475, 481, 484, 487, 493, 497, 500, 504, 509, 514, 520, 525, 529, 534, 542, 550, 556, 565, 576, 583, 592, 596, 603, 611, 615, 619, 623, 630, 637, 645, 651, 660, 671, 679, 688, 693, 698, 702, 710, 715, 719, 722, 730, 734, 736, 741, 743, 748, 754, 760, 766, 772, 780, 785, 789, 796, 802, 807, 813, 819, 826, 831, 835, 840, 844, 849, 857, 863, 870, 877, 883, 890, 903, 917, 932, 940, 944, 955, 966, 969}
var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 37, 39, 42, 48, 52, 58, 64, 73, 85, 94, 103, 115, 124, 136, 138, 141, 151, 158, 165, 172, 176, 180, 188, 196, 205, 208, 213, 220, 227, 233, 242, 250, 258, 264, 268, 277, 286, 293, 297, 300, 307, 315, 322, 328, 331, 337, 344, 352, 356, 363, 371, 373, 375, 377, 379, 381, 383, 388, 393, 401, 404, 413, 416, 420, 428, 435, 444, 457, 460, 463, 466, 469, 472, 475, 481, 484, 487, 493, 497, 500, 504, 509, 514, 520, 525, 529, 534, 542, 550, 556, 565, 576, 583, 592, 596, 603, 611, 615, 619, 623, 630, 637, 645, 651, 660, 671, 679, 688, 693, 698, 702, 710, 715, 719, 722, 730, 734, 736, 741, 743, 748, 754, 760, 766, 772, 780, 785, 789, 796, 802, 807, 813, 819, 826, 831, 835, 840, 844, 849, 857, 863, 870, 877, 883, 890, 903, 917, 932, 943, 951, 955, 966, 977, 980}
func (i Op) String() string {
if i >= Op(len(_Op_index)-1) {

View File

@ -319,3 +319,17 @@ func TypeNodeAt(pos src.XPos, t *types.Type) Ntype {
}
return newTypeNode(pos, t)
}
// A DynamicType represents the target type in a type switch.
type DynamicType struct {
miniExpr
X Node // a *runtime._type for the targeted type
ITab Node // for type switches from nonempty interfaces to non-interfaces, this is the itab for that pair.
}
func NewDynamicType(pos src.XPos, x Node) *DynamicType {
n := &DynamicType{X: x}
n.pos = pos
n.op = ODYNAMICTYPE
return n
}

View File

@ -107,6 +107,10 @@ type gfInfo struct {
// Nodes in generic functions that are a conversion from a typeparam/derived
// type to a specific interface.
itabConvs []ir.Node
// For type switches on nonempty interfaces, a map from OTYPE entries of
// HasTParam type, to the interface type we're switching from.
// TODO: what if the type we're switching from is a shape type?
type2switchType map[ir.Node]*types.Type
}
// instInfo is information gathered on an gcshape (or fully concrete)

View File

@ -1140,6 +1140,38 @@ func (subst *subster) node(n ir.Node) ir.Node {
m = ir.NewDynamicTypeAssertExpr(dt.Pos(), op, dt.X, rt)
m.SetType(dt.Type())
m.SetTypecheck(1)
case ir.OCASE:
if _, ok := x.(*ir.CommClause); ok {
// This is not a type switch. TODO: Should we use an OSWITCH case here instead of OCASE?
break
}
x := x.(*ir.CaseClause)
m := m.(*ir.CaseClause)
for i, c := range x.List {
if c.Op() == ir.OTYPE && c.Type().HasTParam() {
// Use a *runtime._type for the dynamic type.
ix := findDictType(subst.info, c.Type())
assert(ix >= 0)
dt := ir.NewDynamicType(c.Pos(), getDictionaryEntry(c.Pos(), subst.info.dictParam, ix, subst.info.dictLen))
// For type switch from nonemoty interfaces to non-interfaces, we need an itab as well.
if _, ok := subst.info.gfInfo.type2switchType[c]; ok {
// Type switch from nonempty interface. We need a *runtime.itab
// for the dynamic type.
ix := -1
for i, ic := range subst.info.gfInfo.itabConvs {
if ic == c {
ix = subst.info.startItabConv + i
break
}
}
assert(ix >= 0)
dt.ITab = getDictionaryEntry(c.Pos(), subst.info.dictParam, ix, subst.info.dictLen)
}
typed(m.List[i].Type(), dt)
m.List[i] = dt
}
}
}
return m
}
@ -1483,6 +1515,9 @@ func (g *irgen) finalizeSyms() {
case ir.OCONVIFACE:
srctype = subst.Typ(n.(*ir.ConvExpr).X.Type())
dsttype = subst.Typ(n.Type())
case ir.OTYPE:
srctype = subst.Typ(n.Type())
dsttype = subst.Typ(info.type2switchType[n])
default:
base.Fatalf("itab entry with unknown op %s", n.Op())
}
@ -1652,6 +1687,21 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo {
ir.Visit(n1, visitFunc)
}
}
if n.Op() == ir.OSWITCH && n.(*ir.SwitchStmt).Tag != nil && n.(*ir.SwitchStmt).Tag.Op() == ir.OTYPESW && !n.(*ir.SwitchStmt).Tag.(*ir.TypeSwitchGuard).X.Type().IsEmptyInterface() {
for _, cc := range n.(*ir.SwitchStmt).Cases {
for _, c := range cc.List {
if c.Op() == ir.OTYPE && c.Type().HasTParam() {
// Type switch from a non-empty interface to a noninterface.
infoPrint(" Itab for type switch: %v\n", c)
info.itabConvs = append(info.itabConvs, c)
if info.type2switchType == nil {
info.type2switchType = map[ir.Node]*types.Type{}
}
info.type2switchType[c] = n.(*ir.SwitchStmt).Tag.(*ir.TypeSwitchGuard).X.Type()
}
}
}
}
addType(&info, n, n.Type())
}

View File

@ -313,6 +313,10 @@ assignOK:
r := r.(*ir.TypeAssertExpr)
stmt.SetOp(ir.OAS2DOTTYPE)
r.SetOp(ir.ODOTTYPE2)
case ir.ODYNAMICDOTTYPE:
r := r.(*ir.DynamicTypeAssertExpr)
stmt.SetOp(ir.OAS2DOTTYPE)
r.SetOp(ir.ODYNAMICDOTTYPE2)
default:
break assignOK
}

View File

@ -172,6 +172,10 @@ assignOK:
r := r.(*ir.TypeAssertExpr)
stmt.SetOp(ir.OAS2DOTTYPE)
r.SetOp(ir.ODOTTYPE2)
case ir.ODYNAMICDOTTYPE:
r := r.(*ir.DynamicTypeAssertExpr)
stmt.SetOp(ir.OAS2DOTTYPE)
r.SetOp(ir.ODYNAMICDOTTYPE2)
default:
break assignOK
}

View File

@ -360,10 +360,10 @@ func walkSwitchType(sw *ir.SwitchStmt) {
}
if singleType != nil && singleType.IsInterface() {
s.Add(ncase.Pos(), n1.Type(), caseVar, jmp)
s.Add(ncase.Pos(), n1, caseVar, jmp)
caseVarInitialized = true
} else {
s.Add(ncase.Pos(), n1.Type(), nil, jmp)
s.Add(ncase.Pos(), n1, nil, jmp)
}
}
@ -377,6 +377,17 @@ func walkSwitchType(sw *ir.SwitchStmt) {
}
val = ifaceData(ncase.Pos(), s.facename, singleType)
}
if len(ncase.List) == 1 && ncase.List[0].Op() == ir.ODYNAMICTYPE {
dt := ncase.List[0].(*ir.DynamicType)
x := ir.NewDynamicTypeAssertExpr(ncase.Pos(), ir.ODYNAMICDOTTYPE, val, dt.X)
if dt.ITab != nil {
// TODO: make ITab a separate field in DynamicTypeAssertExpr?
x.T = dt.ITab
}
x.SetType(caseVar.Type())
x.SetTypecheck(1)
val = x
}
l := []ir.Node{
ir.NewDecl(ncase.Pos(), ir.ODCL, caseVar),
ir.NewAssignStmt(ncase.Pos(), caseVar, val),
@ -446,7 +457,8 @@ type typeClause struct {
body ir.Nodes
}
func (s *typeSwitch) Add(pos src.XPos, typ *types.Type, caseVar *ir.Name, jmp ir.Node) {
func (s *typeSwitch) Add(pos src.XPos, n1 ir.Node, caseVar *ir.Name, jmp ir.Node) {
typ := n1.Type()
var body ir.Nodes
if caseVar != nil {
l := []ir.Node{
@ -462,9 +474,25 @@ func (s *typeSwitch) Add(pos src.XPos, typ *types.Type, caseVar *ir.Name, jmp ir
// cv, ok = iface.(type)
as := ir.NewAssignListStmt(pos, ir.OAS2, nil, nil)
as.Lhs = []ir.Node{caseVar, s.okname} // cv, ok =
switch n1.Op() {
case ir.OTYPE:
// Static type assertion (non-generic)
dot := ir.NewTypeAssertExpr(pos, s.facename, nil)
dot.SetType(typ) // iface.(type)
as.Rhs = []ir.Node{dot}
case ir.ODYNAMICTYPE:
// Dynamic type assertion (generic)
dt := n1.(*ir.DynamicType)
dot := ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, s.facename, dt.X)
if dt.ITab != nil {
dot.T = dt.ITab
}
dot.SetType(typ)
dot.SetTypecheck(1)
as.Rhs = []ir.Node{dot}
default:
base.Fatalf("unhandled type case %s", n1.Op())
}
appendWalkStmt(&body, as)
// if ok { goto label }
@ -473,9 +501,10 @@ func (s *typeSwitch) Add(pos src.XPos, typ *types.Type, caseVar *ir.Name, jmp ir
nif.Body = []ir.Node{jmp}
body.Append(nif)
if !typ.IsInterface() {
if n1.Op() == ir.OTYPE && !typ.IsInterface() {
// Defer static, noninterface cases so they can be binary searched by hash.
s.clauses = append(s.clauses, typeClause{
hash: types.TypeHash(typ),
hash: types.TypeHash(n1.Type()),
body: body,
})
return

View File

@ -0,0 +1,29 @@
// run -gcflags=-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 main
func f[T any](i interface{}) {
switch i.(type) {
case T:
println("T")
case int:
println("int")
case int32, int16:
println("int32/int16")
case struct { a, b T }:
println("struct{T,T}")
default:
println("other")
}
}
func main() {
f[float64](float64(6))
f[float64](int(7))
f[float64](int32(8))
f[float64](struct{a, b float64}{a:1, b:2})
f[float64](int8(9))
}

View File

@ -0,0 +1,5 @@
T
int
int32/int16
struct{T,T}
other

View File

@ -0,0 +1,31 @@
// run -gcflags=-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 main
import "reflect"
func f[T any](i interface{}) {
switch x := i.(type) {
case T:
println("T", x)
case int:
println("int", x)
case int32, int16:
println("int32/int16", reflect.ValueOf(x).Int())
case struct { a, b T }:
println("struct{T,T}", x.a, x.b)
default:
println("other", reflect.ValueOf(x).Int())
}
}
func main() {
f[float64](float64(6))
f[float64](int(7))
f[float64](int32(8))
f[float64](struct{a, b float64}{a:1, b:2})
f[float64](int8(9))
}

View File

@ -0,0 +1,5 @@
T +6.000000e+000
int 7
int32/int16 8
struct{T,T} +1.000000e+000 +2.000000e+000
other 9

View File

@ -0,0 +1,35 @@
// run -gcflags=-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 main
type I interface { foo() int }
type myint int
func (x myint) foo() int { return int(x) }
type myfloat float64
func (x myfloat) foo() int { return int(x) }
type myint32 int32
func (x myint32) foo() int { return int(x) }
func f[T I](i I) {
switch x := i.(type) {
case T:
println("T", x.foo())
case myint:
println("myint", x.foo())
default:
println("other", x.foo())
}
}
func main() {
f[myfloat](myint(6))
f[myfloat](myfloat(7))
f[myfloat](myint32(8))
}

View File

@ -0,0 +1,3 @@
myint 6
T 7
other 8

View File

@ -0,0 +1,33 @@
// run -gcflags=-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 main
type I interface { foo() int }
type myint int
func (x myint) foo() int {return int(x)}
type myfloat float64
func (x myfloat) foo() int {return int(x)}
type myint32 int32
func (x myint32) foo() int { return int(x) }
func f[T I](i I) {
switch x := i.(type) {
case T, myint32:
println("T/myint32", x.foo())
default:
println("other", x.foo())
}
}
func main() {
f[myfloat](myint(6))
f[myfloat](myfloat(7))
f[myfloat](myint32(8))
}

View File

@ -0,0 +1,3 @@
other 6
T/myint32 7
T/myint32 8

View File

@ -0,0 +1,28 @@
// run -gcflags=-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 main
type myint int
func (x myint) foo() int {return int(x)}
type myfloat float64
func (x myfloat) foo() float64 {return float64(x) }
func f[T any](i interface{}) {
switch x := i.(type) {
case interface { foo() T }:
println("fooer", x.foo())
default:
println("other")
}
}
func main() {
f[int](myint(6))
f[int](myfloat(7))
f[float64](myint(8))
f[float64](myfloat(9))
}

View File

@ -0,0 +1,4 @@
fooer 6
other
other
fooer +9.000000e+000