Description
Here S
is any type (i32, f64, Struct, Array, etc.).
In:
def f(x: In[S]) -> None:
x[5] = 5 # Assignment not allowed
InOut:
def f(x: InOut[S]) -> None:
x[5] = 5 # Allowed
Out:
def f(x: Out[S]) -> None:
x[5] = 5 # Allowed
When we write just:
def f(x: S) -> None:
x[5] = 5 # ?
Then we have to decide what the default intent is. The three options that make the most sense are In, InOut, Unspecified
, all of which are supported in ASR. The Unspecified intent mean that it can be any of In
, InOut
, Out
, and we don't know which one. This is problematic, because the caller then doesn't know if you do f(5)
if 5
should be passed by value (for In), or by reference (for InOut / Out). In Fortran we have to support it in ASR, but in Python I would not allow it, I would always specify intent (implicitly or explicitly), so ASR is always exactly one of In, InOut or Out. I think that is better for optimizations and checking.
A good backwards compatible choice is that default of x: S
will be x: In[S]
, so assignment is NOT allowed:
def f(x: S) -> None:
x[5] = 5 # Assignment not allowed
Later, if we wanted, we can change the default to InOut, so assignment would be allowed and it would not break any existing code. However, given the fact that int
can only be In
(see below), for consistency defaulting everything to In
seems like a good consistent choice.
I think being restrictive by default is good, and users opt-in for less restrictions.
Finally, there is one Python specific issue: the Python semantics only allows InOut and Out for composite types like Class, Array, List. It is not allowed for primitive types like int, float, which are passed "by value" in CPython, so they can only be "In". As a result, x: InOut[i32]
is a compile time error.
Note: Fortran also allows unspecified intent, and "value" attribute. I think this allows to actually assign to the argument, without modifying it in the caller. We CAN later support this, but I would start with being restrictive and not allow Unspecified intent. It does not close the door to later add it. I've never used this feature in Fortran, and I always specify intent, I think the compiler should even enforce it by default. So I recommend in LPython to start with the above minimal design, always require In, InOut, Out as the only options in ASR, and if it is not specified in the Python source code, the LPython frontend will default to In
and set In
in ASR. Later it can be changed / extended, without breaking anything.