cmd/compile: fix stack frame info for calls in receiver slot

Previously, after inlining a call, we made a second pass to rewrite
the AST's position information to record the inlined stack frame. The
call arguments were part of this AST, but it would be incorrect to
rewrite them too, so extra effort was made to temporarily remove them
while the position rewriting was done.

However, this extra logic was only done for regular arguments: it was
not done for receiver arguments. Consequently if m was inlined in
"f().m(g(), h())", g and h would have correct call frames, but f would
appear to be called by m.

The fix taken by this CL is to merge setpos into inlsubst and only
rewrite position information for nodes that were actually copied from
the original function AST body. As a side benefit, this eliminates an
extra AST pass and some AST walking code.

Fixes #21879.

Change-Id: I22b25c208313fc25c358d3a2eebfc9b012400084
Reviewed-on: https://go-review.googlesource.com/64470
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go
index f9f273f..3a34ab92 100644
--- a/src/cmd/compile/internal/gc/inl.go
+++ b/src/cmd/compile/internal/gc/inl.go
@@ -740,10 +740,18 @@
 
 	inlgen++
 
+	parent := -1
+	if b := Ctxt.PosTable.Pos(n.Pos).Base(); b != nil {
+		parent = b.InliningIndex()
+	}
+	newIndex := Ctxt.InlTree.Add(parent, n.Pos, fn.Sym.Linksym())
+
 	subst := inlsubst{
-		retlabel: retlabel,
-		retvars:  retvars,
-		inlvars:  inlvars,
+		retlabel:    retlabel,
+		retvars:     retvars,
+		inlvars:     inlvars,
+		bases:       make(map[*src.PosBase]*src.PosBase),
+		newInlIndex: newIndex,
 	}
 
 	body := subst.list(fn.Func.Inl)
@@ -762,28 +770,6 @@
 	call.Type = n.Type
 	call.SetTypecheck(1)
 
-	// Hide the args from setPos -- the parameters to the inlined
-	// call already have good line numbers that should be preserved.
-	args := as.Rlist
-	as.Rlist.Set(nil)
-
-	// Rewrite the line information for the inlined AST.
-	parent := -1
-	callBase := Ctxt.PosTable.Pos(n.Pos).Base()
-	if callBase != nil {
-		parent = callBase.InliningIndex()
-	}
-	newIndex := Ctxt.InlTree.Add(parent, n.Pos, fn.Sym.Linksym())
-	setpos := &setPos{
-		bases:       make(map[*src.PosBase]*src.PosBase),
-		newInlIndex: newIndex,
-	}
-	setpos.node(call)
-
-	as.Rlist.Set(args.Slice())
-
-	//dumplist("call body", body);
-
 	n = call
 
 	// transitive inlining
@@ -861,6 +847,14 @@
 	retvars []*Node
 
 	inlvars map[*Node]*Node
+
+	// bases maps from original PosBase to PosBase with an extra
+	// inlined call frame.
+	bases map[*src.PosBase]*src.PosBase
+
+	// newInlIndex is the index of the inlined call frame to
+	// insert for inlined nodes.
+	newInlIndex int
 }
 
 // list inlines a list of nodes.
@@ -908,7 +902,6 @@
 	//		dump("Return before substitution", n);
 	case ORETURN:
 		m := nod(OGOTO, subst.retlabel, nil)
-
 		m.Ninit.Set(subst.list(n.Ninit))
 
 		if len(subst.retvars) != 0 && n.List.Len() != 0 {
@@ -934,6 +927,7 @@
 	case OGOTO, OLABEL:
 		m := nod(OXXX, nil, nil)
 		*m = *n
+		m.Pos = subst.updatedPos(m.Pos)
 		m.Ninit.Set(nil)
 		p := fmt.Sprintf("%s·%d", n.Left.Sym.Name, inlgen)
 		m.Left = newname(lookup(p))
@@ -943,6 +937,7 @@
 
 	m := nod(OXXX, nil, nil)
 	*m = *n
+	m.Pos = subst.updatedPos(m.Pos)
 	m.Ninit.Set(nil)
 
 	if n.Op == OCLOSURE {
@@ -959,50 +954,13 @@
 	return m
 }
 
-// setPos is a visitor to update position info with a new inlining index.
-type setPos struct {
-	bases       map[*src.PosBase]*src.PosBase
-	newInlIndex int
-}
-
-func (s *setPos) nodelist(ll Nodes) {
-	for _, n := range ll.Slice() {
-		s.node(n)
-	}
-}
-
-func (s *setPos) node(n *Node) {
-	if n == nil {
-		return
-	}
-	if n.Op == OLITERAL || n.Op == OTYPE {
-		if n.Sym != nil {
-			// This node is not a copy, so don't clobber position.
-			return
-		}
-	}
-
-	// don't clobber names, unless they're freshly synthesized
-	if n.Op != ONAME || !n.Pos.IsKnown() {
-		n.Pos = s.updatedPos(n)
-	}
-
-	s.node(n.Left)
-	s.node(n.Right)
-	s.nodelist(n.List)
-	s.nodelist(n.Rlist)
-	s.nodelist(n.Ninit)
-	s.nodelist(n.Nbody)
-}
-
-func (s *setPos) updatedPos(n *Node) src.XPos {
-	pos := Ctxt.PosTable.Pos(n.Pos)
+func (subst *inlsubst) updatedPos(xpos src.XPos) src.XPos {
+	pos := Ctxt.PosTable.Pos(xpos)
 	oldbase := pos.Base() // can be nil
-	newbase := s.bases[oldbase]
+	newbase := subst.bases[oldbase]
 	if newbase == nil {
-		newbase = src.NewInliningBase(oldbase, s.newInlIndex)
-		pos.SetBase(newbase)
-		s.bases[oldbase] = newbase
+		newbase = src.NewInliningBase(oldbase, subst.newInlIndex)
+		subst.bases[oldbase] = newbase
 	}
 	pos.SetBase(newbase)
 	return Ctxt.PosTable.XPos(pos)
diff --git a/test/fixedbugs/issue21879.go b/test/fixedbugs/issue21879.go
new file mode 100644
index 0000000..1029ca0
--- /dev/null
+++ b/test/fixedbugs/issue21879.go
@@ -0,0 +1,37 @@
+// run
+
+// Copyright 2017 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 (
+	"runtime"
+)
+
+func main() {
+	println(caller().frame.Function)
+
+	// Used to erroneously print "main.call.name" instead of
+	// "main.main".
+	println(caller().name())
+}
+
+func caller() call {
+	var pcs [3]uintptr
+	n := runtime.Callers(1, pcs[:])
+	frames := runtime.CallersFrames(pcs[:n])
+	frame, _ := frames.Next()
+	frame, _ = frames.Next()
+
+	return call{frame: frame}
+}
+
+type call struct {
+	frame runtime.Frame
+}
+
+func (c call) name() string {
+	return c.frame.Function
+}
diff --git a/test/fixedbugs/issue21879.out b/test/fixedbugs/issue21879.out
new file mode 100644
index 0000000..066f1a8
--- /dev/null
+++ b/test/fixedbugs/issue21879.out
@@ -0,0 +1,2 @@
+main.main
+main.main