0

i asked a similar question a while back julialang: can (should) this type error be caught at compile time?, and i would like to pose a related question.

function somefun()
    for i = 1:10e10; sin(i); end # a time-consuming loop                  
    
    nonexist_fun() # call to a non existing function => error
end

here a non existing function nonexist_fun() is called and causes an error. unfortunately the error is only discovered at run-time, after spending a long time executing some previous instructions inside somefun(), not at "parse-time".

question:

could and should this be discovered at a preliminary "parse-time" pass?

if such a "parse-time" option exists, how to apply it?

otherwise, what is the recommended workflow, to detect and/or prevent this kind of error ahead of runtime, where the program may explode only after already having wasted a long time executing?

thanks

mrchance
  • 1,133
  • 8
  • 24
  • "should it" is an entirely normative question, that we cannot answer for you. If you decide that you want that, you'd have to switch to a different language or write your own analysis tools. Also, it's undecidable. My recommended workflow would be to try it first with a smaller number. – phipsgabler Sep 21 '20 at 13:34
  • @phipsgabler this is only an example, the "offending code" may be difficult to uncover with tests (eg. using "smaller" data). in any case, if "testing" is the answer, that means that the burden is placed on the programmer instead of tools. – mrchance Sep 21 '20 at 13:38

2 Answers2

6

No, that error can not be caught at "parse-time", because julia does not require things to be defined prior to them being called. It might seem sensible for a language to require that, but it turns out to be quite a pain for mutually recursive functions. (I have used languages the do require this and it is a pain.)

Consider:

julia> foo(x) = rand(Bool) ? bar(x) : 0
foo (generic function with 1 method)

julia> bar(x) = (println(x); foo(x+1))
bar (generic function with 1 method)

julia> foo(0)
0
1
2
3
4
0

Both of those functions are parsed independently, but if you tried to detect that error then

As a rule: julia doesn't throw any errors except syntax errors (and errors encountered during macro expansion) at parse (or for that mater compile) time.

Futher, it is even legal to catch MethodErrors. I can think of few good reasons to do so except for purposes of rethrowing with a better error message. Which I have done before in the wild, this try-catch is primarily, though not exclusively, for MethodErrors.

You might like a linter to detect this for you. At least heursitically. Right now as far as i can tell, the VC-Code linter: StaticLint.jl does not catch it. I do recall that one of the old linters for Julia 0.4 could catch things like this, it might be reasonable to suggest it for StaticLint.jl. Catching it always is not possible, but some easy cases probably can be

Frames Catherine White
  • 27,368
  • 21
  • 87
  • 137
  • 1
    Small correction: The VSCode linter does catch this case, but we've turned off the `MissingRef` pass off by default. – pfitzseb Sep 21 '20 at 14:55
  • @pfitzseb perhaps you might suggest an edit to my answer to explain that. Or maybe an answer of your own showing how to tune it on? – Frames Catherine White Sep 21 '20 at 15:04
  • @pfitzseb regarding linter: perhaps you can anser this question: https://stackoverflow.com/questions/63983407/vs-code-julia-linter-doesnt-work-on-mac – mrchance Sep 21 '20 at 15:14
1

If your goal is to identify "potential problems" before executing the code it can actually be solved in Julia with analyzing the abstract syntax tree (AST) of your function.

Consider this code:

function find_problems(fun, param_types)
    exprs = code_lowered(somefun, ())[1].code
    [e for e in exprs if typeof(e)==Expr && e.head == :call &&
     typeof(e.args[1])==GlobalRef && !isdefined(e.args[1].mod, e.args[1].name)]
end

This is very simple version - does not include branching where you should traverse the AST tree which is still easy, neither it includes problems with other functions (but perhaps you need to test critical pieces of your code?)

Anyway let's give it a spin:

julia> find_problems(somefun, ())
1-element Array{Expr,1}:
 :(Main.nonexist_fun())

You could definitely use this approach to build a tool for avoiding "no function found" errors in long-running codes.

Przemyslaw Szufel
  • 40,002
  • 3
  • 32
  • 62