diff --git a/src/cmd/compile/internal/ir/expr.go b/src/cmd/compile/internal/ir/expr.go index 4659b99fbf7..81b2c52b87f 100644 --- a/src/cmd/compile/internal/ir/expr.go +++ b/src/cmd/compile/internal/ir/expr.go @@ -700,6 +700,11 @@ func (n *UnaryExpr) SetOp(op Op) { } } +// Probably temporary: using Implicit() flag to mark generic function nodes that +// are called to make getGfInfo analysis easier in one pre-order pass. +func (n *InstExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } +func (n *InstExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } + // An InstExpr is a generic function or type instantiation. type InstExpr struct { miniExpr diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go index 9d14b06d3c8..8f390612506 100644 --- a/src/cmd/compile/internal/noder/irgen.go +++ b/src/cmd/compile/internal/noder/irgen.go @@ -96,6 +96,15 @@ func check2(noders []*noder) { } } +// gfInfo is information gathered on a generic function. +type gfInfo struct { + tparams []*types.Type + derivedTypes []*types.Type + // Node in generic function that requires a subdictionary. Some of these + // are not function/method values (not strictly calls). + subDictCalls []ir.Node +} + type irgen struct { target *ir.Package self *types2.Package @@ -110,6 +119,10 @@ type irgen struct { instTypeList []*types.Type dnum int // for generating unique dictionary variables + + // Map from generic function to information about its type params, derived + // types, and subdictionaries. + gfInfoMap map[*types.Sym]*gfInfo } func (g *irgen) generate(noders []*noder) { diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go index 710289b76c0..1917c95be71 100644 --- a/src/cmd/compile/internal/noder/stencil.go +++ b/src/cmd/compile/internal/noder/stencil.go @@ -10,12 +10,15 @@ package noder import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" + "cmd/compile/internal/objw" "cmd/compile/internal/reflectdata" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" + "cmd/internal/obj" "cmd/internal/src" "fmt" "go/constant" + "strings" ) func assert(p bool) { @@ -24,6 +27,16 @@ func assert(p bool) { } } +// Temporary - for outputting information on derived types, dictionaries, sub-dictionaries. +// Turn off when running tests. +var infoPrintMode = false + +func infoPrint(format string, a ...interface{}) { + if infoPrintMode { + fmt.Printf(format, a...) + } +} + // stencil scans functions for instantiated generic function calls and creates the // required instantiations for simple generic functions. It also creates // instantiated methods for all fully-instantiated generic types that have been @@ -31,6 +44,7 @@ func assert(p bool) { // process. func (g *irgen) stencil() { g.target.Stencils = make(map[*types.Sym]*ir.Func) + g.gfInfoMap = make(map[*types.Sym]*gfInfo) // Instantiate the methods of instantiated generic types that we have seen so far. g.instantiateMethods() @@ -87,7 +101,14 @@ func (g *irgen) stencil() { // instantiation. call := n.(*ir.CallExpr) inst := call.X.(*ir.InstExpr) - st := g.getInstantiationForNode(inst) + st, dict := g.getInstantiationForNode(inst) + if infoPrintMode && g.target.Stencils[decl.Sym()] == nil { + if inst.X.Op() == ir.OCALLPART { + fmt.Printf("Main dictionary in %v at generic method call: %v - %v\n", decl, inst.X, call) + } else { + fmt.Printf("Main dictionary in %v at generic function call: %v - %v\n", decl, inst.X, call) + } + } // Replace the OFUNCINST with a direct reference to the // new stenciled function call.X = st.Nname @@ -99,7 +120,6 @@ func (g *irgen) stencil() { call.Args.Prepend(inst.X.(*ir.SelectorExpr).X) } // Add dictionary to argument list. - dict := reflectdata.GetDictionaryForInstantiation(inst) call.Args.Prepend(dict) // Transform the Call now, which changes OCALL // to OCALLFUNC and does typecheckaste/assignconvfn. @@ -125,10 +145,9 @@ func (g *irgen) stencil() { } } - st := g.getInstantiation(gf, targs, true) + st, dict := g.getInstantiation(gf, targs, true) call.SetOp(ir.OCALL) call.X = st.Nname - dict := reflectdata.GetDictionaryForMethod(gf, targs) call.Args.Prepend(dict, meth.X) // Transform the Call now, which changes OCALL // to OCALLFUNC and does typecheckaste/assignconvfn. @@ -212,13 +231,14 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node { // For functions, the target expects a dictionary as its first argument. // For method values, the target expects a dictionary and the receiver // as its first two arguments. - target = g.getInstantiation(gf, targs, rcvrValue != nil) - - // The value to use for the dictionary argument. - if rcvrValue == nil { - dictValue = reflectdata.GetDictionaryForFunc(gf, targs) - } else { - dictValue = reflectdata.GetDictionaryForMethod(gf, targs) + // dictValue is the value to use for the dictionary argument. + target, dictValue = g.getInstantiation(gf, targs, rcvrValue != nil) + if infoPrintMode && (outer == nil || g.target.Stencils[outer.Sym()] == nil) { + if rcvrValue == nil { + fmt.Printf("Main dictionary in %v for function value %v\n", outer, inst.X) + } else { + fmt.Printf("Main dictionary in %v for method value %v\n", outer, inst.X) + } } } else { // ir.OMETHEXPR // Method expression T.M where T is a generic type. @@ -248,8 +268,10 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node { break } } - target = g.getInstantiation(gf, targs, true) - dictValue = reflectdata.GetDictionaryForMethod(gf, targs) + target, dictValue = g.getInstantiation(gf, targs, true) + if infoPrintMode && (outer == nil || g.target.Stencils[outer.Sym()] == nil) { + fmt.Printf("Main dictionary in %v for method expression %v\n", outer, x) + } } // Build a closure to implement a function instantiation. @@ -444,7 +466,7 @@ func (g *irgen) instantiateMethods() { baseType := baseSym.Def.(*ir.Name).Type() for j, _ := range typ.Methods().Slice() { baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name) - // Eagerly generate the instantiations that implement these methods. + // Eagerly generate the instantiations and dictionaries that implement these methods. // We don't use the instantiations here, just generate them (and any // further instantiations those generate, etc.). // Note that we don't set the Func for any methods on instantiated @@ -452,16 +474,16 @@ func (g *irgen) instantiateMethods() { // Direct method calls go directly to the instantiations, implemented above. // Indirect method calls use wrappers generated in reflectcall. Those wrappers // will use these instantiations if they are needed (for interface tables or reflection). - _ = g.getInstantiation(baseNname, typ.RParams(), true) + _, _ = g.getInstantiation(baseNname, typ.RParams(), true) } } g.instTypeList = nil } -// getInstantiationForNode returns the function/method instantiation for a -// InstExpr node inst. -func (g *irgen) getInstantiationForNode(inst *ir.InstExpr) *ir.Func { +// getInstantiationForNode returns the function/method instantiation and +// dictionary value for a InstExpr node inst. +func (g *irgen) getInstantiationForNode(inst *ir.InstExpr) (*ir.Func, ir.Node) { if meth, ok := inst.X.(*ir.SelectorExpr); ok { return g.getInstantiation(meth.Selection.Nname.(*ir.Name), typecheck.TypesOf(inst.Targs), true) } else { @@ -469,10 +491,10 @@ func (g *irgen) getInstantiationForNode(inst *ir.InstExpr) *ir.Func { } } -// getInstantiation gets the instantiantion of the function or method nameNode +// getInstantiation gets the instantiantion and dictionary of the function or method nameNode // with the type arguments targs. If the instantiated function is not already // cached, then it calls genericSubst to create the new instantiation. -func (g *irgen) getInstantiation(nameNode *ir.Name, targs []*types.Type, isMeth bool) *ir.Func { +func (g *irgen) getInstantiation(nameNode *ir.Name, targs []*types.Type, isMeth bool) (*ir.Func, ir.Node) { if nameNode.Func.Body == nil && nameNode.Func.Inl != nil { // If there is no body yet but Func.Inl exists, then we can can // import the whole generic body. @@ -497,7 +519,7 @@ func (g *irgen) getInstantiation(nameNode *ir.Name, targs []*types.Type, isMeth ir.Dump(fmt.Sprintf("\nstenciled %v", st), st) } } - return st + return st, g.getDictionary(sym.Name, nameNode, targs) } // Struct containing info needed for doing the substitution as we create the @@ -994,3 +1016,245 @@ func deref(t *types.Type) *types.Type { } return t } + +// getDictionary returns the dictionary for the named instantiated function, which +// is instantiated from generic function or method gf, with the type arguments targs. +func (g *irgen) getDictionary(name string, gf *ir.Name, targs []*types.Type) ir.Node { + if len(targs) == 0 { + base.Fatalf("%s should have type arguments", name) + } + + // The dictionary for this instantiation is named after the function + // and concrete types it is instantiated with. + // TODO: decouple this naming from the instantiation naming. The instantiation + // naming will be based on GC shapes, this naming must be fully stenciled. + if !strings.HasPrefix(name, ".inst.") { + base.Fatalf("%s should start in .inst.", name) + } + + info := g.getGfInfo(gf) + + name = ".dict." + name[6:] + + // Get a symbol representing the dictionary. + sym := typecheck.Lookup(name) + + // Initialize the dictionary, if we haven't yet already. + if lsym := sym.Linksym(); len(lsym.P) == 0 { + infoPrint("Creating dictionary %v\n", name) + off := 0 + // Emit an entry for each targ (concrete type or gcshape). + for _, t := range targs { + infoPrint(" * %v\n", t) + s := reflectdata.TypeLinksym(t) + off = objw.SymPtr(lsym, off, s, 0) + } + subst := typecheck.Tsubster{ + Tparams: info.tparams, + Targs: targs, + } + // Emit an entry for each derived type (after substituting targs) + for _, t := range info.derivedTypes { + ts := subst.Typ(t) + infoPrint(" - %v\n", ts) + s := reflectdata.TypeLinksym(ts) + off = objw.SymPtr(lsym, off, s, 0) + } + // Emit an entry for each subdictionary (after substituting targs) + // TODO: actually emit symbol for the subdictionary entry + for _, n := range info.subDictCalls { + if n.Op() == ir.OCALL { + call := n.(*ir.CallExpr) + if call.X.Op() == ir.OXDOT { + subtargs := deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).RParams() + s2targs := make([]*types.Type, len(subtargs)) + for i, t := range subtargs { + s2targs[i] = subst.Typ(t) + } + sym := typecheck.MakeInstName(ir.MethodSym(call.X.(*ir.SelectorExpr).X.Type(), call.X.(*ir.SelectorExpr).Sel), s2targs, true) + infoPrint(" - Subdict .dict.%v\n", sym.Name[6:]) + } else { + inst := n.(*ir.CallExpr).X.(*ir.InstExpr) + var nameNode *ir.Name + var meth *ir.SelectorExpr + var isMeth bool + if meth, isMeth = inst.X.(*ir.SelectorExpr); isMeth { + nameNode = meth.Selection.Nname.(*ir.Name) + } else { + nameNode = inst.X.(*ir.Name) + } + subtargs := typecheck.TypesOf(inst.Targs) + for i, t := range subtargs { + subtargs[i] = subst.Typ(t) + } + sym := typecheck.MakeInstName(nameNode.Sym(), subtargs, isMeth) + // TODO: This can actually be a static + // main dictionary, if all of the subtargs + // are concrete types (!HasTParam) + infoPrint(" - Subdict .dict.%v\n", sym.Name[6:]) + } + } else if n.Op() == ir.OFUNCINST { + inst := n.(*ir.InstExpr) + nameNode := inst.X.(*ir.Name) + subtargs := typecheck.TypesOf(inst.Targs) + for i, t := range subtargs { + subtargs[i] = subst.Typ(t) + } + sym := typecheck.MakeInstName(nameNode.Sym(), subtargs, false) + // TODO: This can actually be a static + // main dictionary, if all of the subtargs + // are concrete types (!HasTParam) + infoPrint(" - Subdict .dict.%v\n", sym.Name[6:]) + } else if n.Op() == ir.OXDOT { + selExpr := n.(*ir.SelectorExpr) + subtargs := selExpr.X.Type().RParams() + s2targs := make([]*types.Type, len(subtargs)) + for i, t := range subtargs { + s2targs[i] = subst.Typ(t) + } + sym := typecheck.MakeInstName(ir.MethodSym(selExpr.X.Type(), selExpr.Sel), s2targs, true) + infoPrint(" - Subdict .dict.%v\n", sym.Name[6:]) + } + // TODO: handle closure cases that need sub-dictionaries + } + objw.Global(lsym, int32(off), obj.DUPOK|obj.RODATA) + } + + // Make a node referencing the dictionary symbol. + n := typecheck.NewName(sym) + n.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter + n.SetTypecheck(1) + n.Class = ir.PEXTERN + sym.Def = n + + // Return the address of the dictionary. + np := typecheck.NodAddr(n) + // Note: treat dictionary pointers as uintptrs, so they aren't pointers + // with respect to GC. That saves on stack scanning work, write barriers, etc. + // We can get away with it because dictionaries are global variables. + // TODO: use a cast, or is typing directly ok? + np.SetType(types.Types[types.TUINTPTR]) + np.SetTypecheck(1) + return np +} + +// getGfInfo get information for a generic function - type params, derived generic +// types, and subdictionaries. +func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo { + infop := g.gfInfoMap[gn.Sym()] + if infop != nil { + return infop + } + + var info gfInfo + gf := gn.Func + recv := gf.Type().Recv() + if recv != nil { + info.tparams = deref(recv.Type).RParams() + } else { + info.tparams = make([]*types.Type, len(gn.Type().TParams().FieldSlice())) + for i, f := range gn.Type().TParams().FieldSlice() { + info.tparams[i] = f.Type + } + } + for _, n := range gf.Dcl { + addType(&info, n, n.Type()) + } + + if infoPrintMode { + fmt.Printf(">>> Info for %v\n", gn) + for _, t := range info.tparams { + fmt.Printf(" Typeparam %v\n", t) + } + for _, t := range info.derivedTypes { + fmt.Printf(" Derived type %v\n", t) + } + } + + for _, stmt := range gf.Body { + ir.Visit(stmt, func(n ir.Node) { + if n.Op() == ir.OFUNCINST && !n.(*ir.InstExpr).Implicit() { + infoPrint(" Closure&subdictionary required at generic function value %v\n", n.(*ir.InstExpr).X) + info.subDictCalls = append(info.subDictCalls, n) + } else if n.Op() == ir.OXDOT && !n.(*ir.SelectorExpr).Implicit() && + !n.(*ir.SelectorExpr).X.Type().IsInterface() && + len(n.(*ir.SelectorExpr).X.Type().RParams()) > 0 { + // Fix this - doesn't account for embedded fields, etc. + field := typecheck.Lookdot1(n.(*ir.SelectorExpr), n.(*ir.SelectorExpr).Sel, n.(*ir.SelectorExpr).X.Type(), n.(*ir.SelectorExpr).X.Type().Fields(), 0) + if field == nil { + if n.(*ir.SelectorExpr).X.Op() == ir.OTYPE { + infoPrint(" Closure&subdictionary required at generic meth expr %v\n", n) + } else { + infoPrint(" Closure&subdictionary required at generic meth value %v\n", n) + } + info.subDictCalls = append(info.subDictCalls, n) + } + } + if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OFUNCINST { + infoPrint(" Subdictionary at generic function call: %v - %v\n", n.(*ir.CallExpr).X.(*ir.InstExpr).X, n) + n.(*ir.CallExpr).X.(*ir.InstExpr).SetImplicit(true) + info.subDictCalls = append(info.subDictCalls, n) + } + if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OXDOT && + !n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type().IsInterface() && + len(deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).RParams()) > 0 { + infoPrint(" Subdictionary at generic method call: %v\n", n) + n.(*ir.CallExpr).X.(*ir.SelectorExpr).SetImplicit(true) + info.subDictCalls = append(info.subDictCalls, n) + } + if n.Op() == ir.OCLOSURE { + oldfn := n.(*ir.ClosureExpr).Func + needDict := false + if oldfn.Nname.Type().HasTParam() { + needDict = true + infoPrint(" Subdictionary for closure that has generic params: %v\n", oldfn) + } else { + for _, cv := range oldfn.ClosureVars { + if cv.Type().HasTParam() { + needDict = true + infoPrint(" Subdictionary for closure that has generic capture: %v\n", oldfn) + break + } + } + } + if needDict { + info.subDictCalls = append(info.subDictCalls, n) + } + } + + addType(&info, n, n.Type()) + }) + } + g.gfInfoMap[gn.Sym()] = &info + return &info +} + +// addType adds t to info.derivedTypes if it is parameterized type (which is not +// just a simple type param) that is different from any existing type on +// info.derivedTypes. +func addType(info *gfInfo, n ir.Node, t *types.Type) { + if t == nil || !t.HasTParam() { + return + } + if t.IsTypeParam() && t.Underlying() == t { + return + } + if t.Kind() == types.TFUNC && n != nil && + (n.Op() != ir.ONAME || n.Name().Class == ir.PFUNC) { + // For now, only record function types that are associate with a + // local/global variable (a name which is not a named global + // function). + return + } + if t.Kind() == types.TSTRUCT && t.IsFuncArgStruct() { + // Multiple return values are not a relevant new type (?). + return + } + // Ignore a derived type we've already added. + for _, et := range info.derivedTypes { + if types.Identical(t, et) { + return + } + } + info.derivedTypes = append(info.derivedTypes, t) +} diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go index 52534db70d0..8378fab36d9 100644 --- a/src/cmd/compile/internal/reflectdata/reflect.go +++ b/src/cmd/compile/internal/reflectdata/reflect.go @@ -1716,8 +1716,12 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy rcvr = rcvr.PtrTo() } generic := false - if !rcvr.IsInterface() && len(rcvr.RParams()) > 0 || rcvr.IsPtr() && len(rcvr.Elem().RParams()) > 0 { // TODO: right detection? - // TODO: check that we do the right thing when rcvr.IsInterface(). + if !types.IsInterfaceMethod(method.Type) && + (len(rcvr.RParams()) > 0 || + rcvr.IsPtr() && len(rcvr.Elem().RParams()) > 0) { // TODO: right detection? + // Don't need dictionary if we are reaching a method (possibly via + // an embedded field) which is an interface method. + // TODO: check that we do the right thing when method is an interface method. generic = true } if base.Debug.Unified != 0 { @@ -1786,12 +1790,6 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy } dot := typecheck.AddImplicitDots(ir.NewSelectorExpr(base.Pos, ir.OXDOT, nthis, method.Sym)) - if generic && dot.X != nthis && dot.X.Type().IsInterface() { - // We followed some embedded fields, and the last type was - // actually an interface, so no need for a dictionary. - generic = false - } - // generate call // It's not possible to use a tail call when dynamic linking on ppc64le. The // bad scenario is when a local call is made to the wrapper: the wrapper will @@ -1815,6 +1813,14 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy } else { fn.SetWrapper(true) // ignore frame for panic+recover matching var call *ir.CallExpr + + if generic && dot.X != nthis { + // TODO: for now, we don't try to generate dictionary wrappers for + // any methods involving embedded fields, because we're not + // generating the needed dictionaries in instantiateMethods. + generic = false + } + if generic { var args []ir.Node var targs []*types.Type @@ -1827,7 +1833,17 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy fmt.Printf("%s\n", ir.MethodSym(orig, method.Sym).Name) panic("multiple .inst.") } - args = append(args, getDictionary(".inst."+ir.MethodSym(orig, method.Sym).Name, targs)) // TODO: remove .inst. + // Temporary fix: the wrapper for an auto-generated + // pointer/non-pointer receiver method should share the + // same dictionary as the corresponding original + // (user-written) method. + baseOrig := orig + if baseOrig.IsPtr() && !method.Type.Recv().Type.IsPtr() { + baseOrig = baseOrig.Elem() + } else if !baseOrig.IsPtr() && method.Type.Recv().Type.IsPtr() { + baseOrig = types.NewPtr(baseOrig) + } + args = append(args, getDictionary(".inst."+ir.MethodSym(baseOrig, method.Sym).Name, targs)) // TODO: remove .inst. if indirect { args = append(args, ir.NewStarExpr(base.Pos, dot.X)) } else if methodrcvr.IsPtr() && methodrcvr.Elem() == dot.X.Type() { @@ -1852,6 +1868,9 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy } target := ir.AsNode(sym.Def) call = ir.NewCallExpr(base.Pos, ir.OCALL, target, args) + // Fill-in the generic method node that was not filled in + // in instantiateMethod. + method.Nname = fn.Nname } else { call = ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil) call.Args = ir.ParamNames(tfn.Type()) @@ -1924,23 +1943,6 @@ func MarkUsedIfaceMethod(n *ir.CallExpr) { r.Type = objabi.R_USEIFACEMETHOD } -// getDictionaryForInstantiation returns the dictionary that should be used for invoking -// the concrete instantiation described by inst. -func GetDictionaryForInstantiation(inst *ir.InstExpr) ir.Node { - targs := typecheck.TypesOf(inst.Targs) - if meth, ok := inst.X.(*ir.SelectorExpr); ok { - return GetDictionaryForMethod(meth.Selection.Nname.(*ir.Name), targs) - } - return GetDictionaryForFunc(inst.X.(*ir.Name), targs) -} - -func GetDictionaryForFunc(fn *ir.Name, targs []*types.Type) ir.Node { - return getDictionary(typecheck.MakeInstName(fn.Sym(), targs, false).Name, targs) -} -func GetDictionaryForMethod(meth *ir.Name, targs []*types.Type) ir.Node { - return getDictionary(typecheck.MakeInstName(meth.Sym(), targs, true).Name, targs) -} - // getDictionary returns the dictionary for the given named generic function // or method, with the given type arguments. // TODO: pass a reference to the generic function instead? We might need @@ -1964,14 +1966,7 @@ func getDictionary(name string, targs []*types.Type) ir.Node { // Initialize the dictionary, if we haven't yet already. if lsym := sym.Linksym(); len(lsym.P) == 0 { - off := 0 - // Emit an entry for each concrete type. - for _, t := range targs { - s := TypeLinksym(t) - off = objw.SymPtr(lsym, off, s, 0) - } - // TODO: subdictionaries - objw.Global(lsym, int32(off), obj.DUPOK|obj.RODATA) + base.Fatalf("Dictionary should have alredy been generated: %v", sym) } // Make a node referencing the dictionary symbol. diff --git a/test/typeparam/dictionaryCapture.go b/test/typeparam/dictionaryCapture.go index 1b2ee1de910..af508859e1c 100644 --- a/test/typeparam/dictionaryCapture.go +++ b/test/typeparam/dictionaryCapture.go @@ -10,12 +10,19 @@ package main +import ( + "fmt" +) + func main() { functions() methodExpressions() + genMethodExpressions[int](7) methodValues() + genMethodValues[int](7) interfaceMethods() globals() + recursive() } func g0[T any](x T) { @@ -72,6 +79,20 @@ func methodExpressions() { is77(f2(x)) } +func genMethodExpressions[T comparable](want T) { + x := s[T]{a: want} + f0 := s[T].g0 + f0(x) + f1 := s[T].g1 + if got := f1(x); got != want { + panic(fmt.Sprintf("f1(x) == %d, want %d", got, want)) + } + f2 := s[T].g2 + if got1, got2 := f2(x); got1 != want || got2 != want { + panic(fmt.Sprintf("f2(x) == %d, %d, want %d, %d", got1, got2, want, want)) + } +} + func methodValues() { x := s[int]{a:7} f0 := x.g0 @@ -82,6 +103,20 @@ func methodValues() { is77(f2()) } +func genMethodValues[T comparable](want T) { + x := s[T]{a: want} + f0 := x.g0 + f0() + f1 := x.g1 + if got := f1(); got != want { + panic(fmt.Sprintf("f1() == %d, want %d", got, want)) + } + f2 := x.g2 + if got1, got2 := f2(); got1 != want || got2 != want { + panic(fmt.Sprintf("f2() == %d, %d, want %d, %d", got1, got2, want, want)) + } +} + var x interface{ g0() g1()int @@ -124,3 +159,34 @@ func globals() { is7(ii1()) is77(ii2()) } + + +func recursive() { + if got, want := recur1[int](5), 110; got != want { + panic(fmt.Sprintf("recur1[int](5) = %d, want = %d", got, want)) + } +} + +type Integer interface { + int | int32 | int64 +} + +func recur1[T Integer](n T) T { + if n == 0 || n == 1 { + return T(1) + } else { + return n * recur2(n - 1) + } +} + +func recur2[T Integer](n T) T { + list := make([]T, n) + for i, _ := range list { + list[i] = T(i+1) + } + var sum T + for _, elt := range list { + sum += elt + } + return sum + recur1(n-1) +}