checkFetchBody(nameNode)
}
+ var tparams []*types.Type
+ if isMeth {
+ // Get the type params from the method receiver (after skipping
+ // over any pointer)
+ recvType := nameNode.Type().Recv().Type
+ recvType = deref(recvType)
+ tparams = recvType.RParams()
+ } else {
+ fields := nameNode.Type().TParams().Fields().Slice()
+ tparams = make([]*types.Type, len(fields))
+ for i, f := range fields {
+ tparams[i] = f.Type
+ }
+ }
+
// Convert any non-shape type arguments to their shape, so we can reduce the
// number of instantiations we have to generate. You can actually have a mix
// of shape and non-shape arguments, because of inferred or explicitly
// specified concrete type args.
s1 := make([]*types.Type, len(shapes))
for i, t := range shapes {
+ var tparam *types.Type
+ if tparams[i].Kind() == types.TTYPEPARAM {
+ // Shapes are grouped differently for structural types, so we
+ // pass the type param to Shapify(), so we can distinguish.
+ tparam = tparams[i]
+ }
if !t.IsShape() {
- s1[i] = typecheck.Shapify(t, i)
+ s1[i] = typecheck.Shapify(t, i, tparam)
} else {
// Already a shape, but make sure it has the correct index.
- s1[i] = typecheck.Shapify(shapes[i].Underlying(), i)
+ s1[i] = typecheck.Shapify(shapes[i].Underlying(), i, tparam)
}
}
shapes = s1
}
// genericSubst fills in info.dictParam and info.shapeToBound.
- st := g.genericSubst(sym, nameNode, shapes, isMeth, info)
+ st := g.genericSubst(sym, nameNode, tparams, shapes, isMeth, info)
info.fun = st
g.instInfoMap[sym] = info
// function type where the receiver becomes the first parameter. For either a generic
// method or function, a dictionary parameter is the added as the very first
// parameter. genericSubst fills in info.dictParam and info.shapeToBound.
-func (g *genInst) genericSubst(newsym *types.Sym, nameNode *ir.Name, shapes []*types.Type, isMethod bool, info *instInfo) *ir.Func {
- var tparams []*types.Type
- if isMethod {
- // Get the type params from the method receiver (after skipping
- // over any pointer)
- recvType := nameNode.Type().Recv().Type
- recvType = deref(recvType)
- tparams = recvType.RParams()
- } else {
- fields := nameNode.Type().TParams().Fields().Slice()
- tparams = make([]*types.Type, len(fields))
- for i, f := range fields {
- tparams[i] = f.Type
- }
- }
+func (g *genInst) genericSubst(newsym *types.Sym, nameNode *ir.Name, tparams []*types.Type, shapes []*types.Type, isMethod bool, info *instInfo) *ir.Func {
gf := nameNode.Func
// Pos of the instantiated function is same as the generic function
newf := ir.NewFunc(gf.Pos())
ir.CurFunc = info.fun
case ir.OXDOT:
+ // This is the case of a dot access on a type param. This is
+ // typically a bound call on the type param, but could be a
+ // field access, if the constraint has a single structural type.
mse := m.(*ir.SelectorExpr)
src := mse.X.Type()
assert(src.IsShape())
- // The only dot on a shape type value are methods.
if mse.X.Op() == ir.OTYPE {
// Method expression T.M
m = g.buildClosure2(info, m)
// No need for transformDot - buildClosure2 has already
// transformed to OCALLINTER/ODOTINTER.
} else {
- // Implement x.M as a conversion-to-bound-interface
- // 1) convert x to the bound interface
- // 2) call M on that interface
dst := info.dictInfo.shapeToBound[m.(*ir.SelectorExpr).X.Type()]
- if src.IsInterface() {
- // If type arg is an interface (unusual case),
- // we do a type assert to the type bound.
- mse.X = assertToBound(info, info.dictParam, m.Pos(), mse.X, dst)
- } else {
- mse.X = convertUsingDictionary(info, info.dictParam, m.Pos(), mse.X, m, dst, true)
- // Note: we set nonEscaping==true, because we can assume the backing store for the
- // interface conversion doesn't escape. The method call will immediately go to
- // a wrapper function which copies all the data out of the interface value.
- // (It only matters for non-pointer-shaped interface conversions. See issue 50182.)
+ // If we can't find the selected method in the
+ // AllMethods of the bound, then this must be an access
+ // to a field of a structural type. If so, we skip the
+ // dictionary lookups - transformDot() will convert to
+ // the desired direct field access.
+ if typecheck.Lookdot1(mse, mse.Sel, dst, dst.AllMethods(), 1) != nil {
+ // Implement x.M as a conversion-to-bound-interface
+ // 1) convert x to the bound interface
+ // 2) call M on that interface
+ if src.IsInterface() {
+ // If type arg is an interface (unusual case),
+ // we do a type assert to the type bound.
+ mse.X = assertToBound(info, info.dictParam, m.Pos(), mse.X, dst)
+ } else {
+ mse.X = convertUsingDictionary(info, info.dictParam, m.Pos(), mse.X, m, dst, true)
+ // Note: we set nonEscaping==true, because we can assume the backing store for the
+ // interface conversion doesn't escape. The method call will immediately go to
+ // a wrapper function which copies all the data out of the interface value.
+ // (It only matters for non-pointer-shaped interface conversions. See issue 50182.)
+ }
}
transformDot(mse, false)
}
// Target method uses shaped names.
targs2 := make([]*types.Type, len(targs))
+ origRParams := deref(orig).OrigSym().Def.(*ir.Name).Type().RParams()
for i, t := range targs {
- targs2[i] = typecheck.Shapify(t, i)
+ targs2[i] = typecheck.Shapify(t, i, origRParams[i])
}
targs = targs2
baseType := t.OrigSym().Def.(*ir.Name).Type()
shapes := make([]*types.Type, len(t.RParams()))
for i, t1 := range t.RParams() {
- shapes[i] = Shapify(t1, i)
+ shapes[i] = Shapify(t1, i, baseType.RParams()[i])
}
for j := range t.Methods().Slice() {
baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name)
// For now, we only consider two types to have the same shape, if they have exactly
// the same underlying type or they are both pointer types.
//
+// tparam is the associated typeparam. If there is a structural type for
+// the associated type param (not common), then a pointer type t is mapped to its
+// underlying type, rather than being merged with other pointers.
+//
// Shape types are also distinguished by the index of the type in a type param/arg
// list. We need to do this so we can distinguish and substitute properly for two
// type params in the same function that have the same shape for a particular
// instantiation.
-func Shapify(t *types.Type, index int) *types.Type {
+func Shapify(t *types.Type, index int, tparam *types.Type) *types.Type {
assert(!t.IsShape())
// Map all types with the same underlying type to the same shape.
u := t.Underlying()
// TODO: Make unsafe.Pointer the same shape as normal pointers.
// Note: pointers to arrays are special because of slice-to-array-pointer
// conversions. See issue 49295.
- if u.Kind() == types.TPTR && u.Elem().Kind() != types.TARRAY {
+ if u.Kind() == types.TPTR && u.Elem().Kind() != types.TARRAY &&
+ tparam.Bound().StructuralType() == nil {
u = types.Types[types.TUINT8].PtrTo()
}
--- /dev/null
+package types
+
+// Implementation of structural type computation for types.
+
+// TODO: we would like to depend only on the types2 computation of structural type,
+// but we can only do that the next time we change the export format and export
+// structural type info along with each constraint type, since the compiler imports
+// types directly into types1 format.
+
+// A term describes elementary type sets:
+//
+// term{false, T} set of type T
+// term{true, T} set of types with underlying type t
+// term{} empty set (we specifically check for typ == nil)
+type term struct {
+ tilde bool
+ typ *Type
+}
+
+// StructuralType returns the structural type of an interface, or nil if it has no
+// structural type.
+func (t *Type) StructuralType() *Type {
+ sts, _ := specificTypes(t)
+ var su *Type
+ for _, st := range sts {
+ u := st.typ.Underlying()
+ if su != nil {
+ u = match(su, u)
+ if u == nil {
+ return nil
+ }
+ }
+ // su == nil || match(su, u) != nil
+ su = u
+ }
+ return su
+}
+
+// If x and y are identical, match returns x.
+// If x and y are identical channels but for their direction
+// and one of them is unrestricted, match returns the channel
+// with the restricted direction.
+// In all other cases, match returns nil.
+// x and y are assumed to be underlying types, hence are not named types.
+func match(x, y *Type) *Type {
+ if IdenticalStrict(x, y) {
+ return x
+ }
+
+ if x.IsChan() && y.IsChan() && IdenticalStrict(x.Elem(), y.Elem()) {
+ // We have channels that differ in direction only.
+ // If there's an unrestricted channel, select the restricted one.
+ // If both have the same direction, return x (either is fine).
+ switch {
+ case x.ChanDir().CanSend() && x.ChanDir().CanRecv():
+ return y
+ case y.ChanDir().CanSend() && y.ChanDir().CanRecv():
+ return x
+ }
+ }
+ return nil
+}
+
+// specificTypes returns the list of specific types of an interface type or nil if
+// there are none. It also returns a flag that indicates, for an empty term list
+// result, whether it represents the empty set, or the infinite set of all types (in
+// both cases, there are no specific types).
+func specificTypes(t *Type) (list []term, inf bool) {
+ t.wantEtype(TINTER)
+
+ // We have infinite term list before processing any type elements
+ // (or if there are no type elements).
+ inf = true
+ for _, m := range t.Methods().Slice() {
+ var r2 []term
+ inf2 := false
+
+ switch {
+ case m.IsMethod():
+ inf2 = true
+
+ case m.Type.IsUnion():
+ nt := m.Type.NumTerms()
+ for i := 0; i < nt; i++ {
+ t, tilde := m.Type.Term(i)
+ if t.IsInterface() {
+ r3, r3inf := specificTypes(t)
+ if r3inf {
+ // Union with an infinite set of types is
+ // infinite, so skip remaining terms.
+ r2 = nil
+ inf2 = true
+ break
+ }
+ // Add the elements of r3 to r2.
+ for _, r3e := range r3 {
+ r2 = insertType(r2, r3e)
+ }
+ } else {
+ r2 = insertType(r2, term{tilde, t})
+ }
+ }
+
+ case m.Type.IsInterface():
+ r2, inf2 = specificTypes(m.Type)
+
+ default:
+ // m.Type is a single non-interface type, so r2 is just a
+ // one-element list, inf2 is false.
+ r2 = []term{term{false, m.Type}}
+ }
+
+ if inf2 {
+ // If the current type element has infinite types,
+ // its intersection with r is just r, so skip this type element.
+ continue
+ }
+
+ if inf {
+ // If r is infinite, then the intersection of r and r2 is just r2.
+ list = r2
+ inf = false
+ continue
+ }
+
+ // r and r2 are finite, so intersect r and r2.
+ var r3 []term
+ for _, re := range list {
+ for _, r2e := range r2 {
+ if tm := intersect(re, r2e); tm.typ != nil {
+ r3 = append(r3, tm)
+ }
+ }
+ }
+ list = r3
+ }
+ return
+}
+
+// insertType adds t to the returned list if it is not already in list.
+func insertType(list []term, tm term) []term {
+ for i, elt := range list {
+ if new := union(elt, tm); new.typ != nil {
+ // Replace existing elt with the union of elt and new.
+ list[i] = new
+ return list
+ }
+ }
+ return append(list, tm)
+}
+
+// If x and y are disjoint, return term with nil typ (which means the union should
+// include both types). If x and y are not disjoint, return the single type which is
+// the union of x and y.
+func union(x, y term) term {
+ if disjoint(x, y) {
+ return term{false, nil}
+ }
+ if x.tilde || !y.tilde {
+ return x
+ }
+ return y
+}
+
+// intersect returns the intersection x ∩ y.
+func intersect(x, y term) term {
+ if disjoint(x, y) {
+ return term{false, nil}
+ }
+ if !x.tilde || y.tilde {
+ return x
+ }
+ return y
+}
+
+// disjoint reports whether x ∩ y == ∅.
+func disjoint(x, y term) bool {
+ ux := x.typ
+ if y.tilde {
+ ux = ux.Underlying()
+ }
+ uy := y.typ
+ if x.tilde {
+ uy = uy.Underlying()
+ }
+ return !IdenticalStrict(ux, uy)
+}
--- /dev/null
+// Copyright 2022 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.
+
+// Test that StructuralType() calculates the correct value of structural type for
+// unusual cases.
+
+package types
+
+import (
+ "cmd/internal/src"
+ "testing"
+)
+
+type test struct {
+ typ *Type
+ structuralType *Type
+}
+
+func TestStructuralType(t *testing.T) {
+ // These are the few constants that need to be initialized in order to use
+ // the types package without using the typecheck package by calling
+ // typecheck.InitUniverse() (the normal way to initialize the types package).
+ PtrSize = 8
+ RegSize = 8
+ MaxWidth = 1 << 50
+
+ // type intType = int
+ intType := newType(TINT)
+ // type structf = struct { f int }
+ structf := NewStruct(nil, []*Field{
+ NewField(src.NoXPos, LocalPkg.Lookup("f"), intType),
+ })
+
+ // type Sf structf
+ Sf := newType(TFORW)
+ Sf.sym = LocalPkg.Lookup("Sf")
+ Sf.SetUnderlying(structf)
+
+ // type A int
+ A := newType(TFORW)
+ A.sym = LocalPkg.Lookup("A")
+ A.SetUnderlying(intType)
+
+ // type B int
+ B := newType(TFORW)
+ B.sym = LocalPkg.Lookup("B")
+ B.SetUnderlying(intType)
+
+ emptyInterface := NewInterface(BuiltinPkg, []*Field{}, false)
+ any := newType(TFORW)
+ any.sym = LocalPkg.Lookup("any")
+ any.SetUnderlying(emptyInterface)
+
+ // The tests marked NONE have no structural type; all the others have a
+ // structural type of structf - "struct { f int }"
+ tests := []*test{
+ {
+ // interface { struct { f int } }
+ embed(structf),
+ structf,
+ },
+ {
+ // interface { struct { f int }; any }
+ embed(structf, any),
+ structf,
+ },
+ {
+ // interface { Sf }
+ embed(Sf),
+ structf,
+ },
+ {
+ // interface { any | Sf }
+ embed(any, Sf),
+ structf,
+ },
+ {
+ // interface { struct { f int }; Sf } - NONE
+ embed(structf, Sf),
+ nil,
+ },
+ {
+ // interface { struct { f int } | ~struct { f int } }
+ embed(NewUnion([]*Type{structf, structf}, []bool{false, true})),
+ structf,
+ },
+ {
+ // interface { ~struct { f int } ; Sf }
+ embed(NewUnion([]*Type{structf}, []bool{true}), Sf),
+ structf,
+ },
+ {
+ // interface { struct { f int } ; Sf } - NONE
+ embed(NewUnion([]*Type{structf}, []bool{false}), Sf),
+ nil,
+ },
+ {
+ // interface { Sf | A; B | Sf}
+ embed(NewUnion([]*Type{Sf, A}, []bool{false, false}),
+ NewUnion([]*Type{B, Sf}, []bool{false, false})),
+ structf,
+ },
+ {
+ // interface { Sf | A; A | Sf } - NONE
+ embed(NewUnion([]*Type{Sf, A}, []bool{false, false}),
+ NewUnion([]*Type{A, Sf}, []bool{false, false})),
+ nil,
+ },
+ {
+ // interface { Sf | any } - NONE
+ embed(NewUnion([]*Type{Sf, any}, []bool{false, false})),
+ nil,
+ },
+ {
+ // interface { Sf | any; Sf }
+ embed(NewUnion([]*Type{Sf, any}, []bool{false, false}), Sf),
+ structf,
+ },
+ }
+ for _, tst := range tests {
+ if got, want := tst.typ.StructuralType(), tst.structuralType; got != want {
+ t.Errorf("StructuralType(%v) = %v, wanted %v",
+ tst.typ, got, want)
+ }
+ }
+}
+
+func embed(types ...*Type) *Type {
+ fields := make([]*Field, len(types))
+ for i, t := range types {
+ fields[i] = NewField(src.NoXPos, nil, t)
+ }
+ return NewInterface(LocalPkg, fields, false)
+}
}
skip := map[string]string{
- "equal.go": "inconsistent embedded sorting", // TODO(rfindley): investigate this.
- "nested.go": "fails to compile", // TODO(rfindley): investigate this.
+ "equal.go": "inconsistent embedded sorting", // TODO(rfindley): investigate this.
+ "nested.go": "fails to compile", // TODO(rfindley): investigate this.
+ "issue50417.go": "inconsistent interface member sorting",
}
for _, entry := range list {
"typeparam/typeswitch2.go", // duplicate case failure due to stenciling
"typeparam/typeswitch3.go", // duplicate case failure due to stenciling
"typeparam/typeswitch4.go", // duplicate case failure due to stenciling
+ "typeparam/issue50417b.go", // Need to handle field access on a type param
"typeparam/issue50552.go", // gives missing method for instantiated type
)
p.f = 0
}
-// TODO(danscales) enable once the compiler is fixed
-// var _ = f0[Sf]
-// var _ = f0t[Sf]
+var _ = f0[Sf]
+var _ = f0t[Sf]
func f1[P interface {
- Sf
+ ~struct{ f int }
m()
}](p P) {
_ = p.f
p.m()
}
+var _ = f1[Sfm]
+
type Sm struct{}
func (Sm) m() {}
p.m()
}
-// TODO(danscales) enable once the compiler is fixed
-// var _ = f2[Sfm]
+var _ = f2[Sfm]
// special case: structural type is a named pointer type
p.f = 0
}
-// TODO(danscales) enable once the compiler is fixed
-// var _ = f3[PSfm]
+var _ = f3[PSfm]
+
+// special case: structural type is an unnamed pointer type
+
+func f4[P interface{ *Sfm }](p P) {
+ _ = p.f
+ p.f = 0
+}
+
+var _ = f4[*Sfm]
+
+type A int
+type B int
+type C float64
+
+type Int interface {
+ *Sf | A
+ *Sf | B
+}
+
+func f5[P Int](p P) {
+ _ = p.f
+ p.f = 0
+}
+
+var _ = f5[*Sf]
+
+type Int2 interface {
+ *Sf | A
+ any
+ *Sf | C
+}
+
+func f6[P Int2](p P) {
+ _ = p.f
+ p.f = 0
+}
+
+var _ = f6[*Sf]
+
+type Int3 interface {
+ Sf
+ ~struct{ f int }
+}
+
+func f7[P Int3](p P) {
+ _ = p.f
+ p.f = 0
+}
+
+var _ = f7[Sf]
+
+type Em1 interface {
+ *Sf | A
+}
+
+type Em2 interface {
+ *Sf | B
+}
+
+type Int4 interface {
+ Em1
+ Em2
+ any
+}
+
+func f8[P Int4](p P) {
+ _ = p.f
+ p.f = 0
+}
+
+var _ = f8[*Sf]
--- /dev/null
+// run -gcflags=-G=3
+
+// Copyright 2022 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 "fmt"
+
+type MyStruct struct {
+ b1, b2 string
+ E
+}
+
+type E struct {
+ val int
+}
+
+type C interface {
+ ~struct {
+ b1, b2 string
+ E
+ }
+}
+
+func f[T C]() T {
+ var x T = T{
+ b1: "a",
+ b2: "b",
+ }
+
+ if got, want := x.b2, "b"; got != want {
+ panic(fmt.Sprintf("got %d, want %d", got, want))
+ }
+ x.b1 = "y"
+ x.val = 5
+
+ return x
+}
+
+func main() {
+ x := f[MyStruct]()
+ if got, want := x.b1, "y"; got != want {
+ panic(fmt.Sprintf("got %d, want %d", got, want))
+ }
+ if got, want := x.val, 5; got != want {
+ panic(fmt.Sprintf("got %d, want %d", got, want))
+ }
+}