Sija/debug.cr

Error in macro ?

hutou opened this issue ยท 13 comments

hutou commented

Given the following code:

require "debug"

Debug.enabled = true

class Test(T)
  def initialize(@sources : Enumerable(T))
    @count = @sources.size
  end
end

t = Test.new([1, 2, 3])

with p! t, I get # => #<Test(Int32):0x7f83d3425e70 @sources=[1, 2, 3], @count=3>, which is expected, but with debug!(t), an error occurs :

In zzz.cr:7:23

 7 | @count = @sources.size
                       ^---
Error: Can't calculate size of an open range

The full trace is in the attached zip file:
zzz.trace.zip

Thanks for this shard I find very useful.

hutou commented

Just an hint:
If I add a different type (String, Float, ...) in the array, no error !

t = Test.new([1, 2, "A", 4])
debug! t =>  DEBUG -- test_sija.cr:12 -- t = #<Test(Int32 | String):0x7f6800ea6e10 @count=4, @sources=[1, 2, "A", 4]> (Test(Int32 | String))
Sija commented

There are some strange things goin' on in here... ๐Ÿค”

Sija commented

Your example code stops working immediately after the open ended Range is somewhere in the code - without the debug.cr involved at all:

class Test(T)
  def initialize(@sources : Enumerable(T))
    @count = @sources.size
  end
end
 
p! 0..
p! Test.new([1, 2, 3])

This raises the same error (https://carc.in/#/r/elfa)

Interestingly, the begin-less Range (..10) or the infinite one (..) doesn't trigger the error ๐Ÿค”

I'm sensing this might be a Crystal compiler error...
/cc @asterite @HertzDevil

The open range instantiates Range(Int32, Nil) which means its #size implementation will be taken into account as an override of Enumerable#size. And Range(Int32, Nil)#size raises at compile time which effectively makes this method undefined for Range(Int32, Nil).
I think that's an error in stdlib.

Sija commented

@straight-shoota Yeah, that's more or less expected. What's weird is outlined in my last comment.

Ah you mean the part that the begin-less or infinite range are unaffected?

That's because Range(B, E) is an Enumerable(B), so only the type of the begin value counts. Thus those two are Enumerable(Nil).
The problem I described above appears because 0.. is Enumerable(Int32) and thus the same as [1, 2, 3]. The type of @sources is Enumerable(Int32) and thus the call @sources.size finds implementations in Array#size and Range(Int32, Nil)#size.

Sija commented

@straight-shoota Oh, I see, that makes sense, thanks for the explanation. One thing that kinda bothers me is that the error only happens, when there's a reference to the type-matching Range - which causes these confusing hard-to-identify-and-debug kind of errors - not mentioning that the stack trace and the error message are not helping either.

Yes, I think there are two things to do here:

  1. Range#size should not raise at compile time.
  2. Provide more context for compile time error messages. In this case it would've helped if the error message stated that it's raised while instantiating Range(Int32, Nil)#size as an implementation of Enumerable(Int32)#size.
Sija commented

@straight-shoota Should I create a ticket for that?

Sure, go ahead if you like. Otherwise I'll take care of that in the next days.
Should be two separate issues.

Sija commented

@straight-shoota Actually, I'd leave it to you if you don't mind, since I'm out of time for next days. Just wanted to ensure it will be reported.

Sija commented

I'm gonna keep it open as a reminder for the issues raised.

I created crystal-lang/crystal#13121 and crystal-lang/crystal#13123 to track the raised issues.
This can probably be closed then.