Source file test/escape_alias.go

     1  // errorcheck -0 -d=escapealias=1
     2  
     3  //go:build goexperiment.runtimefreegc
     4  
     5  // Copyright 2025 The Go Authors. All rights reserved.
     6  // Use of this source code is governed by a BSD-style
     7  // license that can be found in the LICENSE file.
     8  
     9  // Test recognizing certain patterns of usage,
    10  // currently focused on whether a slice is aliased.
    11  
    12  package escapealias
    13  
    14  import "runtime"
    15  
    16  // Basic examples.
    17  //
    18  // Some of these directly overlap with later tests below, but are presented at the start
    19  // to help show the big picture (before going into more variations).
    20  
    21  var alias []int
    22  
    23  func basic1() {
    24  	// A simple append with no aliasing of s.
    25  	var s []int
    26  	s = append(s, 0) // ERROR "append using non-aliased slice"
    27  	_ = s
    28  }
    29  
    30  func basic2() []int {
    31  	// The slice can escape.
    32  	var s []int
    33  	s = append(s, 0) // ERROR "append using non-aliased slice"
    34  	return s
    35  }
    36  
    37  func basic3() {
    38  	// A simple example of s being aliased.
    39  	// We give up when we see the aliasing.
    40  	var s []int
    41  	alias = s
    42  	s = append(s, 0)
    43  	_ = s
    44  }
    45  
    46  func basic4() {
    47  	// The analysis is conservative, giving up on
    48  	// IR nodes it doesn't understand. It does not
    49  	// yet understand comparisons, for example.
    50  	var s []int
    51  	_ = s == nil
    52  	s = append(s, 0)
    53  	_ = s
    54  }
    55  
    56  func basic5() {
    57  	// We also give up if s is assigned to another variable.
    58  	var s []int
    59  	s2 := s
    60  	s2 = append(s2, 0)
    61  	_ = s2
    62  }
    63  
    64  func basic6() {
    65  	// A self-assigning append does not create an alias,
    66  	// so s is still unaliased when we reach the second append here.
    67  	var s []int
    68  	s = append(s, 0) // ERROR "append using non-aliased slice"
    69  	s = append(s, 0) // ERROR "append using non-aliased slice"
    70  	_ = s
    71  }
    72  
    73  func basic7() {
    74  	// An append can be unaliased if it happens before aliasing.
    75  	var s []int
    76  	s = append(s, 0) // ERROR "append using non-aliased slice"
    77  	alias = s
    78  	s = append(s, 0)
    79  	_ = s
    80  }
    81  
    82  func basic8() {
    83  	// Aliasing anywhere in a loop means we give up for the whole loop body,
    84  	// even if the aliasing is after the append in the loop body.
    85  	var s []int
    86  	for range 10 {
    87  		s = append(s, 0)
    88  		alias = s
    89  	}
    90  	_ = s
    91  }
    92  
    93  func basic9() {
    94  	// Aliases after a loop do not affect whether this is aliasing in the loop.
    95  	var s []int
    96  	for range 10 {
    97  		s = append(s, 0) // ERROR "append using non-aliased slice"
    98  	}
    99  	alias = s
   100  	_ = s
   101  }
   102  
   103  func basic10() {
   104  	// We track the depth at which a slice is declared vs. aliased,
   105  	// which helps for example with nested loops.
   106  	// In this example, the aliasing occurs after both loops are done.
   107  	var s []int
   108  	for range 10 {
   109  		for range 10 {
   110  			s = append(s, 0) // ERROR "append using non-aliased slice"
   111  		}
   112  	}
   113  	alias = s
   114  }
   115  
   116  func basic11() {
   117  	// In contrast, here the aliasing occurs in the outer loop body.
   118  	var s []int
   119  	for range 10 {
   120  		for range 10 {
   121  			s = append(s, 0)
   122  		}
   123  		alias = s
   124  	}
   125  }
   126  
   127  // Some variations on single appends.
   128  
   129  func singleAppend1() []int {
   130  	var s []int
   131  	s = append(s, 0) // ERROR "append using non-aliased slice"
   132  	return s
   133  }
   134  
   135  func singleAppend2() {
   136  	var s []int
   137  	alias = s
   138  	s = append(s, 0)
   139  }
   140  
   141  func singleAppend3() {
   142  	var s []int
   143  	s = append(s, 0) // ERROR "append using non-aliased slice"
   144  	alias = s
   145  }
   146  
   147  func singleAppend4() {
   148  	var s []int
   149  	p := &s
   150  	_ = p
   151  	s = append(s, 0)
   152  }
   153  
   154  func singleAppend5(s []int) {
   155  	s = append(s, 0)
   156  }
   157  
   158  func singleAppend6() {
   159  	var s []int
   160  	alias, _ = s, 0
   161  	s = append(s, 0)
   162  }
   163  
   164  // Examples with variations on slice declarations.
   165  
   166  func sliceDeclaration1() {
   167  	s := []int{}
   168  	s = append(s, 0) // ERROR "append using non-aliased slice"
   169  }
   170  
   171  func sliceDeclaration2() {
   172  	s := []int{1, 2, 3}
   173  	s = append(s, 0) // ERROR "append using non-aliased slice"
   174  }
   175  
   176  func sliceDeclaration3() {
   177  	s := make([]int, 3)
   178  	s = append(s, 0) // ERROR "append using non-aliased slice"
   179  }
   180  
   181  func sliceDeclaration4() {
   182  	s := []int{}
   183  	alias = s
   184  	s = append(s, 0)
   185  }
   186  
   187  func sliceDeclaration5() {
   188  	s := []int{1, 2, 3}
   189  	alias = s
   190  	s = append(s, 0)
   191  }
   192  
   193  func sliceDeclaration6() {
   194  	s := make([]int, 3)
   195  	alias = s
   196  	s = append(s, 0)
   197  }
   198  
   199  func sliceDeclaration7() {
   200  	s, x := []int{}, 0
   201  	s = append(s, x) // ERROR "append using non-aliased slice"
   202  }
   203  
   204  // Basic loops. First, a single loop.
   205  
   206  func loops1a() {
   207  	var s []int
   208  	for i := range 10 {
   209  		s = append(s, i) // ERROR "append using non-aliased slice"
   210  	}
   211  }
   212  
   213  func loops1b() {
   214  	var s []int
   215  	for i := range 10 {
   216  		alias = s
   217  		s = append(s, i)
   218  	}
   219  }
   220  
   221  func loops1c() {
   222  	var s []int
   223  	for i := range 10 {
   224  		s = append(s, i)
   225  		alias = s
   226  	}
   227  }
   228  
   229  func loops1d() {
   230  	var s []int
   231  	for i := range 10 {
   232  		s = append(s, i) // ERROR "append using non-aliased slice"
   233  	}
   234  	alias = s
   235  }
   236  
   237  func loops1e() {
   238  	var s []int
   239  	for i := range use(s) {
   240  		s = append(s, i)
   241  	}
   242  }
   243  
   244  func loops1f() {
   245  	var s []int
   246  	for i := range use(s) {
   247  		s = append(s, i)
   248  	}
   249  	s = append(s, 0)
   250  }
   251  
   252  // Nested loops with s declared outside the loops.
   253  
   254  func loops2a() {
   255  	var s []int
   256  	for range 10 {
   257  		for i := range 10 {
   258  			s = append(s, i) // ERROR "append using non-aliased slice"
   259  		}
   260  	}
   261  }
   262  
   263  func loops2b() {
   264  	var s []int
   265  	for range 10 {
   266  		alias = s
   267  		for i := range 10 {
   268  			s = append(s, i)
   269  		}
   270  	}
   271  }
   272  
   273  func loops2c() {
   274  	var s []int
   275  	for range 10 {
   276  		for i := range 10 {
   277  			s = append(s, i)
   278  		}
   279  		alias = s
   280  	}
   281  }
   282  
   283  func loops2d() {
   284  	var s []int
   285  	for range 10 {
   286  		for i := range 10 {
   287  			s = append(s, i) // ERROR "append using non-aliased slice"
   288  		}
   289  	}
   290  	alias = s
   291  }
   292  
   293  func loops2e() {
   294  	var s []int
   295  	for range use(s) {
   296  		for i := range 10 {
   297  			s = append(s, i)
   298  		}
   299  		s = append(s, 0)
   300  	}
   301  	s = append(s, 0)
   302  }
   303  
   304  func loops2f() {
   305  	var s []int
   306  	for range 10 {
   307  		for i := range use(s) {
   308  			s = append(s, i)
   309  		}
   310  		s = append(s, 0)
   311  	}
   312  	s = append(s, 0)
   313  }
   314  
   315  // Nested loops with s declared inside the first loop.
   316  
   317  func loops3a() {
   318  	for range 10 {
   319  		var s []int
   320  		for i := range 10 {
   321  			s = append(s, i) // ERROR "append using non-aliased slice"
   322  		}
   323  	}
   324  }
   325  
   326  func loops3b() {
   327  	for range 10 {
   328  		var s []int
   329  		for i := range 10 {
   330  			alias = s
   331  			s = append(s, i)
   332  		}
   333  	}
   334  }
   335  
   336  func loops3c() {
   337  	for range 10 {
   338  		var s []int
   339  		for i := range 10 {
   340  			s = append(s, i)
   341  			alias = s
   342  		}
   343  	}
   344  }
   345  
   346  func loops3d() {
   347  	for range 10 {
   348  		var s []int
   349  		for i := range 10 {
   350  			s = append(s, i) // ERROR "append using non-aliased slice"
   351  		}
   352  		alias = s
   353  	}
   354  }
   355  
   356  func loops3e() {
   357  	for range 10 {
   358  		var s []int
   359  		for i := range use(s) {
   360  			s = append(s, i)
   361  		}
   362  		s = append(s, 0)
   363  	}
   364  }
   365  
   366  // Loops using OFOR instead of ORANGE.
   367  
   368  func loops4a() {
   369  	var s []int
   370  	for i := 0; i < 10; i++ {
   371  		s = append(s, i) // ERROR "append using non-aliased slice"
   372  	}
   373  }
   374  
   375  func loops4b() {
   376  	var s []int
   377  	for i := 0; i < 10; i++ {
   378  		alias = s
   379  		s = append(s, i)
   380  	}
   381  }
   382  
   383  func loops4c() {
   384  	var s []int
   385  	for i := 0; i < 10; i++ {
   386  		s = append(s, i)
   387  		alias = s
   388  	}
   389  }
   390  
   391  func loops4d() {
   392  	var s []int
   393  	for i := 0; i < 10; i++ {
   394  		s = append(s, i) // ERROR "append using non-aliased slice"
   395  	}
   396  	alias = s
   397  }
   398  
   399  // Loops with some initialization variations.
   400  
   401  func loopsInit1() {
   402  	var i int
   403  	for s := []int{}; i < 10; i++ {
   404  		s = append(s, i) // ERROR "append using non-aliased slice"
   405  	}
   406  }
   407  
   408  func loopsInit2() {
   409  	var i int
   410  	for s := []int{}; i < 10; i++ {
   411  		s = append(s, i)
   412  		alias = s
   413  	}
   414  }
   415  
   416  func loopsInit3() {
   417  	var i int
   418  	for s := []int{}; i < 10; i++ {
   419  		for range 10 {
   420  			s = append(s, i) // ERROR "append using non-aliased slice"
   421  		}
   422  	}
   423  }
   424  
   425  func loopsInit5() {
   426  	var i int
   427  	for s := []int{}; i < 10; i++ {
   428  		for range 10 {
   429  			s = append(s, i)
   430  			alias = s
   431  		}
   432  	}
   433  }
   434  
   435  func loopsInit5b() {
   436  	var i int
   437  	for s := []int{}; i < 10; i++ {
   438  		for range 10 {
   439  			s = append(s, i)
   440  		}
   441  		alias = s
   442  	}
   443  }
   444  
   445  func loopsInit6() {
   446  	for range 10 {
   447  		var i int
   448  		for s := []int{}; i < 10; i++ {
   449  			s = append(s, i) // ERROR "append using non-aliased slice"
   450  		}
   451  	}
   452  }
   453  
   454  func loopsInit7() {
   455  	for range 10 {
   456  		var i int
   457  		for s := []int{}; i < 10; i++ {
   458  			s = append(s, i)
   459  			alias = s
   460  		}
   461  	}
   462  }
   463  
   464  // Some initialization variations with use of s in the for or range.
   465  
   466  func loopsInit8() {
   467  	var s []int
   468  	for use(s) == 0 {
   469  		s = append(s, 0)
   470  	}
   471  }
   472  
   473  func loopsInit9() {
   474  	for s := []int{}; use(s) == 0; {
   475  		s = append(s, 0)
   476  	}
   477  }
   478  
   479  func loopsInit10() {
   480  	for s := []int{}; ; use(s) {
   481  		s = append(s, 0)
   482  	}
   483  }
   484  
   485  func loopsInit11() {
   486  	var s [][]int
   487  	for _, s2 := range s {
   488  		s = append(s, s2)
   489  	}
   490  }
   491  
   492  // Examples of calling functions that get inlined,
   493  // starting with a simple pass-through function.
   494  
   495  // TODO(thepudds): we handle many of these starting in https://go.dev/cl/712422
   496  
   497  func inlineReturn(param []int) []int {
   498  	return param
   499  }
   500  
   501  func inline1a() {
   502  	var s []int
   503  	s = inlineReturn(s)
   504  	s = append(s, 0)
   505  }
   506  
   507  func inline1b() {
   508  	var s []int
   509  	for range 10 {
   510  		s = inlineReturn(s)
   511  		s = append(s, 0)
   512  	}
   513  }
   514  
   515  func inline1c() {
   516  	var s []int
   517  	for range 10 {
   518  		s = inlineReturn(s)
   519  		alias = s
   520  		s = append(s, 0)
   521  	}
   522  }
   523  
   524  func inline1d() {
   525  	var s []int
   526  	for range 10 {
   527  		s = inlineReturn(s)
   528  		s = append(s, 0)
   529  		alias = s
   530  	}
   531  }
   532  
   533  // Examples with an inlined function that uses append.
   534  
   535  func inlineAppend(param []int) []int {
   536  	param = append(param, 0)
   537  	// TODO(thepudds): could in theory also handle a direct 'return append(param, 0)'
   538  	return param
   539  }
   540  
   541  func inline2a() {
   542  	var s []int
   543  	s = inlineAppend(s)
   544  	s = append(s, 0)
   545  }
   546  
   547  func inline2b() {
   548  	var s []int
   549  	for range 10 {
   550  		s = inlineAppend(s)
   551  		s = append(s, 0)
   552  	}
   553  }
   554  
   555  func inline2c() {
   556  	var s []int
   557  	for range 10 {
   558  		s = inlineAppend(s)
   559  		alias = s
   560  		s = append(s, 0)
   561  	}
   562  }
   563  
   564  func inline2d() {
   565  	var s []int
   566  	for range 10 {
   567  		s = inlineAppend(s)
   568  		s = append(s, 0)
   569  		alias = s
   570  	}
   571  }
   572  
   573  // Examples calling non-inlined functions that do and do not escape.
   574  
   575  var sink interface{}
   576  
   577  //go:noinline
   578  func use(s []int) int { return 0 } // s content does not escape
   579  
   580  //go:noinline
   581  func escape(s []int) int { sink = s; return 0 } // s content escapes
   582  
   583  func call1() {
   584  	var s []int
   585  	s = append(s, 0) // ERROR "append using non-aliased slice"
   586  	use(s)
   587  }
   588  
   589  // TODO(thepudds): OK to disallow this for now, but would be nice to allow this given use(s) is non-escaping.
   590  func call2() {
   591  	var s []int
   592  	use(s)
   593  	s = append(s, 0)
   594  }
   595  
   596  func call3() {
   597  	var s []int
   598  	s = append(s, use(s))
   599  }
   600  
   601  func call4() {
   602  	var s []int
   603  	for i := range 10 {
   604  		s = append(s, i)
   605  		use(s)
   606  	}
   607  }
   608  
   609  func callEscape1() {
   610  	var s []int
   611  	s = append(s, 0) // ERROR "append using non-aliased slice"
   612  	escape(s)
   613  }
   614  
   615  func callEscape2() {
   616  	var s []int
   617  	escape(s)
   618  	s = append(s, 0)
   619  }
   620  
   621  func callEscape3() {
   622  	var s []int
   623  	s = append(s, escape(s))
   624  }
   625  
   626  func callEscape4() {
   627  	var s []int
   628  	for i := range 10 {
   629  		s = append(s, i)
   630  		escape(s)
   631  	}
   632  }
   633  
   634  // Examples of some additional expressions we understand.
   635  
   636  func expr1() {
   637  	var s []int
   638  	_ = len(s)
   639  	_ = cap(s)
   640  	s = append(s, 0) // ERROR "append using non-aliased slice"
   641  }
   642  
   643  // Examples of some expressions or statements we do not understand.
   644  // Some of these we could handle in the future, but some likely not.
   645  
   646  func notUnderstood1() {
   647  	var s []int
   648  	s = append(s[:], 0)
   649  }
   650  
   651  func notUnderstood2() {
   652  	// Note: we must be careful if we analyze slice expressions.
   653  	// See related comment about slice expressions in (*aliasAnalysis).analyze.
   654  	var s []int
   655  	s = append(s, 0) // ERROR "append using non-aliased slice"
   656  	s = s[1:]        // s no longer points to the base of the heap object.
   657  	s = append(s, 0)
   658  }
   659  
   660  func notUnderstood3() {
   661  	// The first append is currently the heart of slices.Grow.
   662  	var s []int
   663  	n := 1000
   664  	s = append(s[:cap(s)], make([]int, n)...)[:len(s)]
   665  	s = append(s, 0)
   666  }
   667  
   668  func notUnderstood4() []int {
   669  	// A return statement could be allowed to use the slice in a loop
   670  	// because we cannot revisit the append once we return.
   671  	var s []int
   672  	for i := range 10 {
   673  		s = append(s, 0)
   674  		if i > 5 {
   675  			return s
   676  		}
   677  	}
   678  	return s
   679  }
   680  
   681  func notUnderstood5() {
   682  	// AddCleanup is an example function call that we do not understand.
   683  	// See related comment about specials in (*aliasAnalysis).analyze.
   684  	var s []int
   685  	runtime.AddCleanup(&s, func(int) {}, 0)
   686  	s = append(s, 0)
   687  }
   688  
   689  // Examples with closures.
   690  
   691  func closure1() {
   692  	var s []int // declared outside the closure
   693  	f := func() {
   694  		for i := range 10 {
   695  			s = append(s, i)
   696  		}
   697  	}
   698  	_ = f // avoid calling f, which would just get inlined
   699  }
   700  
   701  // TODO(thepudds): it's probably ok that we currently allow this. Could conservatively
   702  // disallow if needed.
   703  func closure2() {
   704  	f := func() {
   705  		var s []int // declared inside the closure
   706  		for i := range 10 {
   707  			s = append(s, i) // ERROR "append using non-aliased slice"
   708  		}
   709  	}
   710  	_ = f // avoid calling f, which would just get inlined
   711  }
   712  
   713  // Examples with goto and labels.
   714  
   715  func goto1() {
   716  	var s []int
   717  label:
   718  	s = append(s, 0)
   719  	alias = s
   720  	goto label
   721  }
   722  
   723  func goto2() {
   724  	var s []int
   725  	s = append(s, 0) // ERROR "append using non-aliased slice"
   726  	alias = s
   727  label:
   728  	goto label
   729  }
   730  
   731  func goto3() {
   732  	var s []int
   733  label:
   734  	for i := range 10 {
   735  		s = append(s, i)
   736  	}
   737  	goto label
   738  }
   739  
   740  func break1() {
   741  	var s []int
   742  label:
   743  	for i := range 10 {
   744  		s = append(s, i)
   745  		break label
   746  	}
   747  }
   748  
   749  // Examples with iterators.
   750  
   751  func collect[E any](seq Seq[E]) []E {
   752  	var result []E
   753  	for v := range seq {
   754  		result = append(result, v)
   755  	}
   756  	return result
   757  }
   758  
   759  func count(yield func(int) bool) {
   760  	for i := range 10 {
   761  		if !yield(i) {
   762  			return
   763  		}
   764  	}
   765  }
   766  
   767  func iteratorUse1() {
   768  	var s []int
   769  	s = collect(count)
   770  	_ = s
   771  }
   772  
   773  func iteratorUse2() {
   774  	var s []int
   775  	s = collect(count)
   776  	s = append(s, 0)
   777  }
   778  
   779  type Seq[E any] func(yield func(E) bool)
   780  

View as plain text