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 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
Any code to reproduce this?
@asterite seems that "#{Time.utc.to_rfc3339}"
does that for me :)
@bararchy That works fine for me, no warnings
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 it's being called from
formatter = ::Log::Formatter.new do |entry, io|
io << "[#{Time.utc.to_rfc3339}] [#{entry.severity}] #{entry.message}"
end
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
?
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?
Maybe also record what you are doing so we can see what's going on? Print crystal --version
and so on. Platform too.
@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
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.
@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.
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)
md5-7ba0ddf681f02ac1b4935fecc54c2e6f
# 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.
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.
Yes, I already noticed that with baked_file_system
(https://github.com/schovi/baked_file_system/pull/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
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 馃し
Found them :cry:
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.
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)
If the original concern of this issue is fixed, let's open another one to improve the error message situation
@bcardiff But IO#write
is already an abstract method:
https://github.com/crystal-lang/crystal/blob/d08b646e1c98efae75e9dd4684aae2fa2ca34234/src/io.cr#L103
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 https://github.com/crystal-lang/crystal/issues/9454#issuecomment-641919948
Just to be clear, I already explained what the problem is in a previous comment: https://github.com/crystal-lang/crystal/issues/9454#issuecomment-641922402
Created a new issue to track the confusing compiler error in #9460
Most helpful comment
If the original concern of this issue is fixed, let's open another one to improve the error message situation