Is Cthulhu wrong? Or is it the compiler?

In this fragment, Cthulhu concludes that all types are concrete, known. Yet, at the bottom
it suddenly springs on me csuml is Any. I considered log, and used abs for its argument.
To no avail. Any idea how to fix it, and where it comes from?

        csuml::ComplexF64 = czero::ComplexF64                                                                                                                                                                                                                            
453     csumn::ComplexF64 = czero::ComplexF64                                                                                                                                                                                                                   
454     if (lponel && llorn)                                                                                                                                                                                                                                    
464         @inbounds for ii::Int64 = (1:3)::Int64::Tuple{Int64, Int64}                                                                                                                                                                                         
465             r0::Float64 = ar0::Vector{Float64}[ii::Int64]::Float64                                                                                                                                                                                          
466             ra::Float64 = ara::Vector{Float64}[ii::Int64]::Float64                                                                                                                                                                                          
467             opp::Float64 = aopp::Vector{Float64}[ii::Int64]::Float64                                                                                                                                                                                        
468             if ((r0::Float64 < ra::Float64)::Bool)                                                                                                                                                                                                          
469                 temp::Float64 = ra::Float64                                                                                                                                                                                                                 
470                 ra::Float64 = r0::Float64                                                                                                                                                                                                                   
471                 r0::Float64 = temp::Float64                                                                                                                                                                                                                 
472             end                                                                                                                                                                                                                                             
473             sr0::Float64 = (r0::Float64 * r0::Float64)::Float64                                                                                                                                                                                             
474             sra::Float64 = (ra::Float64 * ra::Float64)::Float64                                                                                                                                                                                             
475             sopp::Float64 = (opp::Float64 * opp::Float64)::Float64                                                                                                                                                                                          
476             a::Float64 = acos(((((((sra::Float64 + sr0::Float64)::Float64 - sopp::Float64)::Float64) / two::Float64)::Float64 / ra::Float64)::Float64 / r0::Float64)::Float64)::Float64                                                                     
477             b::Float64 = atan(((ra * sin(a::Float64)::Float64)::Float64 / ((r0::Float64 - (ra * cos(a::Float64)::Float64)::Float64)::Float64))::Float64)::Float64                                                                                           
478             (csuml::ComplexF64 += (((r0::Float64 * sin(b::Float64)::Float64 * ((log(abs(tan((((b::Float64 + a::Float64)::Float64) / two::Float64)::Float64)::Float64)::Float64)::Float64 - log(abs(tan((b::Float64 / two::Float64)::Float64)::Float64)::Float64)::Float64)::Float64))::Float64) / qarea::Float64)::Float64)::ComplexF64                                                                                                                                                                                     
479             (csumn::ComplexF64 +=(( (((cos((b::Float64 + a::Float64)::Float64)::Float64 - cos(b::Float64)::Float64)::Float64) / r0::Float64)::Float64 / sin(b::Float64)::Float64)::Float64 / qarea::Float64)::Float64)::ComplexF64                          
480         end                                                                                                                                                                                                                                                 
481     end                                                                                                                                                                                                                                                     
482     csumn::ComplexF64 = (csumn - (sko2::ComplexF64 * csuml::Any)::ComplexF64)::ComplexF64  

Actually, it is not just Cthulhu, @code_warntype does the same thing. Everything is fine, until it reports

    csuml::ComplexF64 = (t5::ComplexF64 / qarea::Float64)::ComplexF64                                                                                                                                                                                   
487 csumn::ComplexF64 = (csumn - (sko2::ComplexF64 * csuml::Any)::ComplexF64)::ComplexF64 

One more observation: there are a lot of allocations. Hence I think the compiler boxes things up and therefore it might actually be compiler failure to correctly detect the type.

It’s also strange to me that it’s very confident that *(::ComplexF64, ::Any) returns ComplexF64.

1 Like

The above was tested with 1.8.5. Same result with 1.9.0-rc2, though.

Can you share the function definition this occurred on? Cthulhu just shows what the compiler knows, so there shouldn’t be a mismatch between what it displays and what the compiler infers.

I’m afraid the function is a bit of a monster. Here is the [gist] (mwe.jl · GitHub). To run, include the file.

I figured it out! On line 176 of the gist, csuml is reported to assume type of Any.
This is due to a typo on line 333 (undefined variable)! Unfortunately, I had it backwards:
I thought the variable had become tainted before line 176, while the type was backpropagated
from way below that line. And the tests I wrote for the function failed to test the branch where the undefined variable was referenced. Uff.

Edit: The story is a bit more complex (:slight_smile: ). See below.


Yeah, that’s tricky to find from just @code_typed/@code_warntype alone - I usually use JET’s report_call for that kind of preliminary analysis, which reports such undefined variables.

1 Like

That is good to know, I will check it out.

Are you not using the LSP?
I just copy/pasted your gist, and it gives me Missing reference: fpgv, underlining it.

You also have a lot of variables you’re assigning but not using.

I’m a major proponent of tooling. It can save a lot more time than it takes fiddling with it to get it working.
It does tend to be overzealous with the “possible method call errors” though.


No, I am not. Though in this case it would have been a boon. In general, I found it too concerned about things which were not real problems.

Still baffling to me, if csuml’s type inference was ruined by an operation with an undefined variable way afterward, why wouldn’t it be ruined the whole way and affect other variables like csumn? How would the compiler decide the point where it stops inferring the variable’s type?

1 Like

I think Cthulhu messed up here. On line 176, the compiler clearly concluded csuml was ComplexF64, otherwise the result couldn’t have been of the same type.

Then, csuml can become Any by traversing one path inside the loop starting on line 183. So any of its uses inside the loop should be typed Any. But NOT before the loop.

Edit: reported as issue now.

1 Like


How did you get “Missing Reference”? When I run LSP in VS Code, it does not show me that.

I’m not sure; it works in emacs.
The echo area (bottom of my screen) tells me
Julia: Missing reference: fpgv.

I think there may be settings in VSCode where you can enable some additional diagnostics?

Got it. It is actually a Lint setting.