diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 8a8ed8608c..d2d9d5a550 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -232,6 +232,16 @@ func (r *reader) pos() src.XPos { return base.Ctxt.PosTable.XPos(r.pos0()) } +// origPos reads a position from the bitstream, and returns both the +// original raw position and an inlining-adjusted position. +func (r *reader) origPos() (origPos, inlPos src.XPos) { + r.suppressInlPos++ + origPos = r.pos() + r.suppressInlPos-- + inlPos = r.inlPos(origPos) + return +} + func (r *reader) pos0() src.Pos { r.Sync(pkgbits.SyncPos) if !r.Bool() { @@ -2114,12 +2124,12 @@ func (r *reader) expr() (res ir.Node) { return typecheck.Callee(r.obj()) case exprFuncInst: - pos := r.pos() + origPos, pos := r.origPos() wrapperFn, baseFn, dictPtr := r.funcInst(pos) if wrapperFn != nil { return wrapperFn } - return r.curry(pos, false, baseFn, dictPtr, nil) + return r.curry(origPos, false, baseFn, dictPtr, nil) case exprConst: pos := r.pos() @@ -2149,7 +2159,7 @@ func (r *reader) expr() (res ir.Node) { case exprMethodVal: recv := r.expr() - pos := r.pos() + origPos, pos := r.origPos() wrapperFn, baseFn, dictPtr := r.methodExpr() // For simple wrapperFn values, the existing machinery for creating @@ -2198,7 +2208,7 @@ func (r *reader) expr() (res ir.Node) { // For more complicated method expressions, we construct a // function literal wrapper. - return r.curry(pos, true, baseFn, recv, dictPtr) + return r.curry(origPos, true, baseFn, recv, dictPtr) case exprMethodExpr: recv := r.typ() @@ -2214,7 +2224,7 @@ func (r *reader) expr() (res ir.Node) { addr = true } - pos := r.pos() + origPos, pos := r.origPos() wrapperFn, baseFn, dictPtr := r.methodExpr() // If we already have a wrapper and don't need to do anything with @@ -2237,7 +2247,7 @@ func (r *reader) expr() (res ir.Node) { return typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, ir.TypeNode(recv), method.Sel)).(*ir.SelectorExpr) } - return r.methodExprWrap(pos, recv, implicits, deref, addr, baseFn, dictPtr) + return r.methodExprWrap(origPos, recv, implicits, deref, addr, baseFn, dictPtr) case exprIndex: x := r.expr() @@ -2567,7 +2577,7 @@ func (pr *pkgReader) objDictName(idx pkgbits.Index, implicits, explicits []*type // If nilCheck is true and arg0 is an interface value, then it's // checked to be non-nil as an initial step at the point of evaluating // the function literal itself. -func (r *reader) curry(pos src.XPos, ifaceHack bool, fun ir.Node, arg0, arg1 ir.Node) ir.Node { +func (r *reader) curry(origPos src.XPos, ifaceHack bool, fun ir.Node, arg0, arg1 ir.Node) ir.Node { var captured ir.Nodes captured.Append(fun, arg0) if arg1 != nil { @@ -2591,13 +2601,13 @@ func (r *reader) curry(pos src.XPos, ifaceHack bool, fun ir.Node, arg0, arg1 ir. r.syntheticTailCall(pos, fun, args) } - return r.syntheticClosure(pos, typ, ifaceHack, captured, addBody) + return r.syntheticClosure(origPos, typ, ifaceHack, captured, addBody) } // methodExprWrap returns a function literal that changes method's // first parameter's type to recv, and uses implicits/deref/addr to // select the appropriate receiver parameter to pass to method. -func (r *reader) methodExprWrap(pos src.XPos, recv *types.Type, implicits []int, deref, addr bool, method, dictPtr ir.Node) ir.Node { +func (r *reader) methodExprWrap(origPos src.XPos, recv *types.Type, implicits []int, deref, addr bool, method, dictPtr ir.Node) ir.Node { var captured ir.Nodes captured.Append(method) @@ -2648,12 +2658,13 @@ func (r *reader) methodExprWrap(pos src.XPos, recv *types.Type, implicits []int, r.syntheticTailCall(pos, fn, args) } - return r.syntheticClosure(pos, typ, false, captured, addBody) + return r.syntheticClosure(origPos, typ, false, captured, addBody) } // syntheticClosure constructs a synthetic function literal for -// currying dictionary arguments. pos is the position used for the -// closure. typ is the function literal's signature type. +// currying dictionary arguments. origPos is the position used for the +// closure, which must be a non-inlined position. typ is the function +// literal's signature type. // // captures is a list of expressions that need to be evaluated at the // point of function literal evaluation and captured by the function @@ -2664,7 +2675,7 @@ func (r *reader) methodExprWrap(pos src.XPos, recv *types.Type, implicits []int, // list of captured values passed back has the captured variables for // use within the function literal, corresponding to the expressions // in captures. -func (r *reader) syntheticClosure(pos src.XPos, typ *types.Type, ifaceHack bool, captures ir.Nodes, addBody func(pos src.XPos, r *reader, captured []ir.Node)) ir.Node { +func (r *reader) syntheticClosure(origPos src.XPos, typ *types.Type, ifaceHack bool, captures ir.Nodes, addBody func(pos src.XPos, r *reader, captured []ir.Node)) ir.Node { // isSafe reports whether n is an expression that we can safely // defer to evaluating inside the closure instead, to avoid storing // them into the closure. @@ -2681,9 +2692,15 @@ func (r *reader) syntheticClosure(pos src.XPos, typ *types.Type, ifaceHack bool, return false } - fn := ir.NewClosureFunc(pos, r.curfn != nil) + // The ODCLFUNC and its body need to use the original position, but + // the OCLOSURE node and any Init statements should use the inlined + // position instead. See also the explanation in reader.funcLit. + inlPos := r.inlPos(origPos) + + fn := ir.NewClosureFunc(origPos, r.curfn != nil) fn.SetWrapper(true) clo := fn.OClosure + clo.SetPos(inlPos) ir.NameClosure(clo, r.curfn) setType(fn.Nname, typ) @@ -2696,13 +2713,13 @@ func (r *reader) syntheticClosure(pos src.XPos, typ *types.Type, ifaceHack bool, continue // skip capture; can reference directly } - tmp := r.tempCopy(pos, n, &init) - ir.NewClosureVar(pos, fn, tmp) + tmp := r.tempCopy(inlPos, n, &init) + ir.NewClosureVar(origPos, fn, tmp) // We need to nil check interface receivers at the point of method // value evaluation, ugh. if ifaceHack && i == 1 && n.Type().IsInterface() { - check := ir.NewUnaryExpr(pos, ir.OCHECKNIL, ir.NewUnaryExpr(pos, ir.OITAB, tmp)) + check := ir.NewUnaryExpr(inlPos, ir.OCHECKNIL, ir.NewUnaryExpr(inlPos, ir.OITAB, tmp)) init.Append(typecheck.Stmt(check)) } } @@ -2720,7 +2737,7 @@ func (r *reader) syntheticClosure(pos src.XPos, typ *types.Type, ifaceHack bool, } assert(next == len(r.closureVars)) - addBody(pos, r, captured) + addBody(origPos, r, captured) }} bodyReader[fn] = pri pri.funcBody(fn) diff --git a/test/typeparam/issue58513.go b/test/typeparam/issue58513.go new file mode 100644 index 0000000000..37cb5725ca --- /dev/null +++ b/test/typeparam/issue58513.go @@ -0,0 +1,60 @@ +// run + +// Copyright 2023 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. + +// Some derived-type expressions require the compiler to synthesize +// function literals to plumb sub-dictionaries appropriately. +// However, when these expressions are inlined, we were constructing +// the function literal bodies with the inline-adjusted positions +// instead of the original (inline-free) positions, which could lead +// to infinite loops when unwinding the stack. + +package main + +import "runtime" + +func assert[_ any]() { + panic(0) +} + +func Assert[To any]() func() { + return assert[To] +} + +type asserter[_ any] struct{} + +func (asserter[_]) assert() { + panic(0) +} + +func AssertMV[To any]() func() { + return asserter[To]{}.assert +} + +func AssertME[To any]() func(asserter[To]) { + return asserter[To].assert +} + +var me = AssertME[string]() + +var tests = []func(){ + Assert[int](), + AssertMV[int](), + func() { me(asserter[string]{}) }, +} + +func main() { + for _, test := range tests { + func() { + defer func() { + recover() + + // Check that we can unwind the stack without infinite looping. + runtime.Caller(1000) + }() + test() + }() + } +}