Skip to content

Commit 230c97f

Browse files
authored
Merge pull request #2133 from certik/bind_error
Add an error message for non-C-interoperable structs
2 parents 1ec6b21 + cc5ade4 commit 230c97f

File tree

13 files changed

+173
-32
lines changed

13 files changed

+173
-32
lines changed

integration_tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ RUN(NAME bindc_04 LABELS llvm c NOFAST)
374374
RUN(NAME bindc_07 LABELS cpython llvm c NOFAST)
375375
RUN(NAME bindc_08 LABELS cpython llvm c)
376376
RUN(NAME bindc_09 LABELS cpython llvm c)
377+
RUN(NAME bindc_09b LABELS cpython llvm c)
377378
RUN(NAME bindc_10 LABELS cpython llvm c NOFAST)
378379
RUN(NAME bindc_11 LABELS cpython) # This is CPython test only
379380
RUN(NAME exit_01 LABELS cpython llvm c NOFAST)
@@ -568,6 +569,7 @@ RUN(NAME test_bool_binop LABELS cpython llvm c)
568569
RUN(NAME test_issue_518 LABELS cpython llvm c NOFAST)
569570
RUN(NAME structs_01 LABELS cpython llvm c)
570571
RUN(NAME structs_02 LABELS cpython llvm c)
572+
RUN(NAME structs_02b LABELS cpython llvm c NOFAST)
571573
RUN(NAME structs_03 LABELS llvm c)
572574
RUN(NAME structs_04 LABELS cpython llvm c)
573575
RUN(NAME structs_05 LABELS cpython llvm c)

integration_tests/bindc_09.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from enum import Enum
22

3-
from lpython import CPtr, c_p_pointer, p_c_pointer, dataclass, empty_c_void_p, pointer, Pointer, i32, ccallable
3+
from lpython import (CPtr, c_p_pointer, p_c_pointer, dataclass, empty_c_void_p,
4+
pointer, Pointer, i32, ccallable, InOut)
45

56
class Value(Enum):
67
TEN: i32 = 10
@@ -17,8 +18,7 @@ class Foo:
1718
class FooC:
1819
value: Value
1920

20-
def bar(foo_ptr: CPtr) -> None:
21-
foo: Pointer[Foo] = c_p_pointer(foo_ptr, Foo)
21+
def bar(foo: InOut[Foo]) -> None:
2222
foo.value = Value.FIVE
2323

2424
def barc(foo_ptr: CPtr) -> None:
@@ -30,8 +30,7 @@ def main() -> None:
3030
fooc: FooC = FooC(Value.TWO)
3131
foo_ptr: CPtr = empty_c_void_p()
3232

33-
p_c_pointer(pointer(foo), foo_ptr)
34-
bar(foo_ptr)
33+
bar(foo)
3534
print(foo.value, foo.value.name)
3635
assert foo.value == Value.FIVE
3736

integration_tests/bindc_09b.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from enum import Enum
2+
3+
from lpython import CPtr, c_p_pointer, p_c_pointer, dataclass, empty_c_void_p, pointer, Pointer, i32, ccallable
4+
5+
class Value(Enum):
6+
TEN: i32 = 10
7+
TWO: i32 = 2
8+
ONE: i32 = 1
9+
FIVE: i32 = 5
10+
11+
@ccallable
12+
@dataclass
13+
class Foo:
14+
value: Value
15+
16+
@ccallable
17+
@dataclass
18+
class FooC:
19+
value: Value
20+
21+
def bar(foo_ptr: CPtr) -> None:
22+
foo: Pointer[Foo] = c_p_pointer(foo_ptr, Foo)
23+
foo.value = Value.FIVE
24+
25+
def barc(foo_ptr: CPtr) -> None:
26+
foo: Pointer[FooC] = c_p_pointer(foo_ptr, FooC)
27+
foo.value = Value.ONE
28+
29+
def main() -> None:
30+
foo: Foo = Foo(Value.TEN)
31+
fooc: FooC = FooC(Value.TWO)
32+
foo_ptr: CPtr = empty_c_void_p()
33+
34+
p_c_pointer(pointer(foo), foo_ptr)
35+
bar(foo_ptr)
36+
print(foo.value)
37+
assert foo.value == Value.FIVE.value
38+
39+
p_c_pointer(pointer(fooc), foo_ptr)
40+
barc(foo_ptr)
41+
print(fooc.value)
42+
assert fooc.value == Value.ONE.value
43+
44+
main()

integration_tests/structs_02.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ def f(a: CPtr) -> None:
1717
y = a2.y
1818
assert x == 3
1919
assert f64(y) == 3.25
20-
a2 = c_p_pointer(a, A)
21-
print(a, a2, pointer(a1))
2220

2321
def g():
2422
b: CPtr = empty_c_void_p()

