fn assign_return_types_helper(prog: &mut SirProgram, func: FunctionId) -> WeldResult<Type> {
    use crate::sir::Terminator::*;

    // Base case - already visited this function!
    if prog.funcs[func].return_type != Unknown {
        return Ok(prog.funcs[func].return_type.clone());
    }

    // Symbol returned by the terminator.
    let mut return_symbol = None;
    {
        let function = &prog.funcs[func];
        for block in function.blocks.iter() {
            match block.terminator {
                Branch { .. } => (),
                JumpBlock(_) => (),
                ProgramReturn(ref sym) | EndFunction(ref sym) => {
                    // Type should be set during AST -> SIR.
                    return_symbol = Some(sym.clone());
                }
                Crash => (),
            }
        }
    }

    // Need to do this nonsense to circumvent borrow checker...
    if let Some(symbol) = return_symbol {
        let return_type = prog.funcs[func].symbol_type(&symbol)?.clone();
        prog.funcs[func].return_type = return_type.clone();
        Ok(return_type)
    } else {
        // Indicates that the function did not return...
        unreachable!()
    }
}
Recursive helper for `assign_return_types`.

This function recurses assigns a return type to the given function ID. It recurses into the
terminators and propogates the return types of the terminator to current function.
weld/src/sir/mod.rs