Skip to content

Returning structs from functions #1289

Closed
@czgdp1807

Description

@czgdp1807

One can return the functions from structs in one of the following ways. Each one of these except the last one can be made to break,

  1. Return structs by value - In both LLVM and C backends Handle structs as return type in C and LLVM backends #1288 implements return type of structs by value. And then create a temporary variable to store that struct value into a struct pointer. See my comment Implement List return in LLVM #955 (comment) for more details. This approach will break with structs having array as members. See the example below,
@dataclass
class MatVec:
    mat: f64[2, 2]
    vec: f64[2]

def create_MatVec_obj() -> MatVec:
    mat: f64[2, 2] = empty((2, 2), dtype=float64)
    vec: f64[2] = empty(2, dtype=float64)
    mat[0, 0] = 0.0
    mat[0, 1] = -1.0
    mat[1, 0] = 1.0
    mat[1, 1] = 0.0
    vec[0] = 1.0
    vec[1] = 0.0
    mat_vec: MatVec = MatVec(mat, vec)
    return mat_vec

def test_rotate_by_90():
    mat_vec: MatVec = create_MatVec_obj()

Now in create_MatVec_obj, the backend allocates mat, vec on stack (as they are not allocatables). So once call to create_MatVec_obj completes, mat_vec.mat and mat_vec.vec will go out of scope and we will receive segmentation faults.

  1. Create an ASR to ASR pass - We can convert functions returning structs to subroutines with the struct being returned as intent(out) argument. This sounds good but again, it will face the same problem as the above approach when a struct will have arrays as members. In fact we may not have arrays as members directly but say a struct A has another struct B as member and B is having arrays as members. You can see the modified example from above approach to understand the problem,

Note that this is the code will look like when we apply ASR to ASR pass to convert functions returning structs into subroutines

@dataclass
class Mat:
    mat: f64[2, 2]

@dataclass
class Vec:
    vec: f64[2]

@dataclass
class MatVec:
    mat: Mat
    vec: Vec

def create_MatVec_obj(mat_vec: MatVec):
    mat: f64[2, 2] = empty((2, 2), dtype=float64)
    vec: f64[2] = empty(2, dtype=float64)
    mat[0, 0] = 0.0
    mat[0, 1] = -1.0
    mat[1, 0] = 1.0
    mat[1, 1] = 0.0
    vec[0] = 1.0
    vec[1] = 0.0
    mat_s: Mat = Mat(mat)
    vec_s: Vec = Vec(vec)
    mat_vec.mat = mat_s
    mat_vec.vec = vec_s
    return mat_vec

def test_rotate_by_90():
    mat_vec: MatVec
    create_MatVec_obj(mat_vec)

So the problem of array members going out of scope still exists even though we apply the ASR to ASR pass.

  1. Allocate every array, struct and data structures defined by structs on heap (only for LPython) - I think this is a simple and good approach and we will able to solve the problem of array members of structs going out of scope. NumPy and CPython anyways do this with every object but we will do this only for aggregate types like arrays, structs and data structures.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions