openlang

Can main() supply an exit status without disrupting control flow?

Must an exit status always be supplied through os.exit()? Calling os.exit() inside main() prevents me from using defer, producing this diagnostic:

Error: Unreachable defer statement due to diverging procedure call at the end of the current scope

This GitHub Discussion suggests the approach below:

Can Odin main procedures provide a return value? - GitHub Discussion (https://github.com/odin-lang/Odin/discussions/3097) To imitate C's int main(), say, introduce a separate proc such as run that returns a value. Your main can simply invoke it and respond to its returned error as appropriate - perhaps calling os.exit() with a failure status, or taking whichever action your application needs.

Can main() supply an exit status another way while allowing return, defer, and the remaining control-flow constructs to behave as usual.

The conversation continues 3

Follow the reasoning
  1. Permalink

    Calling os.exit() also changes whether the @(fini) blocks will actually run. The os.exit() documentation says:

    os.exit documentation (pkg.odin-lang.org) (https://pkg.odin-lang.org/core/os/#exit) IMPORTANT: Execution skips @(fini) blocks. To perform @(fini) cleanup, invoke runtime._cleanup_runtime beforehand.

  2. Permalink

    The following lets you combine os.exit with return, defer, an exit status, and @fini blocks:

    package main
    
    import "core:fmt"
    import "core:os"
    import "base:runtime"
    
    status_value: int
    main :: proc() {
        defer fmt.println("message deferred")
        if should_stop {
            status_value = 7
            return
        } else {
            status_value = 2
        }
        fmt.println("arbitrary message")
    }
    
    @fini
    finish :: proc "contextless" () {
        context = runtime.default_context()
        fmt.println("Program is terminating.")
        os.exit(status_value)
    }
  3. Permalink

    Move the code that can fail into its own function, rather than working on main itself.

    handle: ^TaskResource
    
    execute :: proc() -> (problem: TaskError) {
        // ...
    }
    
    main :: proc() {
        problem: TaskError
        defer if problem != nil do os.exit(2)
    
        {
            // In place of .fini
            handle = alloc()
            defer release(handle)
    
            problem = execute()
        }
    }

    You'd normally use this pattern when configuring a custom allocator/logger for separate modes (debug, for instance), leaving the implementation alone. This also keeps the Odin runtime available.

Other sides of the same question

  1. Detecting a missing input file

    Hello, I've run into a difficulty with a script of mine. Searching the web hasn't turned up a solution. A folder receives a p…

    Replies 2Latest activity powershell
  2. F# negation when testing file existence

    I'm using this code to signal an error if a file is missing. Instead, this error appears. What did I misunderstand?

    Replies 2Latest activity fsharp
  3. Email alerts for exceptions and errors in an application

    I want an email sent whenever my application encounters an error. exception plus the .go filename and its line number. that w…

    Replies 2Latest activity go