carp-lang / Carp

A statically typed lisp, without a GC, for real-time applications.
Apache License 2.0
5.47k stars 173 forks source link

[Bug] Issue pattern matching structs #1443

Closed AZMCode closed 1 year ago

AZMCode commented 1 year ago

Pattern-matching a struct leads to a C compilation error

The following code, when built...

(deftype (Struct) [x Int])
(defn extract [s] (
    match s (Struct x) x
))
(sig main (Fn [] ()))
(defn main [] (
    IO.print &(str (extract (Struct 1)))
))

Leads to the following C compilation error:

out/main.c:11300:10: error: no member named '_tag' in 'Struct'
    if(s._tag == Struct_init_tag) {
       ~ ^
out/main.c:11300:18: error: use of undeclared identifier 'Struct_init_tag'; did you mean 'Struct_init'?
    if(s._tag == Struct_init_tag) {
                 ^~~~~~~~~~~~~~~
                 Struct_init
out/main.c:11019:8: note: 'Struct_init' declared here
Struct Struct_init(int x) {
       ^
out/main.c:11302:25: error: no member named 'u' in 'Struct'
        int x = _5_temp.u.init.member0;
                ~~~~~~~ ^
out/main.c:11305:37: error: passing 'Struct (int)' to parameter of incompatible type 'Lambda'
        Function_delete__int_Struct(Struct_init);
                                    ^~~~~~~~~~~
out/main.c:8035:42: note: passing argument to parameter 'f' here
void Function_delete__int_Struct (Lambda f) {
                                         ^
4 errors generated.
carp: callCommand: clang  -o out/Untitled -I <$HOME path>/.carp/core/  -fPIC -g -std=c99 -D_DEFAULT_SOURCE -Wall -Werror -Wno-unused-variable -Wno-self-assign -lm out/main.c (exit 1): failed
AZMCode commented 1 year ago

If necessary I can also include the output C files

scolsen commented 1 year ago

At the moment, pattern matching is only supported for sumtypes, which are defined as follows:

(deftype Foo (Bad [Int]) (Baz [Int]))

Then, you perform a match as follows:

(defn matcher [x]
  (match x)
    (Bar y) y
    (Baz y) y)

In your example, you've defined a product type (just a regular struct). Since product types only have one possible constructor, there's no way to match on them. Once match supports matching on arbitrary values, though, you'll be able to do this.

In the meantime, we should report the example you gave as an error.

AZMCode commented 1 year ago

I see. So there is no way to get a struct's value without it being behind a reference?

scolsen commented 1 year ago

Yes that's right. You can always pass an inline reference to the function if you have an actual value, e.g. in your example:

(defn extract [s]
  @(Struct.x &s))

Note that the field access also returns a reference. We have to do it this way because moving ownership of the field out of the struct could lead to memory problems. If we eventually give structs lifetime information we might be able to have variations that pass ownership. Note though, that the compiler is smart about these copies. For instance, even though you're "copying" the field in the example I gave above, in the compiled code it'll more than likely just be a pointer dereference and not an actual allocation.

AZMCode commented 1 year ago

But then, what about tuples? They are product types, which can be pattern-matched. I've noticed browsing the Core library that a private macro called deftuple or similar is available. Shouldn't pattern-matchable product types be possible to define by users?

scolsen commented 1 year ago

Shouldn't pattern-matchable product types be possible to define by users?

Yes, but it isn't implemented yet. Eventually, we should also support pattern matching directly against literal values like: (match x 1 ... 2 ....).

But then, what about tuples? They are product types, which can be pattern-matched.

Yep, this is true in most languages, but if you try this on the Carp tuple types like Pair it won't work for the same reason your initial code didn't work, we don't support pattern matching on product types yet.

AZMCode commented 1 year ago

What. I thought I tried it and it worked. Lemme check. Nope, i was wrong, you're right. Closing.