2
mac osx catalina (latest)
gfortran 9.2
fortran standard "gnu"

Through something like the code below, I came to understand that, at least in this case, that within the function get_number the result value is constructed, and then results are copied over to the receiving variable. Probably I've also done a no-no by having a pointer target a local variable inside that function, but perhaps the compiler was being nice to me by not deallocating a local variable so my pointer was still okay.

My question is, is this how all function calls behave, namely that the value, whether a simple type or a complex type, is constructed in memory, and then the values are copied over and the original result is thrown away? I was under the impression that the variable receiving the value of the function call was just given the memory of the constructed result.

Suppose I felt that lots of memory copying was wasteful, or suppose as in the code below that it breaks my code, and I wanted to avoid it when doing many many function calls. Is the solution to replace them with subroutine calls? But I like function calls. If memory copying is what happens normally, is there a way to avoid memory copying when doing function calls?

module nums
  type number
     real :: n
  end type number

  type number_parent
     type(number), pointer :: p
   contains
     procedure, pass(this) :: get_number
  end type number_parent

contains

  function get_number(this) result(n)
    class(number_parent) :: this
    type(number), allocatable, target :: n
    allocate(n)
    n%n = 1.0
    this%p => n
  end function get_number

end module nums

program main
  use nums
  implicit none

  type(number) :: n
  type(number_parent) :: np

  n = np%get_number()

  print *, n%n
  print *, np%p%n

  n%n = n%n + 1.0

  print *, n%n
  print *, np%p%n

end program main

program output

> ./a.out 
   1.00000000    
   1.00000000    
   2.00000000    
   1.00000000 
Vince W.
  • 3,561
  • 3
  • 31
  • 59
  • 1
    "Probably I've also done a no-no by having a pointer target a local variable inside that function". Yes, yes you have. – francescalus Aug 05 '20 at 19:57
  • When thinking about these sorts of things, don't forget to consider the constraints that _finalization_ add (for finalizable types, rather than here). – francescalus Aug 05 '20 at 19:59
  • 1
    Also recall that a function result can be allocatable or have other aspects which aren't compatible with the variable it'll be assigned to or the expression it's used in. – francescalus Aug 05 '20 at 20:08
  • yes I suppose that all makes sense, although I am not all that familiar with the concept of finalization. I did some searching, but if you happen to know a good quick link on the topic, I'd appreciate it. I suppose the answer then... subroutines, which did fix my broken code – Vince W. Aug 05 '20 at 20:17
  • A possible way to return an object w/o copy is to write a function that returns a pointer (that is allocated within the function), but this approach gives up all the advantages of allocatables in Fortran... (like automatic deallocation). I also tried to use move_alloc() to move the returned object to the left-hand side, but it was illegal. I guess if you might have chance to include pointers in a derived type later, subroutines (or an init() method/TBP) may turn out to be more convenient (depending on cases). – roygvib Aug 05 '20 at 21:39
  • On the topic of finalization? There are questions here about it but probably nothing "introductory". I'd recommend a good Fortran 2003+ book/tutorial on that (circular, because if it doesn't cover finalization it's not a good Fortran 2003+ book...) instead and you can likely find subtle recommendations around. – francescalus Aug 05 '20 at 22:35
  • 1
    Aspects from [an answer](https://stackoverflow.com/a/55068396/3157076) I wrote for another question may be relevant. In particular the quote about function results (wording has changed slightly in the current standard). – francescalus Aug 05 '20 at 22:40

1 Answers1

3

I understood your question is about a thing called return value optimization and you mainly were thinking about simple variables, non-pointer, non-allocatable. My answer will only touch these. I will also assume no advanced derived type features like polymorphism and finalization.

I will also assume you use the function only in a simple assignment. Of course, functions can appear in many complicated expressions in general.

When you return a simple variable from a function

   integer :: ext

   ext = func(1)

 contains

   integer function func(loc) result(res)
     res = loc + 1
   end function

  end

then what conceptually happens is that a local variable is used for the return value res and its value will be copied to the outer variable after the assignment.

In practice, if a separate variable will indeed be used, it will be allocated on the stack (that costs nothing, not just almost nothing, really nothing). Heap may be used for large objects (mostly arrays) and compilers often have settings for that, but normal variables will be on the stack. Then, when the function returns, the value is copied to the location of the outer variable ext and the stack pointer is reset (that just means some other function or subroutine will use that memory later).

The copying can indeed cost something and it may or may not be negligible. compilers will very often be able to optimize this and make the function to use the outer variable ext directly for its result variable res so that no copying is necessary. However, to see whether it is or is not happening you will have to check the machine code/assembly (or, if available, any intermediate representation) generated by the compiler. It may vary depending on the optimization level (-O1, -O2, -O3) and on the exact context of the function call in the source code.