Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IO.write is broken in 0.35.0 #9454

Closed
bararchy opened this issue Jun 10, 2020 · 23 comments
Closed

IO.write is broken in 0.35.0 #9454

bararchy opened this issue Jun 10, 2020 · 23 comments

Comments

@bararchy
Copy link
Contributor

Error

In /usr/lib/crystal/io/hexdump.cr:35:28

 35 | def write(buf : Bytes) : Int64
                               ^
Error: method must return Int64 but it is returning (Int64 | Nil)
@jhass jhass added kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:stdlib labels Jun 10, 2020
@jhass jhass added this to the 1.0.0 milestone Jun 10, 2020
@bararchy
Copy link
Contributor Author

@jhass Update, seems like basically most IO.write are broken, can't see how this passed CI

class IO::Hexdump < IO
  def write(buf : Bytes) : Int64?
    return 0i64 if buf.empty?

    @io.write(buf).tap do
      @output.puts buf.hexdump if @write
    end
  end
end

class IO::Stapled < IO
  # Writes a slice to `writer`.
  def write(slice : Bytes) : Int64?
    check_open

    return 0i64 if slice.empty?

    @writer.write(slice)
  end
end

module OpenSSL
class DigestIO < IO
def write(slice : Bytes) : Int64?
    return 0i64 if slice.empty?

    if @mode.write?
      digest_algorithm.update(slice)
    end
    io.write(slice)
  end
end
end

etc... and many more

@bararchy bararchy changed the title 0.35.0 Time.utc.to_rfc3339 is broken IO.write is broken in 0.35.0 Jun 10, 2020
@asterite
Copy link
Member

Any code to reproduce this?

@bararchy
Copy link
Contributor Author

@asterite seems that "#{Time.utc.to_rfc3339}" does that for me :)

@asterite
Copy link
Member

@bararchy That works fine for me, no warnings

@asterite
Copy link
Member

Are you compiling just that program without any dependencies?

IO::Hexdump relies on the underlying @io.write to get its type. So if you have any other IO that han't been upgraded to 0.35.0, you will get that warning.

Do you have other IOs in your program?

@asterite asterite added kind:question and removed kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:stdlib labels Jun 10, 2020
@bararchy
Copy link
Contributor Author

@asterite it's being called from

formatter = ::Log::Formatter.new do |entry, io|
    io << "[#{Time.utc.to_rfc3339}] [#{entry.severity}] #{entry.message}"
  end

@bararchy
Copy link
Contributor Author

I don't have anythings else, and the IO is basically

formatter = ::Log::Formatter.new do |entry, io|
    io << "[#{Time.utc.to_rfc3339}] [#{entry.severity}] #{entry.message}"
  end

  backend = ::Log::IOBackend.new
  backend.formatter = formatter

So I guess it's STDOUT?

@asterite
Copy link
Member

I'm trying all of your snippets, no warnings whatsoever.

Could you post a complete snippet? The snippet should be compilable by doing crystal foo.cr. I know what I'm asking is dumb, but your last two snippets don't compile as-is, I needed to add require "log". But even then there's no warning.

Can you point to a github repo? Are you sure it's just a single file without any other dependencies?

@asterite
Copy link
Member

Maybe also record what you are doing so we can see what's going on? Print crystal --version and so on. Platform too.

@bararchy
Copy link
Contributor Author

bararchy commented Jun 10, 2020

@asterite it's from our main project, a large, large crystal program :)

Full error trace
 8 | Log.info { "Start logging" }
         ^---
Error: instantiating 'Log#info()'