integration_tests/structs_02b.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from lpython import (i32, f32, dataclass, CPtr, Pointer, c_p_pointer, pointer,
2+
ccallable, empty_c_void_p, f64, ccall, sizeof, i64)
3+
4+
@ccall
5+
def _lfortran_malloc(size: i32) -> CPtr:
6+
pass
7+
8+
def alloc(buf_size:i64) -> CPtr:
9+
return _lfortran_malloc(i32(buf_size))
10+
11+
@ccallable
12+
@dataclass
13+
class A:
14+
x: i32
15+
y: f32
16+
17+
@ccallable
18+
def f(a: CPtr) -> None:
19+
x: i32
20+
y: f32
21+
a1: A = A(3, f32(3.25))
22+
a2: Pointer[A]
23+
a2 = pointer(a1)
24+
print(a2, pointer(a1))
25+
# TODO: does not work:
26+
#x = a2.x
27+
#y = a2.y
28+
#assert x == 3
29+
#assert f64(y) == 3.25
30+
a2 = c_p_pointer(a, A)
31+
print(a, a2, pointer(a1))
32+
print(a2.x, a2.y)
33+
assert a2.x == 5
34+
assert a2.y == f32(6.0)
35+
36+
def g():
37+
b: CPtr = alloc(sizeof(A))
38+
b2: Pointer[A] = c_p_pointer(b, A)
39+
b2.x = 5
40+
b2.y = f32(6)
41+
f(b)
42+
43+
g()

integration_tests/structs_13.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
from lpython import i32, i16, i64, CPtr, dataclass, ccall, Pointer, c_p_pointer, sizeof
1+
from lpython import (i32, i16, i64, CPtr, dataclass, ccall, Pointer,
2+
c_p_pointer, sizeof, ccallable)
23
from numpy import array
34

5+
@ccallable
46
@dataclass
57
class A:
68
x: i32

src/lpython/semantics/python_ast_to_asr.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2734,6 +2734,21 @@ class CommonVisitor : public AST::BaseVisitor<Struct> {
27342734
);
27352735
throw SemanticAbort();
27362736
}
2737+
if (ASR::is_a<ASR::Struct_t>(*asr_alloc_type)) {
2738+
ASR::StructType_t *st = ASR::down_cast<ASR::StructType_t>(ASR::down_cast<ASR::Struct_t>(asr_alloc_type)->m_derived_type);
2739+
if (st->m_abi != ASR::abiType::BindC) {
2740+
diag.add(diag::Diagnostic(
2741+
"The struct in c_p_pointer must be C interoperable",
2742+
diag::Level::Error, diag::Stage::Semantic, {
2743+
diag::Label("not C interoperable",
2744+
{asr_alloc_type->base.loc}),
2745+
diag::Label("help: add the @ccallable decorator to this struct to make it C interoperable",
2746+
{st->base.base.loc}, false)
2747+
})
2748+
);
2749+
throw SemanticAbort();
2750+
}
2751+
}
27372752
fill_shape_and_lower_bound_for_CPtrToPointer();
27382753
return ASR::make_CPtrToPointer_t(al, loc, cptr, pptr, target_shape, lower_bounds);
27392754
}

tests/errors/bindc_10e.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from lpython import (i64, i16, CPtr, c_p_pointer, Pointer, sizeof, packed,
2+
dataclass, ccallable, ccall, i32)
3+
4+
@ccall
5+
def _lfortran_malloc(size: i32) -> CPtr:
6+
pass
7+
8+
9+
def alloc(buf_size:i64) -> CPtr:
10+
return _lfortran_malloc(i32(buf_size))
11+
12+
13+
@packed
14+
@dataclass
15+
class S:
16+
a: i16
17+
b: i64
18+
19+
20+
def main():
21+
p1: CPtr = alloc(sizeof(S))
22+
print(p1)
23+
p2: Pointer[S] = c_p_pointer(p1, S)
24+
p2.a = i16(5)
25+
p2.b = i64(4)
26+
print(p2.a, p2.b)
27+
assert p2.a == i16(5)
28+
assert p2.b == i64(4)
29+
30+
31+
main()
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"basename": "asr-bindc_10e-8b10394",
3+
"cmd": "lpython --show-asr --no-color {infile} -o {outfile}",
4+
"infile": "tests/errors/bindc_10e.py",
5+
"infile_hash": "36d1b5d366716d6a601db544fe8ff32aba76518181e71a98a4e6500c",
6+
"outfile": null,
7+
"outfile_hash": null,
8+
"stdout": null,
9+
"stdout_hash": null,
10+
"stderr": "asr-bindc_10e-8b10394.stderr",
11+
"stderr_hash": "7686dd8c9b718548d301e28d39cd15a85b00030172c5658b38a75af5",
12+
"returncode": 2
13+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
semantic error: The struct in c_p_pointer must be C interoperable
2+
--> tests/errors/bindc_10e.py:23:38
3+
|
4+
23 | p2: Pointer[S] = c_p_pointer(p1, S)
5+
| ^ not C interoperable
6+
|
7+
15 | class S:
8+
| ~~~~~~~~...
9+
...
10+
|
11+
17 | b: i64
12+
| ...~~~~~~~~~~ help: add the @ccallable decorator to this struct to make it C interoperable

0 commit comments

Comments
 (0)