Compiler AST question


#1

Hi all, seeing an error when compiling some code on Linux with Crystal 0.27.2 + LLVM 4.0.0 or LLVM 5.x

BUG: already had enclosing call

However the (very complex) code compiles and tests fine on OSX Crystal 0.27.2 + LLVM 6.0.1
Just wondering why this occurs, is it related to LLVM (i.e. updating this on Linux might solve the issue) or should I be re-writing the code?

As we typically deploy to linux this is an issue for us, however not sure why there is a difference between the platforms and the best way to approach this.


#2

That’s definitely a semantic bug, the platform shouldn’t matter. Can you try to find the minimum code that reproduces it? Also, a bug with such message already exists in the repo, maybe yours happens for the same reason.


#3

Yeah, I’ll see what I do in terms of minimal reproduction - it occurs at the intersection of quite a few different libraries / classes which will make that hard - I primarily posted to see what direction I should be taking to resolve it, so good to know it shouldn’t be related to LLVM.

I had a look at vlads issue before posting here however it is probably unrelated as it it doesn’t occur in an initializer… Also weird that it doesn’t occur on OSX but does occur on Linux, are there many differences in how these platforms are implemented? i.e. use of Grand Central Dispatch that might structure the AST differently, as the code involves channels and callbacks.


#4

The only thing that could happen is that the code that triggers your bug is behind {% if flag?(:linux) %}, for example. It’s not related to LLVM. It probably happens because of the same cause as the existing issue: multiple dispatch with a block where the receiver can have multiple different types, or is a module type.


#5

Thanks for the pointers. I solved the problem with a minor re-write.
I broke up a large function into a couple of smaller ones - this gave me a more descriptive error on linux, would still compile on OSX though. Then I used not_nil (as nilable was the issue on linux) - which led to a new but matching issue on both platforms.

The end result was it didn’t like creating a lazy Future which was computing the values of a bunch of other lazy futures.

array = inputs.map { |input| input.return_future } # => Array(Future(JSON::Any))
lazy { array.map &.get } # => Array(JSON::Any)

Of course minimal reproductions all work in crystal play :frowning:
However this is the error I was getting:

in macro 'method_missing' /Users/steve/Documents/projects/crystal-engine/crystal-engine-driver/src/engine-driver/proxy/drivers.cr:53, line 5:

   1.     results = @drivers.map do |driver|
   2.       driver.function1(   )
   3.     end
   4.
>  5.     lazy { results.map &.get }
   6.

instantiating 'lazy()'
in /usr/local/Cellar/crystal/0.27.2/src/concurrent/future.cr:151: instantiating 'Concurrent::Future(R).class#new()'

  Concurrent::Future.new run_immediately: false, &block
                     ^~~

in /usr/local/Cellar/crystal/0.27.2/src/concurrent/future.cr:15: instance variable '@block' of Concurrent::Future(Array(JSON::Any)) must be Proc(Array(JSON::Any)), not Proc(Array(JSON::Any))

I created a new class that quacks like a Future and it all works now. So still not sure what caused that error - the thing that stands out to me is: must be Proc(Array(JSON::Any)), not Proc(Array(JSON::Any))