mirror of
https://github.com/golang/go
synced 2024-09-04 15:34:21 +00:00
[dev.typeparams] cmd/compile: fixing case where type arg is an interface
In this case, we can't use an itab for doing a bound call, since we're converting from an interface to an interface. We do a static or dynamic type assert in new function assertToBound(). The dynamic type assert in assertToBound() is only needed if a bound is parameterized. In that case, we must do a dynamic type assert, and therefore need a dictionary entry for the type bound (see change in getGfInfo). I'm not sure if we can somehow limit this case, since using an interface as a type arg AND having the type bound of the type arg be parameterized is a very unlikely case. Had to add the TUNION case to parameterizedBy1() (which is only used for extra checking). Added a bunch of these test cases to 13.go, which now passes. Change-Id: Ic22eed637fa879b5bbb46d36b40aaad6f90b9d01 Reviewed-on: https://go-review.googlesource.com/c/go/+/339898 Trust: Dan Scales <danscales@google.com> Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
parent
1b708c0260
commit
3cdf8b429e
|
@ -1181,7 +1181,7 @@ func (subst *subster) node(n ir.Node) ir.Node {
|
||||||
// The only dot on a shape type value are methods.
|
// The only dot on a shape type value are methods.
|
||||||
if mse.X.Op() == ir.OTYPE {
|
if mse.X.Op() == ir.OTYPE {
|
||||||
// Method expression T.M
|
// Method expression T.M
|
||||||
m = subst.g.buildClosure2(subst.newf, subst.info, m, x)
|
m = subst.g.buildClosure2(subst, m, x)
|
||||||
// No need for transformDot - buildClosure2 has already
|
// No need for transformDot - buildClosure2 has already
|
||||||
// transformed to OCALLINTER/ODOTINTER.
|
// transformed to OCALLINTER/ODOTINTER.
|
||||||
} else {
|
} else {
|
||||||
|
@ -1189,11 +1189,18 @@ func (subst *subster) node(n ir.Node) ir.Node {
|
||||||
// 1) convert x to the bound interface
|
// 1) convert x to the bound interface
|
||||||
// 2) call M on that interface
|
// 2) call M on that interface
|
||||||
gsrc := x.(*ir.SelectorExpr).X.Type()
|
gsrc := x.(*ir.SelectorExpr).X.Type()
|
||||||
dst := gsrc.Bound()
|
bound := gsrc.Bound()
|
||||||
|
dst := bound
|
||||||
if dst.HasTParam() {
|
if dst.HasTParam() {
|
||||||
dst = subst.ts.Typ(dst)
|
dst = subst.ts.Typ(dst)
|
||||||
}
|
}
|
||||||
mse.X = convertUsingDictionary(subst.info, subst.info.dictParam, m.Pos(), mse.X, x, dst, gsrc)
|
if src.IsInterface() {
|
||||||
|
// If type arg is an interface (unusual case),
|
||||||
|
// we do a type assert to the type bound.
|
||||||
|
mse.X = assertToBound(subst.info, subst.info.dictParam, m.Pos(), mse.X, bound, dst)
|
||||||
|
} else {
|
||||||
|
mse.X = convertUsingDictionary(subst.info, subst.info.dictParam, m.Pos(), mse.X, x, dst, gsrc)
|
||||||
|
}
|
||||||
transformDot(mse, false)
|
transformDot(mse, false)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1554,10 +1561,10 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool)
|
||||||
tparam := tmpse.X.Type()
|
tparam := tmpse.X.Type()
|
||||||
assert(tparam.IsTypeParam())
|
assert(tparam.IsTypeParam())
|
||||||
recvType := targs[tparam.Index()]
|
recvType := targs[tparam.Index()]
|
||||||
if len(recvType.RParams()) == 0 {
|
if recvType.IsInterface() || len(recvType.RParams()) == 0 {
|
||||||
// No sub-dictionary entry is
|
// No sub-dictionary entry is
|
||||||
// actually needed, since the
|
// actually needed, since the
|
||||||
// typeparam is not an
|
// type arg is not an
|
||||||
// instantiated type that
|
// instantiated type that
|
||||||
// will have generic methods.
|
// will have generic methods.
|
||||||
break
|
break
|
||||||
|
@ -1686,8 +1693,14 @@ func (g *irgen) finalizeSyms() {
|
||||||
default:
|
default:
|
||||||
base.Fatalf("itab entry with unknown op %s", n.Op())
|
base.Fatalf("itab entry with unknown op %s", n.Op())
|
||||||
}
|
}
|
||||||
itabLsym := reflectdata.ITabLsym(srctype, dsttype)
|
if srctype.IsInterface() {
|
||||||
d.off = objw.SymPtr(lsym, d.off, itabLsym, 0)
|
// No itab is wanted if src type is an interface. We
|
||||||
|
// will use a type assert instead.
|
||||||
|
d.off = objw.Uintptr(lsym, d.off, 0)
|
||||||
|
} else {
|
||||||
|
itabLsym := reflectdata.ITabLsym(srctype, dsttype)
|
||||||
|
d.off = objw.SymPtr(lsym, d.off, itabLsym, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
objw.Global(lsym, int32(d.off), obj.DUPOK|obj.RODATA)
|
objw.Global(lsym, int32(d.off), obj.DUPOK|obj.RODATA)
|
||||||
|
@ -1760,6 +1773,17 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo {
|
||||||
info.tparams[i] = f.Type
|
info.tparams[i] = f.Type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, t := range info.tparams {
|
||||||
|
b := t.Bound()
|
||||||
|
if b.HasTParam() {
|
||||||
|
// If a type bound is parameterized (unusual case), then we
|
||||||
|
// may need its derived type to do a type assert when doing a
|
||||||
|
// bound call for a type arg that is an interface.
|
||||||
|
addType(&info, nil, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, n := range gf.Dcl {
|
for _, n := range gf.Dcl {
|
||||||
addType(&info, n, n.Type())
|
addType(&info, n, n.Type())
|
||||||
}
|
}
|
||||||
|
@ -1950,6 +1974,15 @@ func parameterizedBy1(t *types.Type, params []*types.Type, visited map[*types.Ty
|
||||||
types.TUINTPTR, types.TBOOL, types.TSTRING, types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128:
|
types.TUINTPTR, types.TBOOL, types.TSTRING, types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
case types.TUNION:
|
||||||
|
for i := 0; i < t.NumTerms(); i++ {
|
||||||
|
tt, _ := t.Term(i)
|
||||||
|
if !parameterizedBy1(tt, params, visited) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
|
||||||
default:
|
default:
|
||||||
base.Fatalf("bad type kind %+v", t)
|
base.Fatalf("bad type kind %+v", t)
|
||||||
return true
|
return true
|
||||||
|
@ -2000,15 +2033,32 @@ func startClosure(pos src.XPos, outer *ir.Func, typ *types.Type) (*ir.Func, []*t
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// assertToBound returns a new node that converts a node rcvr with interface type to
|
||||||
|
// the 'dst' interface type. bound is the unsubstituted form of dst.
|
||||||
|
func assertToBound(info *instInfo, dictVar *ir.Name, pos src.XPos, rcvr ir.Node, bound, dst *types.Type) ir.Node {
|
||||||
|
if bound.HasTParam() {
|
||||||
|
ix := findDictType(info, bound)
|
||||||
|
assert(ix >= 0)
|
||||||
|
rt := getDictionaryType(info, dictVar, pos, ix)
|
||||||
|
rcvr = ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, rcvr, rt)
|
||||||
|
typed(dst, rcvr)
|
||||||
|
} else {
|
||||||
|
rcvr = ir.NewTypeAssertExpr(pos, rcvr, nil)
|
||||||
|
typed(bound, rcvr)
|
||||||
|
}
|
||||||
|
return rcvr
|
||||||
|
}
|
||||||
|
|
||||||
// buildClosure2 makes a closure to implement a method expression m (generic form x)
|
// buildClosure2 makes a closure to implement a method expression m (generic form x)
|
||||||
// which has a shape type as receiver. If the receiver is exactly a shape (i.e. from
|
// which has a shape type as receiver. If the receiver is exactly a shape (i.e. from
|
||||||
// a typeparam), then the body of the closure converts the first argument (the
|
// a typeparam), then the body of the closure converts m.X (the receiver) to the
|
||||||
// receiver) to the interface bound type, and makes an interface call with the
|
// interface bound type, and makes an interface call with the remaining arguments.
|
||||||
// remaining arguments.
|
|
||||||
//
|
//
|
||||||
// The returned closure is fully substituted and has already has any needed
|
// The returned closure is fully substituted and has already had any needed
|
||||||
// transformations done.
|
// transformations done.
|
||||||
func (g *irgen) buildClosure2(outer *ir.Func, info *instInfo, m, x ir.Node) ir.Node {
|
func (g *irgen) buildClosure2(subst *subster, m, x ir.Node) ir.Node {
|
||||||
|
outer := subst.newf
|
||||||
|
info := subst.info
|
||||||
pos := m.Pos()
|
pos := m.Pos()
|
||||||
typ := m.Type() // type of the closure
|
typ := m.Type() // type of the closure
|
||||||
|
|
||||||
|
@ -2031,11 +2081,24 @@ func (g *irgen) buildClosure2(outer *ir.Func, info *instInfo, m, x ir.Node) ir.N
|
||||||
rcvr := args[0]
|
rcvr := args[0]
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
assert(m.(*ir.SelectorExpr).X.Type().IsShape())
|
assert(m.(*ir.SelectorExpr).X.Type().IsShape())
|
||||||
rcvr = convertUsingDictionary(info, dictVar, pos, rcvr, x, x.(*ir.SelectorExpr).X.Type().Bound(), x.(*ir.SelectorExpr).X.Type())
|
gsrc := x.(*ir.SelectorExpr).X.Type()
|
||||||
|
bound := gsrc.Bound()
|
||||||
|
dst := bound
|
||||||
|
if dst.HasTParam() {
|
||||||
|
dst = subst.ts.Typ(bound)
|
||||||
|
}
|
||||||
|
if m.(*ir.SelectorExpr).X.Type().IsInterface() {
|
||||||
|
// If type arg is an interface (unusual case), we do a type assert to
|
||||||
|
// the type bound.
|
||||||
|
rcvr = assertToBound(info, dictVar, pos, rcvr, bound, dst)
|
||||||
|
} else {
|
||||||
|
rcvr = convertUsingDictionary(info, dictVar, pos, rcvr, x, dst, gsrc)
|
||||||
|
}
|
||||||
dot := ir.NewSelectorExpr(pos, ir.ODOTINTER, rcvr, x.(*ir.SelectorExpr).Sel)
|
dot := ir.NewSelectorExpr(pos, ir.ODOTINTER, rcvr, x.(*ir.SelectorExpr).Sel)
|
||||||
dot.Selection = typecheck.Lookdot1(dot, dot.Sel, dot.X.Type(), dot.X.Type().AllMethods(), 1)
|
dot.Selection = typecheck.Lookdot1(dot, dot.Sel, dot.X.Type(), dot.X.Type().AllMethods(), 1)
|
||||||
|
|
||||||
typed(x.(*ir.SelectorExpr).Selection.Type, dot)
|
// Do a type substitution on the generic bound, in case it is parameterized.
|
||||||
|
typed(subst.ts.Typ(x.(*ir.SelectorExpr).Selection.Type), dot)
|
||||||
innerCall = ir.NewCallExpr(pos, ir.OCALLINTER, dot, args)
|
innerCall = ir.NewCallExpr(pos, ir.OCALLINTER, dot, args)
|
||||||
t := m.Type()
|
t := m.Type()
|
||||||
if t.NumResults() == 0 {
|
if t.NumResults() == 0 {
|
||||||
|
|
|
@ -2184,7 +2184,6 @@ var g3Failures = setOf(
|
||||||
"typeparam/nested.go", // -G=3 doesn't support function-local types with generics
|
"typeparam/nested.go", // -G=3 doesn't support function-local types with generics
|
||||||
|
|
||||||
"typeparam/mdempsky/4.go", // -G=3 can't export functions with labeled breaks in loops
|
"typeparam/mdempsky/4.go", // -G=3 can't export functions with labeled breaks in loops
|
||||||
"typeparam/mdempsky/13.go", // problem with interface as as a type arg.
|
|
||||||
"typeparam/mdempsky/15.go", // ICE in (*irgen).buildClosure
|
"typeparam/mdempsky/15.go", // ICE in (*irgen).buildClosure
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -6,33 +6,79 @@
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
type Mer interface{ M() }
|
// Interface which will be used as a regular interface type and as a type bound.
|
||||||
|
type Mer interface{
|
||||||
|
M()
|
||||||
|
}
|
||||||
|
|
||||||
func F[T Mer](expectPanic bool) {
|
// Interface that is a superset of Mer.
|
||||||
defer func() {
|
type Mer2 interface {
|
||||||
err := recover()
|
M()
|
||||||
if (err != nil) != expectPanic {
|
String() string
|
||||||
print("FAIL: (", err, " != nil) != ", expectPanic, "\n")
|
}
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var t T
|
func F[T Mer](t T) {
|
||||||
T.M(t)
|
T.M(t)
|
||||||
|
t.M()
|
||||||
}
|
}
|
||||||
|
|
||||||
type MyMer int
|
type MyMer int
|
||||||
|
|
||||||
func (MyMer) M() {}
|
func (MyMer) M() {}
|
||||||
|
func (MyMer) String() string {
|
||||||
|
return "aa"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameterized interface
|
||||||
|
type Abs[T any] interface {
|
||||||
|
Abs() T
|
||||||
|
}
|
||||||
|
|
||||||
|
func G[T Abs[U], U any](t T) {
|
||||||
|
T.Abs(t)
|
||||||
|
t.Abs()
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyInt int
|
||||||
|
func (m MyInt) Abs() MyInt {
|
||||||
|
if m < 0 {
|
||||||
|
return -m
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
type Abs2 interface {
|
||||||
|
Abs() MyInt
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
F[Mer](true)
|
mm := MyMer(3)
|
||||||
F[struct{ Mer }](true)
|
ms := struct{ Mer }{Mer: mm }
|
||||||
F[*struct{ Mer }](true)
|
|
||||||
|
|
||||||
F[MyMer](false)
|
// Testing F with an interface type arg: Mer and Mer2
|
||||||
F[*MyMer](true)
|
F[Mer](mm)
|
||||||
F[struct{ MyMer }](false)
|
F[Mer2](mm)
|
||||||
F[struct{ *MyMer }](true)
|
F[struct{ Mer }](ms)
|
||||||
F[*struct{ MyMer }](true)
|
F[*struct{ Mer }](&ms)
|
||||||
F[*struct{ *MyMer }](true)
|
|
||||||
|
ms2 := struct { MyMer }{MyMer: mm}
|
||||||
|
ms3 := struct { *MyMer }{MyMer: &mm}
|
||||||
|
|
||||||
|
// Testing F with a concrete type arg
|
||||||
|
F[MyMer](mm)
|
||||||
|
F[*MyMer](&mm)
|
||||||
|
F[struct{ MyMer }](ms2)
|
||||||
|
F[struct{ *MyMer }](ms3)
|
||||||
|
F[*struct{ MyMer }](&ms2)
|
||||||
|
F[*struct{ *MyMer }](&ms3)
|
||||||
|
|
||||||
|
// Testing G with a concrete type args
|
||||||
|
mi := MyInt(-3)
|
||||||
|
G[MyInt,MyInt](mi)
|
||||||
|
|
||||||
|
// Interface Abs[MyInt] holding an mi.
|
||||||
|
intMi := Abs[MyInt](mi)
|
||||||
|
// First type arg here is Abs[MyInt], an interface type.
|
||||||
|
G[Abs[MyInt],MyInt](intMi)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue