Skip to content

Commit ea5dd71

Browse files
merged #1351
1 parent 65c5c0b commit ea5dd71

File tree

2 files changed

+136
-61
lines changed

2 files changed

+136
-61
lines changed

compiler/internal/analysis/info_test.go

Lines changed: 135 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -317,15 +317,15 @@ func TestBlocking_Defers_WithReturns_WithNamedFuncs(t *testing.T) {
317317
bt.assertNotBlocking(`nonBlockingPrint`)
318318

319319
bt.assertBlocking(`blockingBody`)
320-
bt.assertBlockingReturn(13)
320+
bt.assertBlockingReturn(13, ``)
321321

322322
bt.assertBlocking(`blockingArg`)
323323
// The defer is non-blocking so the return is not blocking
324324
// even though the function is blocking.
325-
bt.assertNotBlockingReturn(18)
325+
bt.assertNotBlockingReturn(18, ``)
326326

327327
bt.assertNotBlocking(`notBlocking`)
328-
bt.assertNotBlockingReturn(23)
328+
bt.assertNotBlockingReturn(23, ``)
329329
}
330330

331331
func TestBlocking_Defers_WithMultipleReturns(t *testing.T) {
@@ -365,13 +365,13 @@ func TestBlocking_Defers_WithMultipleReturns(t *testing.T) {
365365
bt.assertBlocking(`foo`)
366366
bt.assertNotBlockingLit(4, ``)
367367
// Early escape from function without blocking defers is not blocking.
368-
bt.assertNotBlockingReturn(11)
368+
bt.assertNotBlockingReturn(11, ``)
369369
bt.assertNotBlockingLit(14, ``)
370370
// Function has had blocking by this point but no blocking defers yet.
371-
bt.assertNotBlockingReturn(20)
371+
bt.assertNotBlockingReturn(20, ``)
372372
bt.assertBlockingLit(24, ``)
373373
// The return is blocking because of a blocking defer.
374-
bt.assertBlockingReturn(28)
374+
bt.assertBlockingReturn(28, ``)
375375
// Technically the return on line 31 is not blocking since the defer that
376376
// is blocking can only exit through the return on line 28, but it would be
377377
// difficult to determine which defers would only affect certain returns
@@ -384,7 +384,7 @@ func TestBlocking_Defers_WithMultipleReturns(t *testing.T) {
384384
//
385385
// For now we simply build up the list of defers as we go making
386386
// the return on line 31 also blocking.
387-
bt.assertBlockingReturn(31)
387+
bt.assertBlockingReturn(31, ``)
388388
}
389389

390390
func TestBlocking_Defers_WithReturnsAndDefaultBlocking(t *testing.T) {
@@ -453,12 +453,12 @@ func TestBlocking_Defers_WithReturnsAndDefaultBlocking(t *testing.T) {
453453
bt.assertBlocking(`deferMappedFuncCall`)
454454

455455
// All of these returns are blocking because they have blocking defers.
456-
bt.assertBlockingReturn(17)
457-
bt.assertBlockingReturn(22)
458-
bt.assertBlockingReturn(28)
459-
bt.assertBlockingReturn(34)
460-
bt.assertBlockingReturn(40)
461-
bt.assertBlockingReturn(49)
456+
bt.assertBlockingReturn(17, ``)
457+
bt.assertBlockingReturn(22, ``)
458+
bt.assertBlockingReturn(28, ``)
459+
bt.assertBlockingReturn(34, ``)
460+
bt.assertBlockingReturn(40, ``)
461+
bt.assertBlockingReturn(49, ``)
462462
}
463463

464464
func TestBlocking_Defers_WithReturnsAndDeferBuiltin(t *testing.T) {
@@ -477,7 +477,7 @@ func TestBlocking_Defers_WithReturnsAndDeferBuiltin(t *testing.T) {
477477

478478
bt.assertFuncInstCount(1)
479479
bt.assertNotBlocking(`deferBuiltinCall`)
480-
bt.assertNotBlockingReturn(10)
480+
bt.assertNotBlockingReturn(10, ``)
481481
}
482482

483483
func TestBlocking_Defers_WithReturnsInLoops(t *testing.T) {
@@ -575,14 +575,14 @@ func TestBlocking_Defers_WithReturnsInLoops(t *testing.T) {
575575
// When the following 2 returns are defined there are no defers, however,
576576
// because of the loop, the blocking defers defined after the return will
577577
// block the returns.
578-
bt.assertBlockingReturn(12)
579-
bt.assertBlockingReturn(22)
580-
bt.assertBlockingReturn(31)
581-
bt.assertBlockingReturn(44)
582-
bt.assertBlockingReturn(52)
583-
bt.assertBlockingReturn(66)
584-
bt.assertBlockingReturn(73)
585-
bt.assertBlockingReturn(77)
578+
bt.assertBlockingReturn(12, ``)
579+
bt.assertBlockingReturn(22, ``)
580+
bt.assertBlockingReturn(31, ``)
581+
bt.assertBlockingReturn(44, ``)
582+
bt.assertBlockingReturn(52, ``)
583+
bt.assertBlockingReturn(66, ``)
584+
bt.assertBlockingReturn(73, ``)
585+
bt.assertBlockingReturn(77, ``)
586586
}
587587

588588
func TestBlocking_Defers_WithReturnsInLoopsInLoops(t *testing.T) {
@@ -652,19 +652,19 @@ func TestBlocking_Defers_WithReturnsInLoopsInLoops(t *testing.T) {
652652
bt.assertFuncInstCount(4)
653653
bt.assertBlocking(`blocking`)
654654
bt.assertBlocking(`forLoopTheLoop`)
655-
bt.assertNotBlockingReturn(9)
656-
bt.assertBlockingReturn(13)
657-
bt.assertBlockingReturn(17)
658-
bt.assertBlockingReturn(21)
659-
bt.assertBlockingReturn(25)
660-
bt.assertBlockingReturn(28)
655+
bt.assertNotBlockingReturn(9, ``)
656+
bt.assertBlockingReturn(13, ``)
657+
bt.assertBlockingReturn(17, ``)
658+
bt.assertBlockingReturn(21, ``)
659+
bt.assertBlockingReturn(25, ``)
660+
bt.assertBlockingReturn(28, ``)
661661
bt.assertBlocking(`rangeLoopTheLoop`)
662-
bt.assertBlockingReturn(36)
663-
bt.assertBlockingReturn(41)
662+
bt.assertBlockingReturn(36, ``)
663+
bt.assertBlockingReturn(41, ``)
664664
bt.assertBlocking(`noopThenLoop`)
665-
bt.assertNotBlockingReturn(48)
666-
bt.assertBlockingReturn(54)
667-
bt.assertBlockingReturn(58)
665+
bt.assertNotBlockingReturn(48, ``)
666+
bt.assertBlockingReturn(54, ``)
667+
bt.assertBlockingReturn(58, ``)
668668
}
669669

670670
func TestBlocking_Returns_WithoutDefers(t *testing.T) {
@@ -693,19 +693,67 @@ func TestBlocking_Returns_WithoutDefers(t *testing.T) {
693693
return true // line 22
694694
}`)
695695
bt.assertBlocking(`blocking`)
696-
bt.assertBlockingReturn(4)
696+
bt.assertBlockingReturn(4, ``)
697697

698698
bt.assertBlocking(`blockingBeforeReturn`)
699-
bt.assertNotBlockingReturn(9)
699+
bt.assertNotBlockingReturn(9, ``)
700700

701701
bt.assertBlocking(`indirectlyBlocking`)
702-
bt.assertBlockingReturn(13)
702+
bt.assertBlockingReturn(13, ``)
703703

704704
bt.assertBlocking(`indirectlyBlockingBeforeReturn`)
705-
bt.assertNotBlockingReturn(18)
705+
bt.assertNotBlockingReturn(18, ``)
706706

707707
bt.assertNotBlocking(`notBlocking`)
708-
bt.assertNotBlockingReturn(22)
708+
bt.assertNotBlockingReturn(22, ``)
709+
}
710+
711+
func TestBlocking_Defers_WithReturnsInInstances(t *testing.T) {
712+
// This is an example of a deferred function literal inside of
713+
// an instance of a generic function affecting the return
714+
// differently based on the type arguments of the instance.
715+
bt := newBlockingTest(t,
716+
`package test
717+
718+
type BazBlocker struct {
719+
c chan bool
720+
}
721+
func (bb BazBlocker) Baz() {
722+
println(<-bb.c)
723+
}
724+
725+
type BazNotBlocker struct {}
726+
func (bnb BazNotBlocker) Baz() {
727+
println("hi")
728+
}
729+
730+
type Foo interface { Baz() }
731+
func FooBaz[T Foo]() bool {
732+
defer func() { // line 17
733+
var foo T
734+
foo.Baz()
735+
}()
736+
return true // line 21
737+
}
738+
739+
func main() {
740+
FooBaz[BazBlocker]()
741+
FooBaz[BazNotBlocker]()
742+
}`)
743+
744+
bt.assertFuncInstCount(5)
745+
bt.assertBlocking(`BazBlocker.Baz`)
746+
bt.assertNotBlocking(`BazNotBlocker.Baz`)
747+
bt.assertBlockingInst(`pkg/test.FooBaz<pkg/test.BazBlocker>`)
748+
bt.assertNotBlockingInst(`pkg/test.FooBaz<pkg/test.BazNotBlocker>`)
749+
bt.assertBlocking(`main`)
750+
751+
bt.assertFuncLitCount(2)
752+
bt.assertBlockingLit(17, `pkg/test.BazBlocker`)
753+
bt.assertNotBlockingLit(17, `pkg/test.BazNotBlocker`)
754+
755+
bt.assertBlockingReturn(21, `pkg/test.BazBlocker`)
756+
bt.assertNotBlockingReturn(21, `pkg/test.BazNotBlocker`)
709757
}
710758

711759
func TestBlocking_Defers_WithReturnsAndOtherPackages(t *testing.T) {
@@ -737,10 +785,10 @@ func TestBlocking_Defers_WithReturnsAndOtherPackages(t *testing.T) {
737785
bt := newBlockingTestWithOtherPackage(t, testSrc, otherSrc)
738786

739787
bt.assertBlocking(`deferOtherBlocking`)
740-
bt.assertBlockingReturn(7)
788+
bt.assertBlockingReturn(7, ``)
741789

742790
bt.assertNotBlocking(`deferOtherNotBlocking`)
743-
bt.assertNotBlockingReturn(12)
791+
bt.assertNotBlockingReturn(12, ``)
744792
}
745793

746794
func TestBlocking_FunctionLiteral(t *testing.T) {
@@ -1652,15 +1700,23 @@ func (bt *blockingTest) assertFuncInstCount(expCount int) {
16521700
}
16531701

16541702
func (bt *blockingTest) assertFuncLitCount(expCount int) {
1655-
if got := len(bt.pkgInfo.funcLitInfos); got != expCount {
1703+
got := 0
1704+
for _, fis := range bt.pkgInfo.funcLitInfos {
1705+
got += len(fis)
1706+
}
1707+
if got != expCount {
16561708
bt.f.T.Errorf(`Got %d function literal infos but expected %d.`, got, expCount)
1657-
pos := make([]string, 0, len(bt.pkgInfo.funcLitInfos))
1658-
for fl := range bt.pkgInfo.funcLitInfos {
1659-
pos = append(pos, bt.f.FileSet.Position(fl.Pos()).String())
1709+
1710+
lits := make([]string, 0, len(bt.pkgInfo.funcLitInfos))
1711+
for fl, fis := range bt.pkgInfo.funcLitInfos {
1712+
pos := bt.f.FileSet.Position(fl.Pos()).String()
1713+
for _, fi := range fis {
1714+
lits = append(lits, pos+`<`+fi.typeArgs.String()+`>`)
1715+
}
16601716
}
1661-
sort.Strings(pos)
1662-
for i := range pos {
1663-
bt.f.T.Logf(` %d. %q`, i+1, pos)
1717+
sort.Strings(lits)
1718+
for i := range lits {
1719+
bt.f.T.Logf(` %d. %q`, i+1, lits[i])
16641720
}
16651721
}
16661722
}
@@ -1716,13 +1772,13 @@ func (bt *blockingTest) isTypesFuncBlocking(funcName string) bool {
17161772

17171773
func (bt *blockingTest) assertBlockingLit(lineNo int, typeArgsStr string) {
17181774
if !bt.isFuncLitBlocking(lineNo, typeArgsStr) {
1719-
bt.f.T.Errorf(`Got FuncLit at line %d with type args %q as not blocking but expected it to be blocking.`, lineNo, typeArgsStr)
1775+
bt.f.T.Errorf(`Got FuncLit at line %d with type args %q as not blocking but expected it to be blocking.`, lineNo, typeArgsStr)
17201776
}
17211777
}
17221778

17231779
func (bt *blockingTest) assertNotBlockingLit(lineNo int, typeArgsStr string) {
17241780
if bt.isFuncLitBlocking(lineNo, typeArgsStr) {
1725-
bt.f.T.Errorf(`Got FuncLit at line %d with type args %q as blocking but expected it to be not blocking.`, lineNo, typeArgsStr)
1781+
bt.f.T.Errorf(`Got FuncLit at line %d with type args %q as blocking but expected it to be not blocking.`, lineNo, typeArgsStr)
17261782
}
17271783
}
17281784

@@ -1745,7 +1801,7 @@ func (bt *blockingTest) isFuncLitBlocking(lineNo int, typeArgsStr string) bool {
17451801

17461802
bt.f.T.Logf("FuncList instances:")
17471803
for i, fi := range fis {
1748-
bt.f.T.Logf("\t%d. %q\n", i, fi.typeArgs.String())
1804+
bt.f.T.Logf("\t%d. %q\n", i+1, fi.typeArgs.String())
17491805
}
17501806
bt.f.T.Fatalf(`No FuncInfo found for FuncLit at line %d with type args %q.`, lineNo, typeArgsStr)
17511807
return false
@@ -1772,34 +1828,52 @@ func (bt *blockingTest) isFuncInstBlocking(instanceStr string) bool {
17721828
}
17731829
bt.f.T.Logf(`Function instances found in package info:`)
17741830
for i, inst := range instances {
1775-
bt.f.T.Logf(` %d. %s`, i+1, inst.String())
1831+
bt.f.T.Logf("\t%d. %s", i+1, inst.String())
17761832
}
17771833
bt.f.T.Fatalf(`No function instance found for %q in package info.`, instanceStr)
17781834
return false
17791835
}
17801836

1781-
func (bt *blockingTest) assertBlockingReturn(lineNo int) {
1782-
if !bt.isReturnBlocking(lineNo) {
1783-
bt.f.T.Errorf(`Got return at line %d as not blocking but expected it to be blocking.`, lineNo)
1837+
func (bt *blockingTest) assertBlockingReturn(lineNo int, typeArgsStr string) {
1838+
if !bt.isReturnBlocking(lineNo, typeArgsStr) {
1839+
bt.f.T.Errorf(`Got return at line %d (%q) as not blocking but expected it to be blocking.`, lineNo, typeArgsStr)
17841840
}
17851841
}
17861842

1787-
func (bt *blockingTest) assertNotBlockingReturn(lineNo int) {
1788-
if bt.isReturnBlocking(lineNo) {
1789-
bt.f.T.Errorf(`Got return at line %d as blocking but expected it to be not blocking.`, lineNo)
1843+
func (bt *blockingTest) assertNotBlockingReturn(lineNo int, typeArgsStr string) {
1844+
if bt.isReturnBlocking(lineNo, typeArgsStr) {
1845+
bt.f.T.Errorf(`Got return at line %d (%q) as blocking but expected it to be not blocking.`, lineNo, typeArgsStr)
17901846
}
17911847
}
17921848

1793-
func (bt *blockingTest) isReturnBlocking(lineNo int) bool {
1849+
func (bt *blockingTest) isReturnBlocking(lineNo int, typeArgsStr string) bool {
17941850
ret := srctesting.GetNodeAtLineNo[*ast.ReturnStmt](bt.file, bt.f.FileSet, lineNo)
17951851
if ret == nil {
17961852
bt.f.T.Fatalf(`ReturnStmt on line %d not found in the AST.`, lineNo)
17971853
}
1854+
1855+
foundInfo := []*FuncInfo{}
17981856
for _, info := range bt.pkgInfo.allInfos {
1799-
if blocking, found := info.Blocking[ret]; found {
1800-
return blocking
1857+
for _, rs := range info.returnStmts {
1858+
if rs.analyzeStack[len(rs.analyzeStack)-1] == ret {
1859+
if info.typeArgs.String() == typeArgsStr {
1860+
// Found info that matches the type args and
1861+
// has the return statement so return the blocking value.
1862+
return info.Blocking[ret]
1863+
}
1864+
1865+
// Wrong instance, record for error message in the case
1866+
// that the correct one instance is not found.
1867+
foundInfo = append(foundInfo, info)
1868+
break
1869+
}
18011870
}
18021871
}
1803-
// If not found in any info.Blocking, then it is not blocking.
1872+
1873+
bt.f.T.Logf("FuncInfo instances with ReturnStmt at line %d:", lineNo)
1874+
for i, info := range foundInfo {
1875+
bt.f.T.Logf("\t%d. %q\n", i+1, info.typeArgs.String())
1876+
}
1877+
bt.f.T.Fatalf(`No FuncInfo found for ReturnStmt at line %d with type args %q.`, lineNo, typeArgsStr)
18041878
return false
18051879
}

compiler/typesutil/typelist.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ func (tl TypeList) String() string {
1919
return buf.String()
2020
}
2121

22+
// Equal returns true if both lists of type arguments are identical.
2223
func (tl TypeList) Equal(other TypeList) bool {
2324
if len(tl) != len(other) {
2425
return false

0 commit comments

Comments
 (0)