In /usr/lib/crystal/log/log.cr:36:3

 36 | {% for method, severity in {
      ^
Error: expanding macro


There was a problem expanding macro 'macro_140366444210144'

Called macro defined in /usr/lib/crystal/log/log.cr:36:3

 36 | {% for method, severity in {

Which expanded to:

 >   1 | 
 >   2 |     # Logs a message if the logger's current severity is lower or equal to `0`.
 >   3 |     def trace(*, exception : Exception? = nil)
 >   4 |       severity = Severity.new(0)
 >   5 |       return unless level <= severity
 >   6 | 
 >   7 |       return unless backend = @backend
 >   8 | 
 >   9 |       dsl = Emitter.new(@source, severity, exception)
 >  10 |       result = yield dsl
 >  11 |       entry =
 >  12 |         case result
 >  13 |         when Entry
 >  14 |           result
 >  15 |         else
 >  16 |           dsl.emit(result.to_s)
 >  17 |         end
 >  18 | 
 >  19 |       backend.write entry
 >  20 |     end
 >  21 |   
 >  22 |     # Logs a message if the logger's current severity is lower or equal to `1`.
 >  23 |     def debug(*, exception : Exception? = nil)
 >  24 |       severity = Severity.new(1)
 >  25 |       return unless level <= severity
 >  26 | 
 >  27 |       return unless backend = @backend
 >  28 | 
 >  29 |       dsl = Emitter.new(@source, severity, exception)
 >  30 |       result = yield dsl
 >  31 |       entry =
 >  32 |         case result
 >  33 |         when Entry
 >  34 |           result
 >  35 |         else
 >  36 |           dsl.emit(result.to_s)
 >  37 |         end
 >  38 | 
 >  39 |       backend.write entry
 >  40 |     end
 >  41 |   
 >  42 |     # Logs a message if the logger's current severity is lower or equal to `2`.
 >  43 |     def info(*, exception : Exception? = nil)
 >  44 |       severity = Severity.new(2)
 >  45 |       return unless level <= severity
 >  46 | 
 >  47 |       return unless backend = @backend
 >  48 | 
 >  49 |       dsl = Emitter.new(@source, severity, exception)
 >  50 |       result = yield dsl
 >  51 |       entry =
 >  52 |         case result
 >  53 |         when Entry
 >  54 |           result
 >  55 |         else
 >  56 |           dsl.emit(result.to_s)
 >  57 |         end
 >  58 | 
 >  59 |       backend.write entry
 >  60 |     end
 >  61 |   
 >  62 |     # Logs a message if the logger's current severity is lower or equal to `3`.
 >  63 |     def notice(*, exception : Exception? = nil)
 >  64 |       severity = Severity.new(3)
 >  65 |       return unless level <= severity
 >  66 | 
 >  67 |       return unless backend = @backend
 >  68 | 
 >  69 |       dsl = Emitter.new(@source, severity, exception)
 >  70 |       result = yield dsl
 >  71 |       entry =
 >  72 |         case result
 >  73 |         when Entry
 >  74 |           result
 >  75 |         else
 >  76 |           dsl.emit(result.to_s)
 >  77 |         end
 >  78 | 
 >  79 |       backend.write entry
 >  80 |     end
 >  81 |   
 >  82 |     # Logs a message if the logger's current severity is lower or equal to `4`.
 >  83 |     def warn(*, exception : Exception? = nil)
 >  84 |       severity = Severity.new(4)
 >  85 |       return unless level <= severity
 >  86 | 
 >  87 |       return unless backend = @backend
 >  88 | 
 >  89 |       dsl = Emitter.new(@source, severity, exception)
 >  90 |       result = yield dsl
 >  91 |       entry =
 >  92 |         case result
 >  93 |         when Entry
 >  94 |           result
 >  95 |         else
 >  96 |           dsl.emit(result.to_s)
 >  97 |         end
 >  98 | 
 >  99 |       backend.write entry
 > 100 |     end
 > 101 |   
 > 102 |     # Logs a message if the logger's current severity is lower or equal to `5`.
 > 103 |     def error(*, exception : Exception? = nil)
 > 104 |       severity = Severity.new(5)
 > 105 |       return unless level <= severity
 > 106 | 
 > 107 |       return unless backend = @backend
 > 108 | 
 > 109 |       dsl = Emitter.new(@source, severity, exception)
 > 110 |       result = yield dsl
 > 111 |       entry =
 > 112 |         case result
 > 113 |         when Entry
 > 114 |           result
 > 115 |         else
 > 116 |           dsl.emit(result.to_s)
 > 117 |         end
 > 118 | 
 > 119 |       backend.write entry
 > 120 |     end
 > 121 |   
 > 122 |     # Logs a message if the logger's current severity is lower or equal to `6`.
 > 123 |     def fatal(*, exception : Exception? = nil)
 > 124 |       severity = Severity.new(6)
 > 125 |       return unless level <= severity
 > 126 | 
 > 127 |       return unless backend = @backend
 > 128 | 
 > 129 |       dsl = Emitter.new(@source, severity, exception)
 > 130 |       result = yield dsl
 > 131 |       entry =
 > 132 |         case result
 > 133 |         when Entry
 > 134 |           result
 > 135 |         else
 > 136 |           dsl.emit(result.to_s)
 > 137 |         end
 > 138 | 
 > 139 |       backend.write entry
 > 140 |     end
 > 141 |   
Error: instantiating 'Log::Backend+#write(Log::Entry)'


In /usr/lib/crystal/log/io_backend.cr:11:12

 11 | @mutex.synchronize do
             ^----------
Error: instantiating 'Mutex#synchronize()'


In /usr/lib/crystal/log/io_backend.cr:11:12

 11 | @mutex.synchronize do
             ^----------
Error: instantiating 'Mutex#synchronize()'


In /usr/lib/crystal/log/io_backend.cr:12:7

 12 | format(entry)
      ^-----
Error: instantiating 'format(Log::Entry)'


In /usr/lib/crystal/log/io_backend.cr:21:16

 21 | @formatter.format(entry, io)
                 ^-----
Error: instantiating 'Log::Formatter#format(Log::Entry, IO+)'


In /usr/lib/crystal/log/format.cr:149:22

 149 | new(entry, io).run
                      ^--
Error: instantiating 'Log::StaticFormatter+#run()'


In /usr/lib/crystal/log/format.cr:197:43

 197 | Log.define_formatter Log::ShortFormat, "#{timestamp} #{severity} - #{source(after: ": ")}#{message}" \
                                                 ^--------
Error: instantiating 'timestamp()'


In /usr/lib/crystal/log/format.cr:66:24

 66 | @entry.timestamp.to_rfc3339(@io, fraction_digits: 6)
                       ^---------
Error: instantiating 'Time#to_rfc3339(IO+)'


In /usr/lib/crystal/time.cr:1125:22

 1125 | Format::RFC_3339.format(to_utc, io, fraction_digits)
                         ^-----
Error: instantiating 'Time::Format::RFC_3339:Module#format(Time, IO+, Int32)'


In /usr/lib/crystal/time/format/custom/rfc_3339.cr:14:17

 14 | formatter.rfc_3339(fraction_digits: fraction_digits)
                ^-------
Error: instantiating 'Time::Format::Formatter#rfc_3339()'


In /usr/lib/crystal/time/format/custom/rfc_3339.cr:28:7

 28 | year_month_day
      ^-------------
Error: instantiating 'year_month_day()'


In /usr/lib/crystal/time/format/pattern.cr:191:7

 191 | year
       ^---
Error: instantiating 'year()'


In /usr/lib/crystal/time/format/formatter.cr:15:7

 15 | pad4(time.year, '0')
      ^---
Error: instantiating 'pad4(Int32, Char)'


In /usr/lib/crystal/time/format/formatter.cr:260:10

 260 | io.write_byte padding.ord.to_u8 if value < 1000
          ^---------
Error: instantiating 'IO+#write_byte(UInt8)'


In /usr/lib/crystal/io/buffered.cr:177:14

 177 | return super
              ^----
Error: instantiating 'super(UInt8)'


In /usr/lib/crystal/io.cr:847:5

 847 | write Slice.new(pointerof(x), 1)
       ^----
Error: instantiating 'write(Slice(UInt8))'


In /usr/lib/crystal/io/buffered.cr:141:7

 141 | unbuffered_write(slice)
       ^---------------
Error: instantiating 'unbuffered_write(Slice(UInt8))'


In /usr/lib/crystal/http/server/response.cr:203:9

 203 | ensure_headers_written
       ^---------------------
Error: instantiating 'ensure_headers_written()'


In /usr/lib/crystal/http/server/response.cr:242:30

 242 | response.cookies.add_response_headers(response.headers)
                        ^-------------------
Error: instantiating 'HTTP::Cookies#add_response_headers(HTTP::Headers)'


In /usr/lib/crystal/http/cookie.cr:309:42

 309 | headers.add("Set-Cookie", cookie.to_set_cookie_header)
                                        ^-------------------
Error: instantiating 'HTTP::Cookie#to_set_cookie_header()'


In /usr/lib/crystal/http/cookie.cr:40:14

 40 | String.build do |header|
             ^----
Error: instantiating 'String.class#build()'


In /usr/lib/crystal/string.cr:271:21

 271 | String::Builder.build(capacity) do |builder|
                       ^----
Error: instantiating 'String::Builder.class#build(Int32)'


In /usr/lib/crystal/string.cr:271:21

 271 | String::Builder.build(capacity) do |builder|
                       ^----
Error: instantiating 'String::Builder.class#build(Int32)'


In /usr/lib/crystal/http/cookie.cr:40:14

 40 | String.build do |header|
             ^----
Error: instantiating 'String.class#build()'


In /usr/lib/crystal/http/cookie.cr:44:37

 44 | header << "; expires=#{HTTP.format_time(expires)}" if expires
                                  ^----------
Error: instantiating 'HTTP:Module#format_time(Time)'


In /usr/lib/crystal/http/common.cr:366:29

 366 | Time::Format::HTTP_DATE.format(time)
                               ^-----
Error: instantiating 'Time::Format::HTTP_DATE:Module#format(Time)'


In /usr/lib/crystal/time/format/custom/http_date.cr:41:14

 41 | String.build do |io|
             ^----
Error: instantiating 'String.class#build()'


In /usr/lib/crystal/string.cr:271:21

 271 | String::Builder.build(capacity) do |builder|
                       ^----
Error: instantiating 'String::Builder.class#build(Int32)'


In /usr/lib/crystal/string.cr:271:21

 271 | String::Builder.build(capacity) do |builder|
                       ^----
Error: instantiating 'String::Builder.class#build(Int32)'


In /usr/lib/crystal/time/format/custom/http_date.cr:41:14

 41 | String.build do |io|
             ^----
Error: instantiating 'String.class#build()'


In /usr/lib/crystal/time/format/custom/http_date.cr:42:9

 42 | format(time, io)
      ^-----
Error: instantiating 'format(Time, String::Builder)'


In /usr/lib/crystal/time/format/custom/http_date.cr:33:17

 33 | formatter.rfc_2822(time_zone_gmt: true, two_digit_day: true)
                ^-------
Error: instantiating 'Time::Format::Formatter#rfc_2822()'


In /usr/lib/crystal/time/format/custom/rfc_2822.cr:31:7

 31 | short_day_name_with_comma?
      ^-------------------------
Error: instantiating 'short_day_name_with_comma?()'


In /usr/lib/crystal/time/format/formatter.cr:99:7

 99 | short_day_name
      ^-------------
Error: instantiating 'short_day_name()'


In /usr/lib/crystal/time/format/formatter.cr:91:10

 91 | io << get_short_day_name
         ^-
Error: instantiating 'IO+#<<(String)'


In /usr/lib/crystal/io.cr:175:9

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


In /usr/lib/crystal/string.cr:4789:8

 4789 | io.write_utf8(to_slice)
           ^---------
Error: instantiating 'IO+#write_utf8(Slice(UInt8))'


In /usr/lib/crystal/io.cr:469:15

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


In /usr/lib/crystal/io/encoding.cr:42:30

 42 | bytes_written &+= io.write(outbuf.to_slice[0, outbuf.size - outbytesleft])
                           ^----
Error: instantiating 'IO+#write(Slice(UInt8))'


In /usr/lib/crystal/compress/zlib/writer.cr:52:5

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


In /usr/lib/crystal/compress/zlib/writer.cr:90:9

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


In /usr/lib/crystal/io/buffered.cr:177:14

 177 | return super
              ^----
Error: instantiating 'super(UInt8)'


In /usr/lib/crystal/io.cr:847:5

 847 | write Slice.new(pointerof(x), 1)
       ^----
Error: instantiating 'write(Slice(UInt8))'


In /usr/lib/crystal/io/buffered.cr:148:9

 148 | flush
       ^----
Error: instantiating 'flush()'


In /usr/lib/crystal/io/buffered.cr:229:5

 229 | unbuffered_flush
       ^---------------
Error: instantiating 'unbuffered_flush()'


In /usr/lib/crystal/openssl/ssl/socket.cr:150:13

 150 | @bio.io.flush
               ^----
Error: instantiating 'IO+#flush()'


In /usr/lib/crystal/compress/zlib/writer.cr:68:15

 68 | @flate_io.flush
                ^----
Error: instantiating 'Compress::Deflate::Writer#flush()'


In /usr/lib/crystal/compress/deflate/writer.cr:62:5

 62 | consume_output LibZ::Flush::SYNC_FLUSH
      ^-------------
Error: instantiating 'consume_output(LibZ::Flush)'


In /usr/lib/crystal/compress/deflate/writer.cr:92:5

 92 | loop do
      ^---
Error: instantiating 'loop()'


In /usr/lib/crystal/compress/deflate/writer.cr:92:5

 92 | loop do
      ^---
Error: instantiating 'loop()'


In /usr/lib/crystal/compress/deflate/writer.cr:96:15

 96 | @output.write(@buf.to_slice[0, @buf.size - @stream.avail_out])
              ^----
Error: instantiating 'IO+#write(Slice(UInt8))'


In /usr/lib/crystal/compress/zip/checksum_writer.cr:21:10

 21 | io.write(slice)
         ^----
Error: instantiating 'IO+#write(Slice(UInt8))'


In /usr/lib/crystal/http/web_socket/protocol.cr:63:9

 63 | flush(final: false)
      ^----
Error: instantiating 'flush()'


In /usr/lib/crystal/http/web_socket/protocol.cr:78:18

 78 | @websocket.send(
                 ^---
Error: instantiating 'HTTP::WebSocket::Protocol#send(Slice(UInt8), HTTP::WebSocket::Protocol::Opcode)'


In /usr/lib/crystal/http/web_socket/protocol.cr:104:5

 104 | write_header(data.size, opcode, flags)
       ^-----------
Error: instantiating 'write_header(Int32, HTTP::WebSocket::Protocol::Opcode, HTTP::WebSocket::Protocol::Flags)'


In /usr/lib/crystal/http/web_socket/protocol.cr:122:9

 122 | @io.write_byte(flags.value | opcode.value)
           ^---------
Error: instantiating 'IO+#write_byte(UInt8)'


In /usr/lib/crystal/io/stapled.cr:71:13

 71 | @writer.write_byte(byte)
              ^---------
Error: instantiating 'IO+#write_byte(UInt8)'


In /usr/lib/crystal/io.cr:847:5

 847 | write Slice.new(pointerof(x), 1)
       ^----
Error: instantiating 'write(Slice(UInt8))'


In /usr/lib/crystal/http/server/response.cr:86:15

 86 | @output.write(slice)
              ^----
Error: instantiating 'IO+#write(Slice(UInt8))'


In /usr/lib/crystal/compress/gzip/writer.cr:76:16

 76 | flate_io = write_header
                 ^-----------
Error: instantiating 'write_header()'


In /usr/lib/crystal/compress/gzip/writer.cr:118:14

 118 | header.to_io(@io)
              ^----
Error: instantiating 'Compress::Gzip::Header#to_io(IO+)'


In /usr/lib/crystal/compress/gzip/header.cr:80:8

 80 | io.write_bytes(modification_time.to_unix.to_u32, IO::ByteFormat::LittleEndian)
         ^----------
Error: instantiating 'IO+#write_bytes(UInt32, IO::ByteFormat::LittleEndian:Module)'


In /usr/lib/crystal/io.cr:865:12

 865 | object.to_io(self, format)
              ^----
Error: instantiating 'UInt32#to_io(IO+, IO::ByteFormat::LittleEndian:Module)'


In /usr/lib/crystal/int.cr:677:12

 677 | format.encode(self, io)
              ^-----
Error: instantiating 'IO::ByteFormat::LittleEndian:Module#encode(UInt32, IO+)'


In /usr/lib/crystal/io/byte_format.cr:123:3

 123 | {% for mod in %w(LittleEndian BigEndian) %}
       ^
Error: expanding macro


There was a problem expanding macro 'macro_140366486104592'

Called macro defined in /usr/lib/crystal/io/byte_format.cr:123:3

 123 | {% for mod in %w(LittleEndian BigEndian) %}

Which expanded to:

 >   1 | 
 >   2 |     module LittleEndian
 >   3 |       
 >   4 |         
 >   5 | 
 >   6 |         def self.encode(int : Int8, io : IO) : Int64
 >   7 |           buffer = int.unsafe_as(StaticArray(UInt8, 1))
 >   8 |           buffer.reverse! unless SystemEndian == self
 >   9 |           io.write(buffer.to_slice)
 >  10 |           Int64.new(1)
 >  11 |         end
 >  12 | 
 >  13 |         def self.encode(int : Int8, bytes : Bytes) : Int64
 >  14 |           buffer = int.unsafe_as(StaticArray(UInt8, 1))
 >  15 |           buffer.reverse! unless SystemEndian == self
 >  16 |           buffer.to_slice.copy_to(bytes)
 >  17 |           Int64.new(1)
 >  18 |         end
 >  19 | 
 >  20 |         def self.decode(type : Int8.class, io : IO)
 >  21 |           buffer = uninitialized UInt8[1]
 >  22 |           io.read_fully(buffer.to_slice)
 >  23 |           buffer.reverse! unless SystemEndian == self
 >  24 |           buffer.unsafe_as(Int8)
 >  25 |         end
 >  26 | 
 >  27 |         def self.decode(type : Int8.class, bytes : Bytes)
 >  28 |           buffer = uninitialized UInt8[1]
 >  29 |           bytes.to_slice[0, 1].copy_to(buffer.to_slice)
 >  30 |           buffer.reverse! unless SystemEndian == self
 >  31 |           buffer.unsafe_as(Int8)
 >  32 |         end
 >  33 |       
 >  34 |         
 >  35 | 
 >  36 |         def self.encode(int : UInt8, io : IO) : Int64
 >  37 |           buffer = int.unsafe_as(StaticArray(UInt8, 1))
 >  38 |           buffer.reverse! unless SystemEndian == self
 >  39 |           io.write(buffer.to_slice)
 >  40 |           Int64.new(1)
 >  41 |         end
 >  42 | 
 >  43 |         def self.encode(int : UInt8, bytes : Bytes) : Int64
 >  44 |           buffer = int.unsafe_as(StaticArray(UInt8, 1))
 >  45 |           buffer.reverse! unless SystemEndian == self
 >  46 |           buffer.to_slice.copy_to(bytes)
 >  47 |           Int64.new(1)
 >  48 |         end
 >  49 | 
 >  50 |         def self.decode(type : UInt8.class, io : IO)
 >  51 |           buffer = uninitialized UInt8[1]
 >  52 |           io.read_fully(buffer.to_slice)
 >  53 |           buffer.reverse! unless SystemEndian == self
 >  54 |           buffer.unsafe_as(UInt8)
 >  55 |         end
 >  56 | 
 >  57 |         def self.decode(type : UInt8.class, bytes : Bytes)
 >  58 |           buffer = uninitialized UInt8[1]
 >  59 |           bytes.to_slice[0, 1].copy_to(buffer.to_slice)
 >  60 |           buffer.reverse! unless SystemEndian == self
 >  61 |           buffer.unsafe_as(UInt8)
 >  62 |         end
 >  63 |       
 >  64 |         
 >  65 | 
 >  66 |         def self.encode(int : Int16, io : IO) : Int64
 >  67 |           buffer = int.unsafe_as(StaticArray(UInt8, 2))
 >  68 |           buffer.reverse! unless SystemEndian == self
 >  69 |           io.write(buffer.to_slice)
 >  70 |           Int64.new(2)
 >  71 |         end
 >  72 | 
 >  73 |         def self.encode(int : Int16, bytes : Bytes) : Int64
 >  74 |           buffer = int.unsafe_as(StaticArray(UInt8, 2))
 >  75 |           buffer.reverse! unless SystemEndian == self
 >  76 |           buffer.to_slice.copy_to(bytes)
 >  77 |           Int64.new(2)
 >  78 |         end
 >  79 | 
 >  80 |         def self.decode(type : Int16.class, io : IO)
 >  81 |           buffer = uninitialized UInt8[2]
 >  82 |           io.read_fully(buffer.to_slice)
 >  83 |           buffer.reverse! unless SystemEndian == self
 >  84 |           buffer.unsafe_as(Int16)
 >  85 |         end
 >  86 | 
 >  87 |         def self.decode(type : Int16.class, bytes : Bytes)
 >  88 |           buffer = uninitialized UInt8[2]
 >  89 |           bytes.to_slice[0, 2].copy_to(buffer.to_slice)
 >  90 |           buffer.reverse! unless SystemEndian == self
 >  91 |           buffer.unsafe_as(Int16)
 >  92 |         end
 >  93 |       
 >  94 |         
 >  95 | 
 >  96 |         def self.encode(int : UInt16, io : IO) : Int64
 >  97 |           buffer = int.unsafe_as(StaticArray(UInt8, 2))
 >  98 |           buffer.reverse! unless SystemEndian == self
 >  99 |           io.write(buffer.to_slice)
 > 100 |           Int64.new(2)
 > 101 |         end
 > 102 | 
 > 103 |         def self.encode(int : UInt16, bytes : Bytes) : Int64
 > 104 |           buffer = int.unsafe_as(StaticArray(UInt8, 2))
 > 105 |           buffer.reverse! unless SystemEndian == self
 > 106 |           buffer.to_slice.copy_to(bytes)
 > 107 |           Int64.new(2)
 > 108 |         end
 > 109 | 
 > 110 |         def self.decode(type : UInt16.class, io : IO)
 > 111 |           buffer = uninitialized UInt8[2]
 > 112 |           io.read_fully(buffer.to_slice)
 > 113 |           buffer.reverse! unless SystemEndian == self
 > 114 |           buffer.unsafe_as(UInt16)
 > 115 |         end
 > 116 | 
 > 117 |         def self.decode(type : UInt16.class, bytes : Bytes)
 > 118 |           buffer = uninitialized UInt8[2]
 > 119 |           bytes.to_slice[0, 2].copy_to(buffer.to_slice)
 > 120 |           buffer.reverse! unless SystemEndian == self
 > 121 |           buffer.unsafe_as(UInt16)
 > 122 |         end
 > 123 |       
 > 124 |         
 > 125 | 
 > 126 |         def self.encode(int : Int32, io : IO) : Int64
 > 127 |           buffer = int.unsafe_as(StaticArray(UInt8, 4))
 > 128 |           buffer.reverse! unless SystemEndian == self
 > 129 |           io.write(buffer.to_slice)
 > 130 |           Int64.new(4)
 > 131 |         end
 > 132 | 
 > 133 |         def self.encode(int : Int32, bytes : Bytes) : Int64
 > 134 |           buffer = int.unsafe_as(StaticArray(UInt8, 4))
 > 135 |           buffer.reverse! unless SystemEndian == self
 > 136 |           buffer.to_slice.copy_to(bytes)
 > 137 |           Int64.new(4)
 > 138 |         end
 > 139 | 
 > 140 |         def self.decode(type : Int32.class, io : IO)
 > 141 |           buffer = uninitialized UInt8[4]
 > 142 |           io.read_fully(buffer.to_slice)
 > 143 |           buffer.reverse! unless SystemEndian == self
 > 144 |           buffer.unsafe_as(Int32)
 > 145 |         end
 > 146 | 
 > 147 |         def self.decode(type : Int32.class, bytes : Bytes)
 > 148 |           buffer = uninitialized UInt8[4]
 > 149 |           bytes.to_slice[0, 4].copy_to(buffer.to_slice)
 > 150 |           buffer.reverse! unless SystemEndian == self
 > 151 |           buffer.unsafe_as(Int32)
 > 152 |         end
 > 153 |       
 > 154 |         
 > 155 | 
 > 156 |         def self.encode(int : UInt32, io : IO) : Int64
 > 157 |           buffer = int.unsafe_as(StaticArray(UInt8, 4))
 > 158 |           buffer.reverse! unless SystemEndian == self
 > 159 |           io.write(buffer.to_slice)
 > 160 |           Int64.new(4)
 > 161 |         end
 > 162 | 
 > 163 |         def self.encode(int : UInt32, bytes : Bytes) : Int64
 > 164 |           buffer = int.unsafe_as(StaticArray(UInt8, 4))
 > 165 |           buffer.reverse! unless SystemEndian == self
 > 166 |           buffer.to_slice.copy_to(bytes)
 > 167 |           Int64.new(4)
 > 168 |         end
 > 169 | 
 > 170 |         def self.decode(type : UInt32.class, io : IO)
 > 171 |           buffer = uninitialized UInt8[4]
 > 172 |           io.read_fully(buffer.to_slice)
 > 173 |           buffer.reverse! unless SystemEndian == self
 > 174 |           buffer.unsafe_as(UInt32)
 > 175 |         end
 > 176 | 
 > 177 |         def self.decode(type : UInt32.class, bytes : Bytes)
 > 178 |           buffer = uninitialized UInt8[4]
 > 179 |           bytes.to_slice[0, 4].copy_to(buffer.to_slice)
 > 180 |           buffer.reverse! unless SystemEndian == self
 > 181 |           buffer.unsafe_as(UInt32)
 > 182 |         end
 > 183 |       
 > 184 |         
 > 185 | 
 > 186 |         def self.encode(int : Int64, io : IO) : Int64
 > 187 |           buffer = int.unsafe_as(StaticArray(UInt8, 8))
 > 188 |           buffer.reverse! unless SystemEndian == self
 > 189 |           io.write(buffer.to_slice)
 > 190 |           Int64.new(8)
 > 191 |         end
 > 192 | 
 > 193 |         def self.encode(int : Int64, bytes : Bytes) : Int64
 > 194 |           buffer = int.unsafe_as(StaticArray(UInt8, 8))
 > 195 |           buffer.reverse! unless SystemEndian == self
 > 196 |           buffer.to_slice.copy_to(bytes)
 > 197 |           Int64.new(8)
 > 198 |         end
 > 199 | 
 > 200 |         def self.decode(type : Int64.class, io : IO)
 > 201 |           buffer = uninitialized UInt8[8]
 > 202 |           io.read_fully(buffer.to_slice)
 > 203 |           buffer.reverse! unless SystemEndian == self
 > 204 |           buffer.unsafe_as(Int64)
 > 205 |         end
 > 206 | 
 > 207 |         def self.decode(type : Int64.class, bytes : Bytes)
 > 208 |           buffer = uninitialized UInt8[8]
 > 209 |           bytes.to_slice[0, 8].copy_to(buffer.to_slice)
 > 210 |           buffer.reverse! unless SystemEndian == self
 > 211 |           buffer.unsafe_as(Int64)
 > 212 |         end
 > 213 |       
 > 214 |         
 > 215 | 
 > 216 |         def self.encode(int : UInt64, io : IO) : Int64
 > 217 |           buffer = int.unsafe_as(StaticArray(UInt8, 8))
 > 218 |           buffer.reverse! unless SystemEndian == self
 > 219 |           io.write(buffer.to_slice)
 > 220 |           Int64.new(8)
 > 221 |         end
 > 222 | 
 > 223 |         def self.encode(int : UInt64, bytes : Bytes) : Int64
 > 224 |           buffer = int.unsafe_as(StaticArray(UInt8, 8))
 > 225 |           buffer.reverse! unless SystemEndian == self
 > 226 |           buffer.to_slice.copy_to(bytes)
 > 227 |           Int64.new(8)
 > 228 |         end
 > 229 | 
 > 230 |         def self.decode(type : UInt64.class, io : IO)
 > 231 |           buffer = uninitialized UInt8[8]
 > 232 |           io.read_fully(buffer.to_slice)
 > 233 |           buffer.reverse! unless SystemEndian == self
 > 234 |           buffer.unsafe_as(UInt64)
 > 235 |         end
 > 236 | 
 > 237 |         def self.decode(type : UInt64.class, bytes : Bytes)
 > 238 |           buffer = uninitialized UInt8[8]
 > 239 |           bytes.to_slice[0, 8].copy_to(buffer.to_slice)
 > 240 |           buffer.reverse! unless SystemEndian == self
 > 241 |           buffer.unsafe_as(UInt64)
 > 242 |         end
 > 243 |       
 > 244 |         
 > 245 | 
 > 246 |         def self.encode(int : Int128, io : IO) : Int64
 > 247 |           buffer = int.unsafe_as(StaticArray(UInt8, 16))
 > 248 |           buffer.reverse! unless SystemEndian == self
 > 249 |           io.write(buffer.to_slice)
 > 250 |           Int64.new(16)
 > 251 |         end
 > 252 | 
 > 253 |         def self.encode(int : Int128, bytes : Bytes) : Int64
 > 254 |           buffer = int.unsafe_as(StaticArray(UInt8, 16))
 > 255 |           buffer.reverse! unless SystemEndian == self
 > 256 |           buffer.to_slice.copy_to(bytes)
 > 257 |           Int64.new(16)
 > 258 |         end
 > 259 | 
 > 260 |         def self.decode(type : Int128.class, io : IO)
 > 261 |           buffer = uninitialized UInt8[16]
 > 262 |           io.read_fully(buffer.to_slice)
 > 263 |           buffer.reverse! unless SystemEndian == self
 > 264 |           buffer.unsafe_as(Int128)
 > 265 |         end
 > 266 | 
 > 267 |         def self.decode(type : Int128.class, bytes : Bytes)
 > 268 |           buffer = uninitialized UInt8[16]
 > 269 |           bytes.to_slice[0, 16].copy_to(buffer.to_slice)
 > 270 |           buffer.reverse! unless SystemEndian == self
 > 271 |           buffer.unsafe_as(Int128)
 > 272 |         end
 > 273 |       
 > 274 |         
 > 275 | 
 > 276 |         def self.encode(int : UInt128, io : IO) : Int64
 > 277 |           buffer = int.unsafe_as(StaticArray(UInt8, 16))
 > 278 |           buffer.reverse! unless SystemEndian == self
 > 279 |           io.write(buffer.to_slice)
 > 280 |           Int64.new(16)
 > 281 |         end
 > 282 | 
 > 283 |         def self.encode(int : UInt128, bytes : Bytes) : Int64
 > 284 |           buffer = int.unsafe_as(StaticArray(UInt8, 16))
 > 285 |           buffer.reverse! unless SystemEndian == self
 > 286 |           buffer.to_slice.copy_to(bytes)
 > 287 |           Int64.new(16)
 > 288 |         end
 > 289 | 
 > 290 |         def self.decode(type : UInt128.class, io : IO)
 > 291 |           buffer = uninitialized UInt8[16]
 > 292 |           io.read_fully(buffer.to_slice)
 > 293 |           buffer.reverse! unless SystemEndian == self
 > 294 |           buffer.unsafe_as(UInt128)
 > 295 |         end
 > 296 | 
 > 297 |         def self.decode(type : UInt128.class, bytes : Bytes)
 > 298 |           buffer = uninitialized UInt8[16]
 > 299 |           bytes.to_slice[0, 16].copy_to(buffer.to_slice)
 > 300 |           buffer.reverse! unless SystemEndian == self
 > 301 |           buffer.unsafe_as(UInt128)
 > 302 |         end
 > 303 |       
 > 304 |     end
 > 305 |   
 > 306 |     module BigEndian
 > 307 |       
 > 308 |         
 > 309 | 
 > 310 |         def self.encode(int : Int8, io : IO) : Int64
 > 311 |           buffer = int.unsafe_as(StaticArray(UInt8, 1))
 > 312 |           buffer.reverse! unless SystemEndian == self
 > 313 |           io.write(buffer.to_slice)
 > 314 |           Int64.new(1)
 > 315 |         end
 > 316 | 
 > 317 |         def self.encode(int : Int8, bytes : Bytes) : Int64
 > 318 |           buffer = int.unsafe_as(StaticArray(UInt8, 1))
 > 319 |           buffer.reverse! unless SystemEndian == self
 > 320 |           buffer.to_slice.copy_to(bytes)
 > 321 |           Int64.new(1)
 > 322 |         end
 > 323 | 
 > 324 |         def self.decode(type : Int8.class, io : IO)
 > 325 |           buffer = uninitialized UInt8[1]
 > 326 |           io.read_fully(buffer.to_slice)
 > 327 |           buffer.reverse! unless SystemEndian == self
 > 328 |           buffer.unsafe_as(Int8)
 > 329 |         end
 > 330 | 
 > 331 |         def self.decode(type : Int8.class, bytes : Bytes)
 > 332 |           buffer = uninitialized UInt8[1]
 > 333 |           bytes.to_slice[0, 1].copy_to(buffer.to_slice)
 > 334 |           buffer.reverse! unless SystemEndian == self
 > 335 |           buffer.unsafe_as(Int8)
 > 336 |         end
 > 337 |       
 > 338 |         
 > 339 | 
 > 340 |         def self.encode(int : UInt8, io : IO) : Int64
 > 341 |           buffer = int.unsafe_as(StaticArray(UInt8, 1))
 > 342 |           buffer.reverse! unless SystemEndian == self
 > 343 |           io.write(buffer.to_slice)
 > 344 |           Int64.new(1)
 > 345 |         end
 > 346 | 
 > 347 |         def self.encode(int : UInt8, bytes : Bytes) : Int64
 > 348 |           buffer = int.unsafe_as(StaticArray(UInt8, 1))
 > 349 |           buffer.reverse! unless SystemEndian == self
 > 350 |           buffer.to_slice.copy_to(bytes)
 > 351 |           Int64.new(1)
 > 352 |         end
 > 353 | 
 > 354 |         def self.decode(type : UInt8.class, io : IO)
 > 355 |           buffer = uninitialized UInt8[1]
 > 356 |           io.read_fully(buffer.to_slice)
 > 357 |           buffer.reverse! unless SystemEndian == self
 > 358 |           buffer.unsafe_as(UInt8)
 > 359 |         end
 > 360 | 
 > 361 |         def self.decode(type : UInt8.class, bytes : Bytes)
 > 362 |           buffer = uninitialized UInt8[1]
 > 363 |           bytes.to_slice[0, 1].copy_to(buffer.to_slice)
 > 364 |           buffer.reverse! unless SystemEndian == self
 > 365 |           buffer.unsafe_as(UInt8)
 > 366 |         end
 > 367 |       
 > 368 |         
 > 369 | 
 > 370 |         def self.encode(int : Int16, io : IO) : Int64
 > 371 |           buffer = int.unsafe_as(StaticArray(UInt8, 2))
 > 372 |           buffer.reverse! unless SystemEndian == self
 > 373 |           io.write(buffer.to_slice)
 > 374 |           Int64.new(2)
 > 375 |         end
 > 376 | 
 > 377 |         def self.encode(int : Int16, bytes : Bytes) : Int64
 > 378 |           buffer = int.unsafe_as(StaticArray(UInt8, 2))
 > 379 |           buffer.reverse! unless SystemEndian == self
 > 380 |           buffer.to_slice.copy_to(bytes)
 > 381 |           Int64.new(2)
 > 382 |         end
 > 383 | 
 > 384 |         def self.decode(type : Int16.class, io : IO)
 > 385 |           buffer = uninitialized UInt8[2]
 > 386 |           io.read_fully(buffer.to_slice)
 > 387 |           buffer.reverse! unless SystemEndian == self
 > 388 |           buffer.unsafe_as(Int16)
 > 389 |         end
 > 390 | 
 > 391 |         def self.decode(type : Int16.class, bytes : Bytes)
 > 392 |           buffer = uninitialized UInt8[2]
 > 393 |           bytes.to_slice[0, 2].copy_to(buffer.to_slice)
 > 394 |           buffer.reverse! unless SystemEndian == self
 > 395 |           buffer.unsafe_as(Int16)
 > 396 |         end
 > 397 |       
 > 398 |         
 > 399 | 
 > 400 |         def self.encode(int : UInt16, io : IO) : Int64
 > 401 |           buffer = int.unsafe_as(StaticArray(UInt8, 2))
 > 402 |           buffer.reverse! unless SystemEndian == self
 > 403 |           io.write(buffer.to_slice)
 > 404 |           Int64.new(2)
 > 405 |         end
 > 406 | 
 > 407 |         def self.encode(int : UInt16, bytes : Bytes) : Int64
 > 408 |           buffer = int.unsafe_as(StaticArray(UInt8, 2))
 > 409 |           buffer.reverse! unless SystemEndian == self
 > 410 |           buffer.to_slice.copy_to(bytes)
 > 411 |           Int64.new(2)
 > 412 |         end
 > 413 | 
 > 414 |         def self.decode(type : UInt16.class, io : IO)
 > 415 |           buffer = uninitialized UInt8[2]
 > 416 |           io.read_fully(buffer.to_slice)
 > 417 |           buffer.reverse! unless SystemEndian == self
 > 418 |           buffer.unsafe_as(UInt16)
 > 419 |         end
 > 420 | 
 > 421 |         def self.decode(type : UInt16.class, bytes : Bytes)
 > 422 |           buffer = uninitialized UInt8[2]
 > 423 |           bytes.to_slice[0, 2].copy_to(buffer.to_slice)
 > 424 |           buffer.reverse! unless SystemEndian == self
 > 425 |           buffer.unsafe_as(UInt16)
 > 426 |         end
 > 427 |       
 > 428 |         
 > 429 | 
 > 430 |         def self.encode(int : Int32, io : IO) : Int64
 > 431 |           buffer = int.unsafe_as(StaticArray(UInt8, 4))
 > 432 |           buffer.reverse! unless SystemEndian == self
 > 433 |           io.write(buffer.to_slice)
 > 434 |           Int64.new(4)
 > 435 |         end
 > 436 | 
 > 437 |         def self.encode(int : Int32, bytes : Bytes) : Int64
 > 438 |           buffer = int.unsafe_as(StaticArray(UInt8, 4))
 > 439 |           buffer.reverse! unless SystemEndian == self
 > 440 |           buffer.to_slice.copy_to(bytes)
 > 441 |           Int64.new(4)
 > 442 |         end
 > 443 | 
 > 444 |         def self.decode(type : Int32.class, io : IO)
 > 445 |           buffer = uninitialized UInt8[4]
 > 446 |           io.read_fully(buffer.to_slice)
 > 447 |           buffer.reverse! unless SystemEndian == self
 > 448 |           buffer.unsafe_as(Int32)
 > 449 |         end
 > 450 | 
 > 451 |         def self.decode(type : Int32.class, bytes : Bytes)
 > 452 |           buffer = uninitialized UInt8[4]
 > 453 |           bytes.to_slice[0, 4].copy_to(buffer.to_slice)
 > 454 |           buffer.reverse! unless SystemEndian == self
 > 455 |           buffer.unsafe_as(Int32)
 > 456 |         end
 > 457 |       
 > 458 |         
 > 459 | 
 > 460 |         def self.encode(int : UInt32, io : IO) : Int64
 > 461 |           buffer = int.unsafe_as(StaticArray(UInt8, 4))
 > 462 |           buffer.reverse! unless SystemEndian == self
 > 463 |           io.write(buffer.to_slice)
 > 464 |           Int64.new(4)
 > 465 |         end
 > 466 | 
 > 467 |         def self.encode(int : UInt32, bytes : Bytes) : Int64
 > 468 |           buffer = int.unsafe_as(StaticArray(UInt8, 4))
 > 469 |           buffer.reverse! unless SystemEndian == self
 > 470 |           buffer.to_slice.copy_to(bytes)
 > 471 |           Int64.new(4)
 > 472 |         end
 > 473 | 
 > 474 |         def self.decode(type : UInt32.class, io : IO)
 > 475 |           buffer = uninitialized UInt8[4]
 > 476 |           io.read_fully(buffer.to_slice)
 > 477 |           buffer.reverse! unless SystemEndian == self
 > 478 |           buffer.unsafe_as(UInt32)
 > 479 |         end
 > 480 | 
 > 481 |         def self.decode(type : UInt32.class, bytes : Bytes)
 > 482 |           buffer = uninitialized UInt8[4]
 > 483 |           bytes.to_slice[0, 4].copy_to(buffer.to_slice)
 > 484 |           buffer.reverse! unless SystemEndian == self
 > 485 |           buffer.unsafe_as(UInt32)
 > 486 |         end
 > 487 |       
 > 488 |         
 > 489 | 
 > 490 |         def self.encode(int : Int64, io : IO) : Int64
 > 491 |           buffer = int.unsafe_as(StaticArray(UInt8, 8))
 > 492 |           buffer.reverse! unless SystemEndian == self
 > 493 |           io.write(buffer.to_slice)
 > 494 |           Int64.new(8)
 > 495 |         end
 > 496 | 
 > 497 |         def self.encode(int : Int64, bytes : Bytes) : Int64
 > 498 |           buffer = int.unsafe_as(StaticArray(UInt8, 8))
 > 499 |           buffer.reverse! unless SystemEndian == self
 > 500 |           buffer.to_slice.copy_to(bytes)
 > 501 |           Int64.new(8)
 > 502 |         end
 > 503 | 
 > 504 |         def self.decode(type : Int64.class, io : IO)
 > 505 |           buffer = uninitialized UInt8[8]
 > 506 |           io.read_fully(buffer.to_slice)
 > 507 |           buffer.reverse! unless SystemEndian == self
 > 508 |           buffer.unsafe_as(Int64)
 > 509 |         end
 > 510 | 
 > 511 |         def self.decode(type : Int64.class, bytes : Bytes)
 > 512 |           buffer = uninitialized UInt8[8]
 > 513 |           bytes.to_slice[0, 8].copy_to(buffer.to_slice)
 > 514 |           buffer.reverse! unless SystemEndian == self
 > 515 |           buffer.unsafe_as(Int64)
 > 516 |         end
 > 517 |       
 > 518 |         
 > 519 | 
 > 520 |         def self.encode(int : UInt64, io : IO) : Int64
 > 521 |           buffer = int.unsafe_as(StaticArray(UInt8, 8))
 > 522 |           buffer.reverse! unless SystemEndian == self
 > 523 |           io.write(buffer.to_slice)
 > 524 |           Int64.new(8)
 > 525 |         end
 > 526 | 
 > 527 |         def self.encode(int : UInt64, bytes : Bytes) : Int64
 > 528 |           buffer = int.unsafe_as(StaticArray(UInt8, 8))
 > 529 |           buffer.reverse! unless SystemEndian == self
 > 530 |           buffer.to_slice.copy_to(bytes)
 > 531 |           Int64.new(8)
 > 532 |         end
 > 533 | 
 > 534 |         def self.decode(type : UInt64.class, io : IO)
 > 535 |           buffer = uninitialized UInt8[8]
 > 536 |           io.read_fully(buffer.to_slice)
 > 537 |           buffer.reverse! unless SystemEndian == self
 > 538 |           buffer.unsafe_as(UInt64)
 > 539 |         end
 > 540 | 
 > 541 |         def self.decode(type : UInt64.class, bytes : Bytes)
 > 542 |           buffer = uninitialized UInt8[8]
 > 543 |           bytes.to_slice[0, 8].copy_to(buffer.to_slice)
 > 544 |           buffer.reverse! unless SystemEndian == self
 > 545 |           buffer.unsafe_as(UInt64)
 > 546 |         end
 > 547 |       
 > 548 |         
 > 549 | 
 > 550 |         def self.encode(int : Int128, io : IO) : Int64
 > 551 |           buffer = int.unsafe_as(StaticArray(UInt8, 16))
 > 552 |           buffer.reverse! unless SystemEndian == self
 > 553 |           io.write(buffer.to_slice)
 > 554 |           Int64.new(16)
 > 555 |         end
 > 556 | 
 > 557 |         def self.encode(int : Int128, bytes : Bytes) : Int64
 > 558 |           buffer = int.unsafe_as(StaticArray(UInt8, 16))
 > 559 |           buffer.reverse! unless SystemEndian == self
 > 560 |           buffer.to_slice.copy_to(bytes)
 > 561 |           Int64.new(16)
 > 562 |         end
 > 563 | 
 > 564 |         def self.decode(type : Int128.class, io : IO)
 > 565 |           buffer = uninitialized UInt8[16]
 > 566 |           io.read_fully(buffer.to_slice)
 > 567 |           buffer.reverse! unless SystemEndian == self
 > 568 |           buffer.unsafe_as(Int128)
 > 569 |         end
 > 570 | 
 > 571 |         def self.decode(type : Int128.class, bytes : Bytes)
 > 572 |           buffer = uninitialized UInt8[16]
 > 573 |           bytes.to_slice[0, 16].copy_to(buffer.to_slice)
 > 574 |           buffer.reverse! unless SystemEndian == self
 > 575 |           buffer.unsafe_as(Int128)
 > 576 |         end
 > 577 |       
 > 578 |         
 > 579 | 
 > 580 |         def self.encode(int : UInt128, io : IO) : Int64
 > 581 |           buffer = int.unsafe_as(StaticArray(UInt8, 16))
 > 582 |           buffer.reverse! unless SystemEndian == self
 > 583 |           io.write(buffer.to_slice)
 > 584 |           Int64.new(16)
 > 585 |         end
 > 586 | 
 > 587 |         def self.encode(int : UInt128, bytes : Bytes) : Int64
 > 588 |           buffer = int.unsafe_as(StaticArray(UInt8, 16))
 > 589 |           buffer.reverse! unless SystemEndian == self
 > 590 |           buffer.to_slice.copy_to(bytes)
 > 591 |           Int64.new(16)
 > 592 |         end
 > 593 | 
 > 594 |         def self.decode(type : UInt128.class, io : IO)
 > 595 |           buffer = uninitialized UInt8[16]
 > 596 |           io.read_fully(buffer.to_slice)
 > 597 |           buffer.reverse! unless SystemEndian == self
 > 598 |           buffer.unsafe_as(UInt128)
 > 599 |         end
 > 600 | 
 > 601 |         def self.decode(type : UInt128.class, bytes : Bytes)
 > 602 |           buffer = uninitialized UInt8[16]
 > 603 |           bytes.to_slice[0, 16].copy_to(buffer.to_slice)
 > 604 |           buffer.reverse! unless SystemEndian == self
 > 605 |           buffer.unsafe_as(UInt128)
 > 606 |         end
 > 607 |       
 > 608 |     end
 > 609 |   
Error: instantiating 'IO+#write(Slice(UInt8))'


In /usr/lib/crystal/openssl/digest/digest_io.cr:51:10

 51 | io.write(slice)
         ^----
Error: instantiating 'IO+#write(Slice(UInt8))'


In /usr/lib/crystal/io/stapled.cr:80:13

 80 | @writer.write(slice)
              ^----
Error: instantiating 'IO+#write(Slice(UInt8))'


In /usr/lib/crystal/io/multi_writer.cr:37:14

 37 | @writers.each { |writer| writer.write(slice) }
               ^---
Error: instantiating 'Array(IO)#each()'


In /usr/lib/crystal/indexable.cr:187:5

 187 | each_index do |i|
       ^---------
Error: instantiating 'each_index()'


In /usr/lib/crystal/indexable.cr:187:5

 187 | each_index do |i|
       ^---------
Error: instantiating 'each_index()'


In /usr/lib/crystal/io/multi_writer.cr:37:14

 37 | @writers.each { |writer| writer.write(slice) }
               ^---
Error: instantiating 'Array(IO)#each()'


In /usr/lib/crystal/io/multi_writer.cr:37:37

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


In /usr/lib/crystal/io/hexdump.cr:35:28

 35 | def write(buf : Bytes) : Int64
                               ^
Error: method must return Int64 but it is returning (Int64 | Nil)

Nil trace:

  /usr/lib/crystal/io/hexdump.cr:36

        return 0i64 if buf.empty?


  /usr/lib/crystal/io/hexdump.cr:38

        @io.write(buf).tap do
                       ^~~

  /usr/lib/crystal/object.cr:186

      def tap
          ^~~

  /usr/lib/crystal/object.cr:187

        yield self


  /usr/lib/crystal/object.cr:188

        self



@z64
Copy link
Contributor

z64 commented Jun 10, 2020

I ran into this too. It happened to us on 0.35, and ended up being because some of our custom IO subtypes did not have the updated IO contract of def write(bytes) : Int64.
I'll try to reduce as well if I can.

@asterite
Copy link
Member

@bararchy Try searching def write( in your codebase, including the log folder. If you find any that doesn't return Int64, that's the problem. The problem is not in the standard library. Code in the wild needs time to be updated to 0.35.0.

@z64
Copy link
Contributor

z64 commented Jun 10, 2020

This is what I was able to come up with:

require "log"

puts(Time.utc)

class Foo < IO
  def read(slice : Bytes)
    raise IO::Error.new("read-only")
  end

  def write(slice : Bytes) : Nil
    Log.info { "test" }
  end
end
# crystal test.cr
Showing last frame. Use --error-trace for full trace.

In usr/share/crystal/src/io/hexdump.cr:35:28

 35 | def write(buf : Bytes) : Int64
                               ^
Error: method must return Int64 but it is returning (Int64 | Nil)
# crystal -v
Crystal 0.35.0-dev [9057e8d80] (2020-06-08)

LLVM: 8.0.0
Default target: x86_64-unknown-linux-musl

Fixing the IO contract in Foo resolves the error.

@bararchy
Copy link
Contributor Author

It's a shame we dont just get the path the the IO which actually has the wrong def in it .

Thanks guys, im on it.

@straight-shoota
Copy link
Member

straight-shoota commented Jun 10, 2020

Yes, I already noticed that with baked_file_system (schovi/baked_file_system#36). The error message is not helping at all to fix this.

Isolated example:

module Io
  abstract def write : Int64
end
 
class Foo
  include Io
  def write : Int64 # Error: method must return Int64 but it is returning Nil
    Bar.new.write
  end
end
 
class Bar
  include Io
  
  def write # The error should be reported here!
    nil
  end
end
 
Foo.new.write

@asterite
Copy link
Member

Well, the abstract def checking is done before the semantic code. The problem is that for some reason warnings are delayed and shown until much later, I don't know why 🤷

@bararchy
Copy link
Contributor Author

Found them 😢

  1. 0.35.0 Fixes stumpycr/stumpy_png#28
  2. crystal-lang/crystal-mysql@99cc05d which wasn't released

@bcardiff bcardiff removed this from the 1.0.0 milestone Jun 10, 2020
@straight-shoota
Copy link
Member

I don't think this should be considered as fixed. The compiler error is completely confusing and needs to be improved. It should point to the location that actually needs attention.

@bcardiff
Copy link
Member

One aspect of the problem is that write is not an abstract method. Without the notion of redefine/override there is no way to express that the protocol/interface should be enforced.

I thought that since abstract method are enforced, then maybe having an abstract IO::Base with abstract write(slice : Bytes) : Int64 could be a way to workaround this and raise a compile-time error in the offending subclasses.

But, that wont do it. The following example unfortunately compiles

abstract class Base
  abstract def m : Int64
end

class A < Base
end

class A
  def m : Int64
    0i64
  end
end

class B < A
  def m
    nil
  end
end

a = A.new || B.new
pp! typeof(a.m) # => (Int64 | Nil)

@waj
Copy link
Member

waj commented Jun 10, 2020

If the original concern of this issue is fixed, let's open another one to improve the error message situation

@straight-shoota
Copy link
Member

@bcardiff But IO#write is already an abstract method:

abstract def write(slice : Bytes) : Int64

That's why this is so weird.

Most use cases will probably be fixed if the abstract method return type is properly enforced on the wrapped method. See my isolated example in #9454 (comment)

@asterite
Copy link
Member

Just to be clear, I already explained what the problem is in a previous comment: #9454 (comment)

@straight-shoota
Copy link
Member

Created a new issue to track the confusing compiler error in #9460

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants