AnyDSL/impala

Revealing module design pattern implementation causes Segmentation Fault

PupoSDC opened this issue · 6 comments

I'm trying to implement something like the revealing module pattern in my anyDSL application. Unfortunately my implementation causes a segmentation fault

I have prepared a minimal example:

min.cpp:

#include <stdio.h>
#include "min.h" // generated by anyDSL

int main(int arc, char** argv) {
    run_adsl();
    return 0;
}

min.impala:

extern "C" {
    fn printf( &[u8], int ) -> ();
}

extern fn run_adsl() -> () {
    let module = myModule();
    printf("A) number = %i \n", module.number);
    module.increaseNumber(); // If this line is removed the code runs correctly
}

struct Module {
	number:         int,
	increaseNumber: fn() -> ()
}

fn myModule( ) -> ( Module ) {

	let mut module = Module {
		number:  0,
		increaseNumber: | | -> (){ }
	};

	module.increaseNumber = | | -> (){
		module.number++;
	};
}

Compilation commands:

impala min.impala -emit-llvm -emit-c-interface
llvm-as min.ll
clang min.cpp min.bc -o min.x

In the minimal example, I create a module structure which contains a pointer to a function. In the actual implementation of the code, the myModule function would return different versions of the module struct with pointers to different functions depending on the inputs passed.

The above code when ran produces the following output:

pupoSDC@xxxx:~/Desktop$ ./hello.x 
A) number = 0 
A) number = 0 
Segmentation fault (core dumped)

Any ideas if I'm doing something wrong, or something that is not supported by the language?

The problem is that in order to be able to generate code for this, the compiler needs to perform closure conversion, which is currently not implemented. Going back to your example, the problem is that increaseNumber is a function that captures values of its environment (the address of module, here). Because we cannot represent such a structure (containing a closure) in memory, we cannot generate proper code for this (we can only perform symbolic assignment of closures, so mutable function variables cannot exist). There is however a solution to your problem: Pull the number variable out of the structure, and use accessors to it.

extern "C" {
    fn printf( &[u8], int ) -> ();
}

extern fn run_adsl() -> () {
    let module = myModule();
    printf("A) number = %i \n", module.number()); // prints 0
    module.increaseNumber();
    printf("A) number = %i \n", module.number()); // prints 1
}

struct Module {
    number:         fn () -> int,
    increaseNumber: fn() -> ()
}

fn myModule() -> Module {
    let mut number = 0;
    let module = Module {
        number: || { number },
        increaseNumber: || -> () { number++; }
    };
    module
}

I actually think i like the formatting of the solution better than my original attempt. just to be sure i get it, this:

number: || { number }

is the same as

number: ||->(int) { number }

correct?

Thank you for your prompt help

(Type) is the same as Type, and if you omit the type, impala will infer it for you. The following expressions are equivalent:

|| -> int { 42 }
|| -> (int) { 42 }
|| 42
|| -> int 42

Please also note that in the solution I presented you, any call to myModule() will generate a different module. This is because the compiler inlines the call to myModule, which creates a different number on the stack for every call.

I'm aware that calls to myModule() will return different instances. I do not intend to implement a singleton pattern for this particular module so everything is ok.

Thanks for the explanation on the equivalent expressions.

Cheers

I'll close this issue then. Closure conversion is somewhere on our roadmap, but we are not going to implement it right now, since there are more pressing issues.