Skip to content

Commit 363cd66

Browse files
aarzilliheschi
authored andcommitted
cmd/compile: assign correct declaration line to DIE of captured vars
Fixes the declaration line reported in the DW_AT_decl_line for variables captured in a closure. Fixes #36542 Change-Id: I228d32b57121fd62c4615c2ef71a6e8da616a1e2 Reviewed-on: https://go-review.googlesource.com/c/go/+/214637 Reviewed-by: Heschi Kreinick <[email protected]> Run-TryBot: Heschi Kreinick <[email protected]>
1 parent afd691c commit 363cd66

File tree

2 files changed

+79
-30
lines changed

2 files changed

+79
-30
lines changed

src/cmd/compile/internal/gc/pgen.go

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -426,24 +426,7 @@ func debuginfo(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.S
426426

427427
var varScopes []ScopeID
428428
for _, decl := range decls {
429-
pos := decl.Pos
430-
if decl.Name.Defn != nil && (decl.Name.Captured() || decl.Name.Byval()) {
431-
// It's not clear which position is correct for captured variables here:
432-
// * decl.Pos is the wrong position for captured variables, in the inner
433-
// function, but it is the right position in the outer function.
434-
// * decl.Name.Defn is nil for captured variables that were arguments
435-
// on the outer function, however the decl.Pos for those seems to be
436-
// correct.
437-
// * decl.Name.Defn is the "wrong" thing for variables declared in the
438-
// header of a type switch, it's their position in the header, rather
439-
// than the position of the case statement. In principle this is the
440-
// right thing, but here we prefer the latter because it makes each
441-
// instance of the header variable local to the lexical block of its
442-
// case statement.
443-
// This code is probably wrong for type switch variables that are also
444-
// captured.
445-
pos = decl.Name.Defn.Pos
446-
}
429+
pos := declPos(decl)
447430
varScopes = append(varScopes, findScope(fn.Func.Marks, pos))
448431
}
449432

@@ -455,6 +438,27 @@ func debuginfo(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.S
455438
return scopes, inlcalls
456439
}
457440

441+
func declPos(decl *Node) src.XPos {
442+
if decl.Name.Defn != nil && (decl.Name.Captured() || decl.Name.Byval()) {
443+
// It's not clear which position is correct for captured variables here:
444+
// * decl.Pos is the wrong position for captured variables, in the inner
445+
// function, but it is the right position in the outer function.
446+
// * decl.Name.Defn is nil for captured variables that were arguments
447+
// on the outer function, however the decl.Pos for those seems to be
448+
// correct.
449+
// * decl.Name.Defn is the "wrong" thing for variables declared in the
450+
// header of a type switch, it's their position in the header, rather
451+
// than the position of the case statement. In principle this is the
452+
// right thing, but here we prefer the latter because it makes each
453+
// instance of the header variable local to the lexical block of its
454+
// case statement.
455+
// This code is probably wrong for type switch variables that are also
456+
// captured.
457+
return decl.Name.Defn.Pos
458+
}
459+
return decl.Pos
460+
}
461+
458462
// createSimpleVars creates a DWARF entry for every variable declared in the
459463
// function, claiming that they are permanently on the stack.
460464
func createSimpleVars(apDecls []*Node) ([]*Node, []*dwarf.Var, map[*Node]bool) {
@@ -505,7 +509,7 @@ func createSimpleVar(n *Node) *dwarf.Var {
505509
}
506510
}
507511
}
508-
declpos := Ctxt.InnermostPos(n.Pos)
512+
declpos := Ctxt.InnermostPos(declPos(n))
509513
return &dwarf.Var{
510514
Name: n.Sym.Name,
511515
IsReturnValue: n.Class() == PPARAMOUT,

src/cmd/compile/internal/gc/scope_test.go

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package gc_test
77
import (
88
"cmd/internal/objfile"
99
"debug/dwarf"
10+
"fmt"
1011
"internal/testenv"
1112
"io/ioutil"
1213
"os"
@@ -39,6 +40,12 @@ type testline struct {
3940
// Must be ordered alphabetically.
4041
// Set to nil to skip the check.
4142
vars []string
43+
44+
// decl is the list of variables declared at this line.
45+
decl []string
46+
47+
// declBefore is the list of variables declared at or before this line.
48+
declBefore []string
4249
}
4350

4451
var testfile = []testline{
@@ -58,11 +65,11 @@ var testfile = []testline{
5865
{line: "var floatch = make(chan float64)"},
5966
{line: "var iface interface{}"},
6067
{line: "func TestNestedFor() {", vars: []string{"var a int"}},
61-
{line: " a := 0"},
68+
{line: " a := 0", decl: []string{"a"}},
6269
{line: " f1(a)"},
63-
{line: " for i := 0; i < 5; i++ {", scopes: []int{1}, vars: []string{"var i int"}},
70+
{line: " for i := 0; i < 5; i++ {", scopes: []int{1}, vars: []string{"var i int"}, decl: []string{"i"}},
6471
{line: " f2(i)", scopes: []int{1}},
65-
{line: " for i := 0; i < 5; i++ {", scopes: []int{1, 2}, vars: []string{"var i int"}},
72+
{line: " for i := 0; i < 5; i++ {", scopes: []int{1, 2}, vars: []string{"var i int"}, decl: []string{"i"}},
6673
{line: " f3(i)", scopes: []int{1, 2}},
6774
{line: " }"},
6875
{line: " f4(i)", scopes: []int{1}},
@@ -153,7 +160,7 @@ var testfile = []testline{
153160
{line: "}"},
154161
{line: "func TestClosureScope() {", vars: []string{"var a int", "var b int", "var f func(int)"}},
155162
{line: " a := 1; b := 1"},
156-
{line: " f := func(c int) {", scopes: []int{0}, vars: []string{"arg c int", "var &b *int", "var a int", "var d int"}},
163+
{line: " f := func(c int) {", scopes: []int{0}, vars: []string{"arg c int", "var &b *int", "var a int", "var d int"}, declBefore: []string{"&b", "a"}},
157164
{line: " d := 3"},
158165
{line: " f1(c); f1(d)"},
159166
{line: " if e := 3; e != 0 {", scopes: []int{1}, vars: []string{"var e int"}},
@@ -286,7 +293,18 @@ func TestScopeRanges(t *testing.T) {
286293
if len(out) > 0 {
287294
varsok = checkVars(testfile[i].vars, out[len(out)-1].vars)
288295
if !varsok {
289-
t.Logf("variable mismatch at line %d %q for scope %d: expected: %v got: %v\n", i, testfile[i].line, out[len(out)-1].id, testfile[i].vars, out[len(out)-1].vars)
296+
t.Logf("variable mismatch at line %d %q for scope %d: expected: %v got: %v\n", i+1, testfile[i].line, out[len(out)-1].id, testfile[i].vars, out[len(out)-1].vars)
297+
}
298+
for j := range testfile[i].decl {
299+
if line := declLineForVar(out[len(out)-1].vars, testfile[i].decl[j]); line != i+1 {
300+
t.Errorf("wrong declaration line for variable %s, expected %d got: %d", testfile[i].decl[j], i+1, line)
301+
}
302+
}
303+
304+
for j := range testfile[i].declBefore {
305+
if line := declLineForVar(out[len(out)-1].vars, testfile[i].declBefore[j]); line > i+1 {
306+
t.Errorf("wrong declaration line for variable %s, expected %d (or less) got: %d", testfile[i].declBefore[j], i+1, line)
307+
}
290308
}
291309
}
292310
}
@@ -323,25 +341,43 @@ func checkScopes(tgt []int, out []*lexblock) bool {
323341
return true
324342
}
325343

326-
func checkVars(tgt, out []string) bool {
344+
func checkVars(tgt []string, out []variable) bool {
327345
if len(tgt) != len(out) {
328346
return false
329347
}
330348
for i := range tgt {
331-
if tgt[i] != out[i] {
349+
if tgt[i] != out[i].expr {
332350
return false
333351
}
334352
}
335353
return true
336354
}
337355

356+
func declLineForVar(scope []variable, name string) int {
357+
for i := range scope {
358+
if scope[i].name() == name {
359+
return scope[i].declLine
360+
}
361+
}
362+
return -1
363+
}
364+
338365
type lexblock struct {
339366
id int
340367
ranges [][2]uint64
341-
vars []string
368+
vars []variable
342369
scopes []lexblock
343370
}
344371

372+
type variable struct {
373+
expr string
374+
declLine int
375+
}
376+
377+
func (v *variable) name() string {
378+
return strings.Split(v.expr, " ")[1]
379+
}
380+
345381
type line struct {
346382
file string
347383
lineno int
@@ -369,20 +405,22 @@ func readScope(ctxt *scopexplainContext, scope *lexblock, entry *dwarf.Entry) {
369405
}
370406
switch e.Tag {
371407
case 0:
372-
sort.Strings(scope.vars)
408+
sort.Slice(scope.vars, func(i, j int) bool {
409+
return scope.vars[i].expr < scope.vars[j].expr
410+
})
373411
return
374412
case dwarf.TagFormalParameter:
375413
typ, err := ctxt.dwarfData.Type(e.Val(dwarf.AttrType).(dwarf.Offset))
376414
if err != nil {
377415
panic(err)
378416
}
379-
scope.vars = append(scope.vars, "arg "+e.Val(dwarf.AttrName).(string)+" "+typ.String())
417+
scope.vars = append(scope.vars, entryToVar(e, "arg", typ))
380418
case dwarf.TagVariable:
381419
typ, err := ctxt.dwarfData.Type(e.Val(dwarf.AttrType).(dwarf.Offset))
382420
if err != nil {
383421
panic(err)
384422
}
385-
scope.vars = append(scope.vars, "var "+e.Val(dwarf.AttrName).(string)+" "+typ.String())
423+
scope.vars = append(scope.vars, entryToVar(e, "var", typ))
386424
case dwarf.TagLexDwarfBlock:
387425
scope.scopes = append(scope.scopes, lexblock{id: ctxt.scopegen})
388426
ctxt.scopegen++
@@ -391,6 +429,13 @@ func readScope(ctxt *scopexplainContext, scope *lexblock, entry *dwarf.Entry) {
391429
}
392430
}
393431

432+
func entryToVar(e *dwarf.Entry, kind string, typ dwarf.Type) variable {
433+
return variable{
434+
fmt.Sprintf("%s %s %s", kind, e.Val(dwarf.AttrName).(string), typ.String()),
435+
int(e.Val(dwarf.AttrDeclLine).(int64)),
436+
}
437+
}
438+
394439
// markLines marks all lines that belong to this scope with this scope
395440
// Recursively calls markLines for all children scopes.
396441
func (scope *lexblock) markLines(pcln objfile.Liner, lines map[line][]*lexblock) {

0 commit comments

Comments
 (0)