crystal-lang / crystal

The Crystal Programming Language
https://crystal-lang.org
Apache License 2.0
19.51k stars 1.62k forks source link

Interpreter REPL unable to recalculate type throws inconclusive type errors #13873

Open bcardiff opened 1 year ago

bcardiff commented 1 year ago

I have reproduced this either in Crystal 1.9.2 and on master 5ed476a41adf0719fc540e6c072a521b4c8ed3ec

The following code compiles fine

require "compress/zlib"
require "io/multi_writer"

io = IO::Memory.new
mio = IO::MultiWriter.new(io)
mio << "a"

But when executed in the interpreter (REPL) it fails with a typing issue.

If we remove the require "compress/zlib" the code is interpreted just fine.

icr:1> require "./bug"
In bug.cr:6:5

 6 | mio << "a"
         ^-
Error: instantiating 'IO::MultiWriter#<<(String)'

In /opt/homebrew/Cellar/crystal/1.9.2/share/crystal/src/io.cr:177:9

 177 | obj.to_s self
           ^---
Error: instantiating 'String#to_s(IO::MultiWriter)'

In /opt/homebrew/Cellar/crystal/1.9.2/share/crystal/src/string.cr:5334:8

 5334 | io.write_string(to_slice)
           ^-----------
Error: instantiating 'IO::MultiWriter#write_string(Slice(UInt8))'

In /opt/homebrew/Cellar/crystal/1.9.2/share/crystal/src/io.cr:482:15

 482 | encoder.write(self, slice)
               ^----
Error: instantiating 'IO::Encoder#write(IO::MultiWriter, Slice(UInt8))'

In /opt/homebrew/Cellar/crystal/1.9.2/share/crystal/src/io/encoding.cr:46:12

 46 | io.write(outbuf.to_slice[0, outbuf.size - outbytesleft])
         ^----
Error: instantiating 'IO::MultiWriter#write(Slice(UInt8))'

In /opt/homebrew/Cellar/crystal/1.9.2/share/crystal/src/io/multi_writer.cr:37:21

 37 | @writers.each(&.write(slice))
                      ^----
Error: instantiating 'IO+#write(Slice(UInt8))'

In /opt/homebrew/Cellar/crystal/1.9.2/share/crystal/src/compress/zlib/writer.cr:52:5

 52 | write_header unless @wrote_header
      ^-----------
Error: instantiating 'write_header()'

In /opt/homebrew/Cellar/crystal/1.9.2/share/crystal/src/compress/zlib/writer.cr:88:9

 88 | @io.write_byte cmf
          ^---------
Error: instantiating 'IO+#write_byte(UInt8)'

In /opt/homebrew/Cellar/crystal/1.9.2/share/crystal/src/io/buffered.cr:167:8

 167 | if sync?
          ^----
Error: instantiating 'sync?()'

In /opt/homebrew/Cellar/crystal/1.9.2/share/crystal/src/io/buffered.cr:214:5

 214 | @sync
       ^----
Error: can't infer the type of instance variable '@sync' of Compress::Zlib::Reader

Could you add a type annotation like this

    class Compress::Zlib::Reader
      @sync : Type
    end

replacing `Type` with the expected type of `@sync`?
beta-ziliani commented 1 year ago

I add more information: Running crystal i bug.cr with the above code works fine, as well as first loading a file requiring zlib, and then dropping to the interpreter for executing the rest of the lines.

straight-shoota commented 1 year ago

So looks like this is related to #12624

beta-ziliani commented 1 year ago

Not really, or at least it's hard to see the connection: it doesn't fail because of a missing lib symbol.

straight-shoota commented 1 year ago

I suspect both are related to the underlying problem that REPL mode does not recalculate types.

bcardiff commented 1 year ago

As a workaround I ended up needing to duplicate the ivar initializers, between the prelude and the requires of the actual files.

# require or eval this whole patch

class Compress::Zlib::Reader < IO
  @in_buffer = Pointer(UInt8).null
  @out_buffer = Pointer(UInt8).null
  @in_buffer_rem = Bytes.empty
  @out_count = 0
  @sync = false
  @read_buffering = true
  @flush_on_newline = false
  @buffer_size = IO::DEFAULT_BUFFER_SIZE
end

class Compress::Deflate::Reader < IO
  @in_buffer = Pointer(UInt8).null
  @out_buffer = Pointer(UInt8).null
  @in_buffer_rem = Bytes.empty
  @out_count = 0
  @sync = false
  @read_buffering = true
  @flush_on_newline = false
  @buffer_size = IO::DEFAULT_BUFFER_SIZE
end

require "compress/zlib"
require "compress/deflate"

Then the following snippet can be evaluated in the interpreter successfully

require "compress/zlib"
require "io/multi_writer"

io = IO::Memory.new
mio = IO::MultiWriter.new(io)
mio << "a"