diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index 72ecb5d702..3d58ab8a1e 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -374,6 +374,7 @@ RUN(NAME bindc_04 LABELS llvm c NOFAST) RUN(NAME bindc_07 LABELS cpython llvm c NOFAST) RUN(NAME bindc_08 LABELS cpython llvm c) RUN(NAME bindc_09 LABELS cpython llvm c) +RUN(NAME bindc_09b LABELS cpython llvm c) RUN(NAME bindc_10 LABELS cpython llvm c NOFAST) RUN(NAME bindc_11 LABELS cpython) # This is CPython test only RUN(NAME exit_01 LABELS cpython llvm c NOFAST) @@ -568,6 +569,7 @@ RUN(NAME test_bool_binop LABELS cpython llvm c) RUN(NAME test_issue_518 LABELS cpython llvm c NOFAST) RUN(NAME structs_01 LABELS cpython llvm c) RUN(NAME structs_02 LABELS cpython llvm c) +RUN(NAME structs_02b LABELS cpython llvm c NOFAST) RUN(NAME structs_03 LABELS llvm c) RUN(NAME structs_04 LABELS cpython llvm c) RUN(NAME structs_05 LABELS cpython llvm c) diff --git a/integration_tests/bindc_09.py b/integration_tests/bindc_09.py index 18fdaad29e..956b56b658 100644 --- a/integration_tests/bindc_09.py +++ b/integration_tests/bindc_09.py @@ -1,6 +1,7 @@ from enum import Enum -from lpython import CPtr, c_p_pointer, p_c_pointer, dataclass, empty_c_void_p, pointer, Pointer, i32, ccallable +from lpython import (CPtr, c_p_pointer, p_c_pointer, dataclass, empty_c_void_p, + pointer, Pointer, i32, ccallable, InOut) class Value(Enum): TEN: i32 = 10 @@ -17,8 +18,7 @@ class Foo: class FooC: value: Value -def bar(foo_ptr: CPtr) -> None: - foo: Pointer[Foo] = c_p_pointer(foo_ptr, Foo) +def bar(foo: InOut[Foo]) -> None: foo.value = Value.FIVE def barc(foo_ptr: CPtr) -> None: @@ -30,8 +30,7 @@ def main() -> None: fooc: FooC = FooC(Value.TWO) foo_ptr: CPtr = empty_c_void_p() - p_c_pointer(pointer(foo), foo_ptr) - bar(foo_ptr) + bar(foo) print(foo.value, foo.value.name) assert foo.value == Value.FIVE diff --git a/integration_tests/bindc_09b.py b/integration_tests/bindc_09b.py new file mode 100644 index 0000000000..58be1e768b --- /dev/null +++ b/integration_tests/bindc_09b.py @@ -0,0 +1,44 @@ +from enum import Enum + +from lpython import CPtr, c_p_pointer, p_c_pointer, dataclass, empty_c_void_p, pointer, Pointer, i32, ccallable + +class Value(Enum): + TEN: i32 = 10 + TWO: i32 = 2 + ONE: i32 = 1 + FIVE: i32 = 5 + +@ccallable +@dataclass +class Foo: + value: Value + +@ccallable +@dataclass +class FooC: + value: Value + +def bar(foo_ptr: CPtr) -> None: + foo: Pointer[Foo] = c_p_pointer(foo_ptr, Foo) + foo.value = Value.FIVE + +def barc(foo_ptr: CPtr) -> None: + foo: Pointer[FooC] = c_p_pointer(foo_ptr, FooC) + foo.value = Value.ONE + +def main() -> None: + foo: Foo = Foo(Value.TEN) + fooc: FooC = FooC(Value.TWO) + foo_ptr: CPtr = empty_c_void_p() + + p_c_pointer(pointer(foo), foo_ptr) + bar(foo_ptr) + print(foo.value) + assert foo.value == Value.FIVE.value + + p_c_pointer(pointer(fooc), foo_ptr) + barc(foo_ptr) + print(fooc.value) + assert fooc.value == Value.ONE.value + +main() diff --git a/integration_tests/structs_02.py b/integration_tests/structs_02.py index f3325d6a9a..0e99458c34 100644 --- a/integration_tests/structs_02.py +++ b/integration_tests/structs_02.py @@ -17,8 +17,6 @@ def f(a: CPtr) -> None: y = a2.y assert x == 3 assert f64(y) == 3.25 - a2 = c_p_pointer(a, A) - print(a, a2, pointer(a1)) def g(): b: CPtr = empty_c_void_p() diff --git a/integration_tests/structs_02b.py b/integration_tests/structs_02b.py new file mode 100644 index 0000000000..ed061ff6ce --- /dev/null +++ b/integration_tests/structs_02b.py @@ -0,0 +1,43 @@ +from lpython import (i32, f32, dataclass, CPtr, Pointer, c_p_pointer, pointer, + ccallable, empty_c_void_p, f64, ccall, sizeof, i64) + +@ccall +def _lfortran_malloc(size: i32) -> CPtr: + pass + +def alloc(buf_size:i64) -> CPtr: + return _lfortran_malloc(i32(buf_size)) + +@ccallable +@dataclass +class A: + x: i32 + y: f32 + +@ccallable +def f(a: CPtr) -> None: + x: i32 + y: f32 + a1: A = A(3, f32(3.25)) + a2: Pointer[A] + a2 = pointer(a1) + print(a2, pointer(a1)) + # TODO: does not work: + #x = a2.x + #y = a2.y + #assert x == 3 + #assert f64(y) == 3.25 + a2 = c_p_pointer(a, A) + print(a, a2, pointer(a1)) + print(a2.x, a2.y) + assert a2.x == 5 + assert a2.y == f32(6.0) + +def g(): + b: CPtr = alloc(sizeof(A)) + b2: Pointer[A] = c_p_pointer(b, A) + b2.x = 5 + b2.y = f32(6) + f(b) + +g() diff --git a/integration_tests/structs_13.py b/integration_tests/structs_13.py index 651518fc05..9806fa1ac9 100644 --- a/integration_tests/structs_13.py +++ b/integration_tests/structs_13.py @@ -1,6 +1,8 @@ -from lpython import i32, i16, i64, CPtr, dataclass, ccall, Pointer, c_p_pointer, sizeof +from lpython import (i32, i16, i64, CPtr, dataclass, ccall, Pointer, + c_p_pointer, sizeof, ccallable) from numpy import array +@ccallable @dataclass class A: x: i32 diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index 1611d23d04..ade3a2f39d 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -2734,6 +2734,21 @@ class CommonVisitor : public AST::BaseVisitor { ); throw SemanticAbort(); } + if (ASR::is_a(*asr_alloc_type)) { + ASR::StructType_t *st = ASR::down_cast(ASR::down_cast(asr_alloc_type)->m_derived_type); + if (st->m_abi != ASR::abiType::BindC) { + diag.add(diag::Diagnostic( + "The struct in c_p_pointer must be C interoperable", + diag::Level::Error, diag::Stage::Semantic, { + diag::Label("not C interoperable", + {asr_alloc_type->base.loc}), + diag::Label("help: add the @ccallable decorator to this struct to make it C interoperable", + {st->base.base.loc}, false) + }) + ); + throw SemanticAbort(); + } + } fill_shape_and_lower_bound_for_CPtrToPointer(); return ASR::make_CPtrToPointer_t(al, loc, cptr, pptr, target_shape, lower_bounds); } diff --git a/tests/errors/bindc_10e.py b/tests/errors/bindc_10e.py new file mode 100644 index 0000000000..bdbff7df6c --- /dev/null +++ b/tests/errors/bindc_10e.py @@ -0,0 +1,31 @@ +from lpython import (i64, i16, CPtr, c_p_pointer, Pointer, sizeof, packed, + dataclass, ccallable, ccall, i32) + +@ccall +def _lfortran_malloc(size: i32) -> CPtr: + pass + + +def alloc(buf_size:i64) -> CPtr: + return _lfortran_malloc(i32(buf_size)) + + +@packed +@dataclass +class S: + a: i16 + b: i64 + + +def main(): + p1: CPtr = alloc(sizeof(S)) + print(p1) + p2: Pointer[S] = c_p_pointer(p1, S) + p2.a = i16(5) + p2.b = i64(4) + print(p2.a, p2.b) + assert p2.a == i16(5) + assert p2.b == i64(4) + + +main() diff --git a/tests/reference/asr-bindc_10e-8b10394.json b/tests/reference/asr-bindc_10e-8b10394.json new file mode 100644 index 0000000000..9fdf2f9acd --- /dev/null +++ b/tests/reference/asr-bindc_10e-8b10394.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-bindc_10e-8b10394", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/bindc_10e.py", + "infile_hash": "36d1b5d366716d6a601db544fe8ff32aba76518181e71a98a4e6500c", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-bindc_10e-8b10394.stderr", + "stderr_hash": "7686dd8c9b718548d301e28d39cd15a85b00030172c5658b38a75af5", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-bindc_10e-8b10394.stderr b/tests/reference/asr-bindc_10e-8b10394.stderr new file mode 100644 index 0000000000..3639b97f68 --- /dev/null +++ b/tests/reference/asr-bindc_10e-8b10394.stderr @@ -0,0 +1,12 @@ +semantic error: The struct in c_p_pointer must be C interoperable + --> tests/errors/bindc_10e.py:23:38 + | +23 | p2: Pointer[S] = c_p_pointer(p1, S) + | ^ not C interoperable + | +15 | class S: + | ~~~~~~~~... +... + | +17 | b: i64 + | ...~~~~~~~~~~ help: add the @ccallable decorator to this struct to make it C interoperable diff --git a/tests/reference/asr-structs_02-2ab459a.json b/tests/reference/asr-structs_02-2ab459a.json index 2056a9f832..4d7980c0f1 100644 --- a/tests/reference/asr-structs_02-2ab459a.json +++ b/tests/reference/asr-structs_02-2ab459a.json @@ -2,11 +2,11 @@ "basename": "asr-structs_02-2ab459a", "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", "infile": "tests/../integration_tests/structs_02.py", - "infile_hash": "6d54aa7c2bb850cbce2c0add7b77f9f72c9323162ae080c7eef4867a", + "infile_hash": "281bc75fb743f18fb6f011b278d7ab8103cc688f5856a59cb1f54895", "outfile": null, "outfile_hash": null, "stdout": "asr-structs_02-2ab459a.stdout", - "stdout_hash": "e5df0fb84bfc694b884736c1c4a5ddaec25933d8dba54e894de3e961", + "stdout_hash": "3a3e1108225886c52d459a08a26d5b35d4aea338982034d43079769d", "stderr": null, "stderr_hash": null, "returncode": 0 diff --git a/tests/reference/asr-structs_02-2ab459a.stdout b/tests/reference/asr-structs_02-2ab459a.stdout index feaf95ffa6..83bfd63a07 100644 --- a/tests/reference/asr-structs_02-2ab459a.stdout +++ b/tests/reference/asr-structs_02-2ab459a.stdout @@ -304,28 +304,6 @@ () ) () - ) - (CPtrToPointer - (Var 3 a) - (Var 3 a2) - () - () - ) - (Print - () - [(Var 3 a) - (Var 3 a2) - (GetPointer - (Var 3 a1) - (Pointer - (Struct - 8 A - ) - ) - () - )] - () - () )] () Public diff --git a/tests/tests.toml b/tests/tests.toml index 057269e597..05734f688c 100644 --- a/tests/tests.toml +++ b/tests/tests.toml @@ -1151,6 +1151,10 @@ asr = true filename = "errors/bindc_04.py" asr = true +[[test]] +filename = "errors/bindc_10e.py" +asr = true + [[test]] filename = "errors/cptr_01.py" asr = true