diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index b5e74aef91..3379424b5b 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -328,6 +328,7 @@ RUN(NAME structs_11 LABELS cpython llvm c) RUN(NAME structs_12 LABELS cpython llvm c) RUN(NAME structs_13 LABELS llvm c EXTRAFILES structs_13b.c) +RUN(NAME structs_15 LABELS cpython llvm c) RUN(NAME sizeof_01 LABELS llvm c EXTRAFILES sizeof_01b.c) RUN(NAME enum_01 LABELS cpython llvm c) diff --git a/integration_tests/bindc_01.py b/integration_tests/bindc_01.py index 252e3cb0cf..9954eddd85 100644 --- a/integration_tests/bindc_01.py +++ b/integration_tests/bindc_01.py @@ -1,6 +1,5 @@ -from ltypes import c_p_pointer, CPtr, i16 +from ltypes import c_p_pointer, CPtr, i16, Pointer queries: CPtr -x: Pointer[i16] -c_p_pointer(queries, x) +x: Pointer[i16] = c_p_pointer(queries, i16) print(queries, x) diff --git a/integration_tests/bindc_02.py b/integration_tests/bindc_02.py index fb844aa6fc..524bf58b7e 100644 --- a/integration_tests/bindc_02.py +++ b/integration_tests/bindc_02.py @@ -1,8 +1,7 @@ from ltypes import c_p_pointer, CPtr, pointer, i16, Pointer queries: CPtr -x: Pointer[i16[:]] -c_p_pointer(queries, x) +x: Pointer[i16[:]] = c_p_pointer(queries, i16[:]) print(queries, x) def f(): @@ -17,7 +16,7 @@ def f(): assert yptr1[0] == i16(1) assert yptr1[1] == i16(2) - c_p_pointer(yq, yptr1) + yptr1 = c_p_pointer(yq, i16[:]) print(yq, yptr1) diff --git a/integration_tests/bindc_03.py b/integration_tests/bindc_03.py index e8a09d599f..1138522b09 100644 --- a/integration_tests/bindc_03.py +++ b/integration_tests/bindc_03.py @@ -19,8 +19,7 @@ def get_array(size: i32) -> CPtr: def f(q_void: CPtr) -> None: i: i32 el: i32 - q: Pointer[i32[:]] - c_p_pointer(q_void, q) + q: Pointer[i32[:]] = c_p_pointer(q_void, i32[:]) for i in range(10): q2: CPtr p_c_pointer(pointer(q[i]), q2) @@ -33,8 +32,7 @@ def f(q_void: CPtr) -> None: def h(q_void: CPtr) -> None: i: i32 el: i32 - q: Pointer[i32[:]] - c_p_pointer(q_void, q) + q: Pointer[i32[:]] = c_p_pointer(q_void, i32[:]) for i in range(10): # TODO: Use q[i] directly in the assert. el = q[i] diff --git a/integration_tests/structs_15.py b/integration_tests/structs_15.py new file mode 100644 index 0000000000..a7e9fc9c88 --- /dev/null +++ b/integration_tests/structs_15.py @@ -0,0 +1,41 @@ +from ltypes import i32, i16, i8, i64, CPtr, dataclass, ccall, Pointer, c_p_pointer, sizeof + +@dataclass +class A: + x: i16 + y: i8 + +@ccall +def _lfortran_malloc(size: i32) -> CPtr: + pass + +@ccall +def _lfortran_memset(cptr: CPtr, value: i32, size: i32): + pass + +def add_A_members(Ax: i16, Ay: i8) -> i16: + return Ax + i16(Ay) + +def test_Aptr_member_passing(): + print(sizeof(A)) + + a_cptr: CPtr = _lfortran_malloc(i32(sizeof(A))) + _lfortran_memset(a_cptr, 2, i32(sizeof(A))) + b_cptr: CPtr = _lfortran_malloc(i32(sizeof(A))) + _lfortran_memset(b_cptr, 6, i32(sizeof(A))) + + a_ptr: Pointer[A] = c_p_pointer(a_cptr, A) + b_ptr: Pointer[A] = c_p_pointer(b_cptr, A) + print(a_ptr.x, a_ptr.y) + print(b_ptr.x, b_ptr.y) + assert a_ptr.x * i16(3) == b_ptr.x + assert a_ptr.y * i8(3) == b_ptr.y + + a_ptr.y = i8(-18) + assert a_ptr.x * i16(3) == b_ptr.x + a_ptr.x = i16(20) + print(a_ptr.x, a_ptr.y) + print(add_A_members(a_ptr.x, a_ptr.y)) + assert add_A_members(a_ptr.x, a_ptr.y) == i16(2) + +test_Aptr_member_passing() diff --git a/src/libasr/runtime/lfortran_intrinsics.c b/src/libasr/runtime/lfortran_intrinsics.c index 490a356af3..d6495fd488 100644 --- a/src/libasr/runtime/lfortran_intrinsics.c +++ b/src/libasr/runtime/lfortran_intrinsics.c @@ -886,8 +886,12 @@ LFORTRAN_API char* _lfortran_str_chr(int val) return dest_char; } -LFORTRAN_API char* _lfortran_malloc(int size) { - return (char*)malloc(size); +LFORTRAN_API void _lfortran_memset(void* s, int32_t c, int32_t size) { + memset(s, c, size); +} + +LFORTRAN_API void* _lfortran_malloc(int32_t size) { + return malloc(size); } LFORTRAN_API int8_t* _lfortran_realloc(int8_t* ptr, int32_t size) { diff --git a/src/libasr/runtime/lfortran_intrinsics.h b/src/libasr/runtime/lfortran_intrinsics.h index 05e4c1549f..53e1689bdf 100644 --- a/src/libasr/runtime/lfortran_intrinsics.h +++ b/src/libasr/runtime/lfortran_intrinsics.h @@ -161,7 +161,8 @@ LFORTRAN_API int _lfortran_str_ord(char** s); LFORTRAN_API int _lfortran_str_ord_c(char* s); LFORTRAN_API char* _lfortran_str_chr(int c); LFORTRAN_API int _lfortran_str_to_int(char** s); -LFORTRAN_API char* _lfortran_malloc(int size); +LFORTRAN_API void* _lfortran_malloc(int32_t size); +LFORTRAN_API void _lfortran_memset(void* s, int32_t c, int32_t size); LFORTRAN_API int8_t* _lfortran_realloc(int8_t* ptr, int32_t size); LFORTRAN_API int8_t* _lfortran_calloc(int32_t count, int32_t size); LFORTRAN_API void _lfortran_free(char* ptr); diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index e7c2aeb1b1..ece311f7f8 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -538,6 +538,8 @@ class CommonVisitor : public AST::BaseVisitor { std::string import_path; Vec *current_body; ASR::ttype_t* ann_assign_target_type; + AST::expr_t* assign_ast_target; + bool is_c_p_pointer_call; std::map generic_func_nums; std::map> generic_func_subs; @@ -551,8 +553,8 @@ class CommonVisitor : public AST::BaseVisitor { std::string import_path, bool allow_implicit_casting_) : diag{diagnostics}, al{al}, lm{lm}, current_scope{symbol_table}, main_module{main_module}, ast_overload{ast_overload}, parent_dir{parent_dir}, import_path{import_path}, - current_body{nullptr}, ann_assign_target_type{nullptr}, - allow_implicit_casting{allow_implicit_casting_} { + current_body{nullptr}, ann_assign_target_type{nullptr}, assign_ast_target{nullptr}, + is_c_p_pointer_call{false}, allow_implicit_casting{allow_implicit_casting_} { current_module_dependencies.reserve(al, 4); } @@ -2211,6 +2213,16 @@ class CommonVisitor : public AST::BaseVisitor { current_scope->add_symbol(var_name, v_sym); } + ASR::asr_t* create_CPtrToPointerFromArgs(AST::expr_t* ast_cptr, AST::expr_t* ast_pptr, + const Location& loc) { + this->visit_expr(*ast_cptr); + ASR::expr_t* cptr = ASRUtils::EXPR(tmp); + this->visit_expr(*ast_pptr); + ASR::expr_t* pptr = ASRUtils::EXPR(tmp); + return ASR::make_CPtrToPointer_t(al, loc, cptr, + pptr, nullptr); + } + void visit_AnnAssignUtil(const AST::AnnAssign_t& x, std::string& var_name, bool wrap_derived_type_in_pointer=false, ASR::expr_t* init_expr=nullptr, ASR::abiType abi=ASR::abiType::Source) { @@ -2222,13 +2234,25 @@ class CommonVisitor : public AST::BaseVisitor { type = ASRUtils::TYPE(ASR::make_Pointer_t(al, type->base.loc, type)); } + bool is_c_p_pointer_call_copy = is_c_p_pointer_call; ASR::expr_t *value = nullptr; if( !init_expr ) { tmp = nullptr; + is_c_p_pointer_call = false; if (x.m_value) { this->visit_expr(*x.m_value); } - if (tmp) { + if( is_c_p_pointer_call ) { + create_add_variable_to_scope(var_name, nullptr, nullptr, type, + x.base.base.loc, abi); + AST::Call_t* c_p_pointer_call = AST::down_cast(x.m_value); + AST::expr_t* cptr = c_p_pointer_call->m_args[0]; + AST::expr_t* pptr = assign_ast_target; + tmp = create_CPtrToPointerFromArgs(cptr, pptr, x.base.base.loc); + // if( current_body ) { + // current_body->push_back(al, ASRUtils::STMT(tmp)); + // } + } else if (tmp) { value = ASRUtils::EXPR(tmp); ASR::ttype_t* underlying_type = type; if( ASR::is_a(*type) ) { @@ -2256,10 +2280,16 @@ class CommonVisitor : public AST::BaseVisitor { } else { cast_helper(type, init_expr, init_expr->base.loc); } - create_add_variable_to_scope(var_name, init_expr, value, type, - x.base.base.loc, abi); - tmp = nullptr; + if( !is_c_p_pointer_call ) { + create_add_variable_to_scope(var_name, init_expr, value, type, + x.base.base.loc, abi); + } + + if( !is_c_p_pointer_call ) { + tmp = nullptr; + } + is_c_p_pointer_call = is_c_p_pointer_call_copy; ann_assign_target_type = ann_assign_target_type_copy; } @@ -3869,6 +3899,8 @@ class BodyVisitor : public CommonVisitor { // We treat this as a declaration std::string var_name; std::string var_annotation; + AST::expr_t* assign_ast_target_copy = assign_ast_target; + assign_ast_target = x.m_target; if (AST::is_a(*x.m_target)) { AST::Name_t *n = AST::down_cast(x.m_target); var_name = n->m_id; @@ -3893,6 +3925,7 @@ class BodyVisitor : public CommonVisitor { } visit_AnnAssignUtil(x, var_name); + assign_ast_target = assign_ast_target_copy; } void visit_Delete(const AST::Delete_t &x) { @@ -3924,7 +3957,19 @@ class BodyVisitor : public CommonVisitor { void visit_Assign(const AST::Assign_t &x) { ASR::expr_t *target, *assign_value = nullptr, *tmp_value; + bool is_c_p_pointer_call_copy = is_c_p_pointer_call; + is_c_p_pointer_call = false; this->visit_expr(*x.m_value); + if( is_c_p_pointer_call ) { + LFORTRAN_ASSERT(x.n_targets == 1); + AST::Call_t* c_p_pointer_call = AST::down_cast(x.m_value); + AST::expr_t* cptr = c_p_pointer_call->m_args[0]; + AST::expr_t* pptr = x.m_targets[0]; + tmp = create_CPtrToPointerFromArgs(cptr, pptr, x.base.base.loc); + is_c_p_pointer_call = is_c_p_pointer_call; + return ; + } + is_c_p_pointer_call = is_c_p_pointer_call_copy; if (tmp) { // This happens if `m.m_value` is `empty`, such as in: // a = empty(16) @@ -5813,6 +5858,10 @@ class BodyVisitor : public CommonVisitor { tmp = intrinsic_node_handler.get_intrinsic_node(call_name, al, x.base.base.loc, args); return; + } else if (call_name == "c_p_pointer") { + is_c_p_pointer_call = true; + tmp = nullptr; + return ; } else { // The function was not found and it is not intrinsic throw SemanticError("Function '" + call_name + "' is not declared and not intrinsic", diff --git a/src/runtime/ltypes/ltypes.py b/src/runtime/ltypes/ltypes.py index 7eb4082ac2..ad8bccfd27 100644 --- a/src/runtime/ltypes/ltypes.py +++ b/src/runtime/ltypes/ltypes.py @@ -9,7 +9,7 @@ __slots__ = ["i8", "i16", "i32", "i64", "f32", "f64", "c32", "c64", "CPtr", "overload", "ccall", "TypeVar", "pointer", "c_p_pointer", "Pointer", "p_c_pointer", "vectorize", "inline", "Union", "static", "with_goto", - "packed", "Const"] + "packed", "Const", "sizeof"] # data-types @@ -25,6 +25,8 @@ def __call__(self, arg): class Pointer: def __getitem__(self, type): + if is_dataclass(type): + return convert_to_ctypes_Structure(type) return type class ConstType(Type): @@ -174,6 +176,8 @@ def convert_type_to_ctype(arg): elif isinstance(arg, Array): type = convert_type_to_ctype(arg._type) return ctypes.POINTER(type) + elif is_dataclass(arg): + return convert_to_ctypes_Structure(arg) else: raise NotImplementedError("Type %r not implemented" % arg) @@ -196,7 +200,7 @@ def get_lib_name(name): else: raise NotImplementedError("Platform not implemented") def get_crtlib_path(): - py_mod = os.environ["LPYTHON_PY_MOD_NAME"] + py_mod = os.environ.get("LPYTHON_PY_MOD_NAME", "") if py_mod == "": return os.path.join(get_rtlib_dir(), get_lib_name("lpython_runtime")) @@ -242,6 +246,19 @@ def convert_to_ctypes_Union(f): return f +def convert_to_ctypes_Structure(f): + fields = [] + for name in f.__annotations__: + ltype_ = f.__annotations__[name] + fields.append((name, convert_type_to_ctype(ltype_))) + + class ctypes_Structure(ctypes.Structure): + _fields_ = fields + + ctypes_Structure.__name__ = f.__name__ + + return ctypes_Structure + def ccall(f): if isclass(f) and issubclass(f, Union): return f @@ -280,11 +297,32 @@ def pointer(x, type=None): else: raise Exception("Type not supported in pointer()") +class PointerToStruct: + + def __init__(self, ctypes_ptr_): + self.__dict__["ctypes_ptr"] = ctypes_ptr_ + + def __getattr__(self, name: str): + if name == "ctypes_ptr": + return self.__dict__[name] + return self.ctypes_ptr.contents.__getattribute__(name) + + def __setattr__(self, name: str, value): + self.ctypes_ptr.contents.__setattr__(name, value) + def c_p_pointer(cptr, targettype): - return pointer(targettype) + targettype_ptr = ctypes.POINTER(convert_type_to_ctype(targettype)) + newa = ctypes.cast(cptr, targettype_ptr) + if is_dataclass(targettype): + # return after wrapping newa inside PointerToStruct + return PointerToStruct(newa) + return newa def p_c_pointer(ptr, cptr): cptr.value = ptr.value def empty_c_void_p(): return ctypes.c_void_p() + +def sizeof(arg): + return ctypes.sizeof(convert_type_to_ctype(arg)) diff --git a/tests/reference/asr-bindc_01-6d521a9.json b/tests/reference/asr-bindc_01-6d521a9.json index e95c1cdf72..07714cd9f8 100644 --- a/tests/reference/asr-bindc_01-6d521a9.json +++ b/tests/reference/asr-bindc_01-6d521a9.json @@ -2,7 +2,7 @@ "basename": "asr-bindc_01-6d521a9", "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", "infile": "tests/../integration_tests/bindc_01.py", - "infile_hash": "d4690d665de9b941c2581ad02041822e1e322dba55485cf8534dfe0a", + "infile_hash": "ce44dc48f31bcf876253727ca139210d99193565be1cf9b5fd3dea40", "outfile": null, "outfile_hash": null, "stdout": "asr-bindc_01-6d521a9.stdout", diff --git a/tests/reference/asr-bindc_02-bc1a7ea.json b/tests/reference/asr-bindc_02-bc1a7ea.json index dbdd9eab72..15c204d201 100644 --- a/tests/reference/asr-bindc_02-bc1a7ea.json +++ b/tests/reference/asr-bindc_02-bc1a7ea.json @@ -2,7 +2,7 @@ "basename": "asr-bindc_02-bc1a7ea", "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", "infile": "tests/../integration_tests/bindc_02.py", - "infile_hash": "26491ee7e9450cc430e5e6b443510ea0cc7507975baa8d8768e6f287", + "infile_hash": "f7a7faa22c2440be545994170ef21f451ce95960c7c7c217db5221fb", "outfile": null, "outfile_hash": null, "stdout": "asr-bindc_02-bc1a7ea.stdout", diff --git a/tests/reference/llvm-bindc_01-c984f09.json b/tests/reference/llvm-bindc_01-c984f09.json index af08f8dfc3..350419c698 100644 --- a/tests/reference/llvm-bindc_01-c984f09.json +++ b/tests/reference/llvm-bindc_01-c984f09.json @@ -2,7 +2,7 @@ "basename": "llvm-bindc_01-c984f09", "cmd": "lpython --no-color --show-llvm {infile} -o {outfile}", "infile": "tests/../integration_tests/bindc_01.py", - "infile_hash": "d4690d665de9b941c2581ad02041822e1e322dba55485cf8534dfe0a", + "infile_hash": "ce44dc48f31bcf876253727ca139210d99193565be1cf9b5fd3dea40", "outfile": null, "outfile_hash": null, "stdout": "llvm-bindc_01-c984f09.stdout", diff --git a/tests/reference/llvm-bindc_02-3cf74e9.json b/tests/reference/llvm-bindc_02-3cf74e9.json index ec55538bc2..2fb5ff6a8d 100644 --- a/tests/reference/llvm-bindc_02-3cf74e9.json +++ b/tests/reference/llvm-bindc_02-3cf74e9.json @@ -2,7 +2,7 @@ "basename": "llvm-bindc_02-3cf74e9", "cmd": "lpython --no-color --show-llvm {infile} -o {outfile}", "infile": "tests/../integration_tests/bindc_02.py", - "infile_hash": "26491ee7e9450cc430e5e6b443510ea0cc7507975baa8d8768e6f287", + "infile_hash": "f7a7faa22c2440be545994170ef21f451ce95960c7c7c217db5221fb", "outfile": null, "outfile_hash": null, "stdout": "llvm-bindc_02-3cf74e9.stdout",