@@ -380,3 +380,120 @@ end
380380using  Base. Experimental:  @opaque 
381381f_oc_getfield (x) =  (@opaque  ()-> x)()
382382@test  fully_eliminated (f_oc_getfield, Tuple{Int})
383+ 
384+ #  check if `x` is a statically-resolved call of a function whose name is `sym`
385+ isinvoke (@nospecialize (x), sym:: Symbol ) =  isinvoke (x, mi-> mi. def. name=== sym)
386+ function  isinvoke (@nospecialize (x), pred)
387+     if  Meta. isexpr (x, :invoke )
388+         return  pred (x. args[1 ]:: Core.MethodInstance )
389+     end 
390+     return  false 
391+ end 
392+ code_typed1 (args... ; kwargs... ) =  (first∘ first)(code_typed (args... ; kwargs... )):: Core.CodeInfo 
393+ 
394+ @testset  " @inline/@noinline annotation before definition" begin 
395+     m =  Module ()
396+     @eval  m begin 
397+         @inline  function  _def_inline (x)
398+             #  this call won't be resolved and thus will prevent inlining to happen if we don't
399+             #  annotate `@inline` at the top of this function body
400+             return  unresolved_call (x)
401+         end 
402+         def_inline (x) =  _def_inline (x)
403+         @noinline  _def_noinline (x) =  x #  obviously will be inlined otherwise
404+         def_noinline (x) =  _def_noinline (x)
405+ 
406+         #  test that they don't conflict with other "before-definition" macros
407+         @inline  Base. @aggressive_constprop  function  _def_inline_noconflict (x)
408+             #  this call won't be resolved and thus will prevent inlining to happen if we don't
409+             #  annotate `@inline` at the top of this function body
410+             return  unresolved_call (x)
411+         end 
412+         def_inline_noconflict (x) =  _def_inline_noconflict (x)
413+         @noinline  Base. @aggressive_constprop  _def_noinline_noconflict (x) =  x #  obviously will be inlined otherwise
414+         def_noinline_noconflict (x) =  _def_noinline_noconflict (x)
415+     end 
416+ 
417+     let  ci =  code_typed1 (m. def_inline, (Int,))
418+         @test  all (ci. code) do  x
419+             ! isinvoke (x, :_def_inline )
420+         end 
421+     end 
422+     let  ci =  code_typed1 (m. def_noinline, (Int,))
423+         @test  any (ci. code) do  x
424+             isinvoke (x, :_def_noinline )
425+         end 
426+     end 
427+     #  test that they don't conflict with other "before-definition" macros
428+     let  ci =  code_typed1 (m. def_inline_noconflict, (Int,))
429+         @test  all (ci. code) do  x
430+             ! isinvoke (x, :_def_inline_noconflict )
431+         end 
432+     end 
433+     let  ci =  code_typed1 (m. def_noinline_noconflict, (Int,))
434+         @test  any (ci. code) do  x
435+             isinvoke (x, :_def_noinline_noconflict )
436+         end 
437+     end 
438+ end 
439+ 
440+ @testset  " @inline/@noinline annotation within a function body" begin 
441+     m =  Module ()
442+     @eval  m begin 
443+         function  _body_inline (x)
444+             @inline 
445+             #  this call won't be resolved and thus will prevent inlining to happen if we don't
446+             #  annotate `@inline` at the top of this function body
447+             return  unresolved_call (x)
448+         end 
449+         body_inline (x) =  _body_inline (x)
450+         function  _body_noinline (x)
451+             @noinline 
452+             return  x #  obviously will be inlined otherwise
453+         end 
454+         body_noinline (x) =  _body_noinline (x)
455+ 
456+         #  test annotations for `do` blocks
457+         @inline  simple_caller (a) =  a ()
458+         function  do_inline (x)
459+             simple_caller () do 
460+                 @inline 
461+                 #  this call won't be resolved and thus will prevent inlining to happen if we don't
462+                 #  annotate `@inline` at the top of this anonymous function body
463+                 return  unresolved_call (x)
464+             end 
465+         end 
466+         function  do_noinline (x)
467+             simple_caller () do 
468+                 @noinline 
469+                 return  x #  obviously will be inlined otherwise
470+             end 
471+         end 
472+     end 
473+ 
474+     let  ci =  code_typed1 (m. body_inline, (Int,))
475+         @test  all (ci. code) do  x
476+             ! isinvoke (x, :_body_inline )
477+         end 
478+     end 
479+     let  ci =  code_typed1 (m. body_noinline, (Int,))
480+         @test  any (ci. code) do  x
481+             isinvoke (x, :_body_noinline )
482+         end 
483+     end 
484+     #  test annotations for `do` blocks
485+     let  ci =  code_typed1 (m. do_inline, (Int,))
486+         #  what we test here is that both `simple_caller` and the anonymous function that the
487+         #  `do` block creates should inlined away, and as a result there is only the unresolved call
488+         @test  all (ci. code) do  x
489+             ! isinvoke (x, :simple_caller ) && 
490+             ! isinvoke (x, mi-> startswith (string (mi. def. name), ' #' 
491+         end 
492+     end 
493+     let  ci =  code_typed1 (m. do_noinline, (Int,))
494+         #  the anonymous function that the `do` block created shouldn't be inlined here
495+         @test  any (ci. code) do  x
496+             isinvoke (x, mi-> startswith (string (mi. def. name), ' #' 
497+         end 
498+     end 
499+ end 
0 commit comments