Shallow copying should work differently with Lpython and Lfortran
I tried the following python snippet for ( Cpython vs Lpython ) and then it's fortran equivalent for ( Gfortran vs Lfortran )
def g():
l = [1, 2, 3]
l2 = l
l[0] = 4
print(l2)
g()
Here in my opinion l2 should be modified when l is modified as they point to the same memory address.
(lp) faze-geek@DESKTOP-497JDCU:~/lpython/lpython$ python try.py
[4, 2, 3]
(lp) faze-geek@DESKTOP-497JDCU:~/lpython/lpython$ ./src/bin/lpython try.py
[1, 2, 3]
Now the equivalent fortran example -
program equivalent
implicit none
integer :: l(3), l2(3)
l = [1, 2, 3]
l2 = l
l(1) = 4
print *, l2
end program equivalent
Here l2 doesn't get modified in both the compilers which makes me question if Lpython handles shallow copying through = operator (or maybe the python copy() function) similarly.
(lf) faze-geek@DESKTOP-497JDCU:~/lfortran/lfortran$ gfortran try.f90 && ./a.out
1 2 3
(lf) faze-geek@DESKTOP-497JDCU:~/lfortran/lfortran$ ./src/bin/lfortran try.f90
1
2
3
@certik Is this issue valid or is the output expected. I had also mentioned this as a potential issue in my GSOC proposal, but did not expect both fortran compilers to give 1 2 3.
Related: https://github.com/lcompilers/lpython/issues/191, https://github.com/lcompilers/lpython/issues/1263.
I know we discussed this exact issue somewhere, but I can't find it. Essentially:
l = [1, 2, 3]
l2 = l
l[0] = 4
print(l2)
print(l)
Cannot be allowed, because it behaves differently in LPython and CPython. This can be allowed:
l = [1, 2, 3]
l2 = l
l[0] = 4
print(l2)
The frontend would need to rewrite it as:
l2 = [1, 2, 3]
l2[0] = 4
print(l2)
But until then I would not allow it. If you do l2 = l, then you cannot use "l" in any way, since the LPython semantics (copy on assignment, no reference counting) is different. So the compiler must give a compile time error message if you use "l" after it was assigned somewhere.
That will fix this issue.
We can also introduce reference counted pointer (RCP) into ASR, and possibly use that. For speed reasons, most likely one would do it explicitly, something like l2 = rcp(l1). Also the question becomes what type l2 will be, possibly l2: RCP[list] = rcp(l1), etc. I would tackle that later.