Repository: mun-lang/mun (language: rust)

Issue:
Calling an extern function without a return type exits the function it is called in
Calling an `extern fn` without a return type seems to exit the function it is called in.

For example, the following code

```rust
// resources/script.mun
extern fn thing(n: int);
extern fn print(n: int) -> int;

pub fn main() {
    // 1st
    print(1);
    thing(5);

    // 2nd
    print(2);
    thing(78);
}

// src/main.rs
use mun_runtime::{invoke_fn, RuntimeBuilder};
use std::{cell::RefCell, rc::Rc};

extern "C" fn thing(n: i64) {
    // do some cool stuff
}

extern "C" fn print(n: i64) -> i64 {
    println!("{}", n);
    // return an int so that it doesn't fail
    0
}

fn main() {
    let runtime = Rc::new(RefCell::new({
        let mut runtime = RuntimeBuilder::new("resources/script.munlib");
        runtime.insert_fn("thing", thing as extern "C" fn(i64));
        runtime.insert_fn("print", print as extern "C" fn(i64) -> i64);
        runtime.spawn().unwrap()
    }));

    let _: Result<_, mun_runtime::InvokeErr0<'_, ()>> = invoke_fn!(runtime, "main");
}
```

will output only

```
1
```

and not 2.


Discussion / hints:
Thanks for the report! I've found the issue, will submit a PR soon. :)

Fix patch:
diff --git a/crates/mun_codegen/src/ir/body.rs b/crates/mun_codegen/src/ir/body.rs
index 0c90bfaa7..ce81138e8 100644
--- a/crates/mun_codegen/src/ir/body.rs
+++ b/crates/mun_codegen/src/ir/body.rs
@@ -218,7 +218,20 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
                             .map(|expr| self.gen_expr(*expr).expect("expected a value"))
                             .collect();
 
-                        self.gen_call(def, &args).try_as_basic_value().left()
+                        self.gen_call(def, &args)
+                            .try_as_basic_value()
+                            .left()
+                            // If the called function is a void function it doesn't return anything.
+                            // If this method (`gen_expr`) returns None we assume the return value
+                            // is `never`. We return a const unit struct here to ensure that at
+                            // least something is returned. This matches with the hir where a
+                            // `nothing` is returned instead of a `never`.
+                            //
+                            // This unit value will also be optimized out.
+                            .or_else(|| match self.infer[expr] {
+                                hir::ty_app!(hir::TypeCtor::Never) => None,
+                                _ => Some(self.db.context().const_struct(&[], false).into()),
+                            })
                     }
                     Some(hir::CallableDef::Struct(_)) => Some(self.gen_named_tuple_lit(expr, args)),
                     None => panic!("expected a callable expression"),


Test patch:
diff --git a/crates/mun_codegen/src/snapshots/test__issue_128_file_ir.snap b/crates/mun_codegen/src/snapshots/test__issue_128_file_ir.snap
new file mode 100644
index 000000000..136b147f4
--- /dev/null
+++ b/crates/mun_codegen/src/snapshots/test__issue_128_file_ir.snap
@@ -0,0 +1,26 @@
+---
+source: crates/mun_codegen/src/test.rs
+expression: "// resources/script.mun\nextern fn thing(n: int);\nextern fn print(n: int) -> int;\n\npub fn main() {\n    // 1st\n    print(1);\n    thing(5);\n\n    // 2nd\n    print(2);\n    thing(78);\n}"
+---
+; ModuleID = 'main.mun'
+source_filename = "main.mun"
+
+%DispatchTable = type { i64 (i64)*, void (i64)* }
+%struct.MunTypeInfo = type { [16 x i8], i8 addrspace(4)*, i32, i8, i8 }
+
+@dispatchTable = external global %DispatchTable
+@global_type_table = external global [1 x %struct.MunTypeInfo addrspace(4)*]
+
+define void @main() {
+body:
+  %print_ptr = load i64 (i64)*, i64 (i64)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0)
+  %print = call i64 %print_ptr(i64 1)
+  %thing_ptr = load void (i64)*, void (i64)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 1)
+  call void %thing_ptr(i64 5)
+  %print_ptr1 = load i64 (i64)*, i64 (i64)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0)
+  %print2 = call i64 %print_ptr1(i64 2)
+  %thing_ptr3 = load void (i64)*, void (i64)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 1)
+  call void %thing_ptr3(i64 78)
+  ret void
+}
+
diff --git a/crates/mun_codegen/src/snapshots/test__issue_128_group_ir.snap b/crates/mun_codegen/src/snapshots/test__issue_128_group_ir.snap
new file mode 100644
index 000000000..6831c521f
--- /dev/null
+++ b/crates/mun_codegen/src/snapshots/test__issue_128_group_ir.snap
@@ -0,0 +1,15 @@
+---
+source: crates/mun_codegen/src/test.rs
+expression: "// resources/script.mun\nextern fn thing(n: int);\nextern fn print(n: int) -> int;\n\npub fn main() {\n    // 1st\n    print(1);\n    thing(5);\n\n    // 2nd\n    print(2);\n    thing(78);\n}"
+---
+; ModuleID = 'group_name'
+source_filename = "group_name"
+
+%struct.MunTypeInfo = type { [16 x i8], i8 addrspace(4)*, i32, i8, i8 }
+%DispatchTable = type { i64 (i64)*, void (i64)* }
+
+@"type_info::<core::i64>::name" = private unnamed_addr constant [10 x i8] c"core::i64\00"
+@"type_info::<core::i64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", [10 x i8]* @"type_info::<core::i64>::name", i32 64, i8 8, i8 0 }
+@global_type_table = global [1 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::<core::i64>"]
+@dispatchTable = global %DispatchTable zeroinitializer
+
diff --git a/crates/mun_codegen/src/test.rs b/crates/mun_codegen/src/test.rs
index 9abca80c2..e3f35fa7e 100644
--- a/crates/mun_codegen/src/test.rs
+++ b/crates/mun_codegen/src/test.rs
@@ -5,6 +5,27 @@ use mun_target::spec::Target;
 use std::cell::RefCell;
 use std::sync::Arc;
 
+#[test]
+fn issue_128() {
+    test_snapshot(
+        r#"
+    // resources/script.mun
+    extern fn thing(n: int);
+    extern fn print(n: int) -> int;
+
+    pub fn main() {
+        // 1st
+        print(1);
+        thing(5);
+
+        // 2nd
+        print(2);
+        thing(78);
+    }
+    "#,
+    )
+}
+
 #[test]
 fn function() {
     test_snapshot(