# Changelog 1.14 ## [1.14.1] (2025-01-08) [1.14.1]: https://github.com/crystal-lang/crystal/releases/1.14.1 ### Bugfixes #### tools - _(formatter)_ Handle trailing comma with multiple parameters on the same line ([#15097], thanks @Blacksmoke16) [#15097]: https://github.com/crystal-lang/crystal/pull/15097 ### Infrastructure - Changelog for 1.14.1 ([#15323], thanks @straight-shoota) - _(ci)_ Update XCode 15.3.0 in circleci ([#15327], thanks @straight-shoota) [#15323]: https://github.com/crystal-lang/crystal/pull/15323 [#15327]: https://github.com/crystal-lang/crystal/pull/15327 ## [1.14.0] (2024-10-09) [1.14.0]: https://github.com/crystal-lang/crystal/releases/1.14.0 ### Features #### lang - Allow `^` in constant numeric expressions ([#14951], thanks @HertzDevil) [#14951]: https://github.com/crystal-lang/crystal/pull/14951 #### stdlib - Add support for Windows on aarch64 ([#14911], thanks @HertzDevil) - _(collection)_ **[breaking]** Add support for negative start index in `Slice#[start, count]` ([#14778], thanks @ysbaddaden) - _(collection)_ Add `Slice#same?` ([#14728], thanks @straight-shoota) - _(concurrency)_ Add `WaitGroup.wait` and `WaitGroup#spawn` ([#14837], thanks @jgaskins) - _(concurrency)_ Open non-blocking regular files as overlapped on Windows ([#14921], thanks @HertzDevil) - _(concurrency)_ Support non-blocking `File#read` and `#write` on Windows ([#14940], thanks @HertzDevil) - _(concurrency)_ Support non-blocking `File#read_at` on Windows ([#14958], thanks @HertzDevil) - _(concurrency)_ Support non-blocking `Process.run` standard streams on Windows ([#14941], thanks @HertzDevil) - _(concurrency)_ Support `IO::FileDescriptor#flock_*` on non-blocking files on Windows ([#14943], thanks @HertzDevil) - _(concurrency)_ Emulate non-blocking `STDIN` console on Windows ([#14947], thanks @HertzDevil) - _(concurrency)_ Async DNS resolution on Windows ([#14979], thanks @HertzDevil) - _(crypto)_ Update `LibCrypto` bindings for LibreSSL 3.5+ ([#14872], thanks @straight-shoota) - _(llvm)_ Expose LLVM instruction builder for `neg` and `fneg` ([#14774], thanks @JarnaChao09) - _(llvm)_ **[experimental]** Add minimal LLVM OrcV2 bindings ([#14887], thanks @HertzDevil) - _(llvm)_ Add `LLVM::Builder#finalize` ([#14892], thanks @JarnaChao09) - _(llvm)_ Support LLVM 19.1 ([#14842], thanks @HertzDevil) - _(macros)_ Add `Crystal::Macros::TypeNode#has_inner_pointers?` ([#14847], thanks @HertzDevil) - _(macros)_ Add `HashLiteral#has_key?` and `NamedTupleLiteral#has_key?` ([#14890], thanks @kamil-gwozdz) - _(numeric)_ Implement floating-point manipulation functions for `BigFloat` ([#11007], thanks @HertzDevil) - _(runtime)_ Stop & start the world (undocumented API) ([#14729], thanks @ysbaddaden) - _(runtime)_ Add `Pointer::Appender#to_slice` ([#14874], thanks @straight-shoota) - _(serialization)_ Add `URI.from_json_object_key?` and `URI#to_json_object_key` ([#14834], thanks @nobodywasishere) - _(serialization)_ Add `URI::Params::Serializable` ([#14684], thanks @Blacksmoke16) - _(system)_ Enable full backtrace for exception in process spawn ([#14796], thanks @straight-shoota) - _(system)_ Implement `System::User` on Windows ([#14933], thanks @HertzDevil) - _(system)_ Implement `System::Group` on Windows ([#14945], thanks @HertzDevil) - _(system)_ Add methods to `Crystal::EventLoop` ([#14977], thanks @ysbaddaden) - _(text)_ Add `underscore_to_space` option to `String#titleize` ([#14822], thanks @Blacksmoke16) - _(text)_ Support Unicode 16.0.0 ([#14997], thanks @HertzDevil) [#14911]: https://github.com/crystal-lang/crystal/pull/14911 [#14778]: https://github.com/crystal-lang/crystal/pull/14778 [#14728]: https://github.com/crystal-lang/crystal/pull/14728 [#14837]: https://github.com/crystal-lang/crystal/pull/14837 [#14921]: https://github.com/crystal-lang/crystal/pull/14921 [#14940]: https://github.com/crystal-lang/crystal/pull/14940 [#14958]: https://github.com/crystal-lang/crystal/pull/14958 [#14941]: https://github.com/crystal-lang/crystal/pull/14941 [#14943]: https://github.com/crystal-lang/crystal/pull/14943 [#14947]: https://github.com/crystal-lang/crystal/pull/14947 [#14979]: https://github.com/crystal-lang/crystal/pull/14979 [#14872]: https://github.com/crystal-lang/crystal/pull/14872 [#14774]: https://github.com/crystal-lang/crystal/pull/14774 [#14887]: https://github.com/crystal-lang/crystal/pull/14887 [#14892]: https://github.com/crystal-lang/crystal/pull/14892 [#14842]: https://github.com/crystal-lang/crystal/pull/14842 [#14847]: https://github.com/crystal-lang/crystal/pull/14847 [#14890]: https://github.com/crystal-lang/crystal/pull/14890 [#11007]: https://github.com/crystal-lang/crystal/pull/11007 [#14729]: https://github.com/crystal-lang/crystal/pull/14729 [#14874]: https://github.com/crystal-lang/crystal/pull/14874 [#14834]: https://github.com/crystal-lang/crystal/pull/14834 [#14684]: https://github.com/crystal-lang/crystal/pull/14684 [#14796]: https://github.com/crystal-lang/crystal/pull/14796 [#14933]: https://github.com/crystal-lang/crystal/pull/14933 [#14945]: https://github.com/crystal-lang/crystal/pull/14945 [#14977]: https://github.com/crystal-lang/crystal/pull/14977 [#14822]: https://github.com/crystal-lang/crystal/pull/14822 [#14997]: https://github.com/crystal-lang/crystal/pull/14997 #### compiler - _(cli)_ Adds initial support for external commands ([#14953], thanks @bcardiff) - _(interpreter)_ Add `Crystal::Repl::Value#runtime_type` ([#14156], thanks @bcardiff) - _(interpreter)_ Implement `Reference.pre_initialize` in the interpreter ([#14968], thanks @HertzDevil) - _(interpreter)_ Enable the interpreter on Windows ([#14964], thanks @HertzDevil) [#14953]: https://github.com/crystal-lang/crystal/pull/14953 [#14156]: https://github.com/crystal-lang/crystal/pull/14156 [#14968]: https://github.com/crystal-lang/crystal/pull/14968 [#14964]: https://github.com/crystal-lang/crystal/pull/14964 ### Bugfixes #### lang - Fix `Slice.literal` for multiple calls with identical signature ([#15009], thanks @HertzDevil) - _(macros)_ Add location info to some `MacroIf` nodes ([#14885], thanks @Blacksmoke16) [#15009]: https://github.com/crystal-lang/crystal/pull/15009 [#14885]: https://github.com/crystal-lang/crystal/pull/14885 #### stdlib - _(collection)_ Fix `Range#size` return type to `Int32` ([#14588], thanks @straight-shoota) - _(concurrency)_ Update `DeallocationStack` for Windows context switch ([#15032], thanks @HertzDevil) - _(concurrency)_ Fix race condition in `pthread_create` handle initialization ([#15043], thanks @HertzDevil) - _(files)_ **[regression]** Fix `File#truncate` and `#lock` for Win32 append-mode files ([#14706], thanks @HertzDevil) - _(files)_ **[breaking]** Avoid flush in finalizers for `Socket` and `IO::FileDescriptor` ([#14882], thanks @straight-shoota) - _(files)_ Make `IO::Buffered#buffer_size=` idempotent ([#14855], thanks @jgaskins) - _(macros)_ Implement `#sort_by` inside macros using `Enumerable#sort_by` ([#14895], thanks @HertzDevil) - _(macros)_ Fix internal error when calling `#is_a?` on `External` nodes ([#14918], thanks @HertzDevil) - _(networking)_ Use correct timeout for `Socket#connect` on Windows ([#14961], thanks @HertzDevil) - _(numeric)_ Fix handle empty string in `String#to_f(whitespace: false)` ([#14902], thanks @Blacksmoke16) - _(numeric)_ Fix exponent wrapping in `Math.frexp(BigFloat)` for very large values ([#14971], thanks @HertzDevil) - _(numeric)_ Fix exponent overflow in `BigFloat#to_s` for very large values ([#14982], thanks @HertzDevil) - _(numeric)_ Add missing `@[Link(dll:)]` annotation to MPIR ([#15003], thanks @HertzDevil) - _(runtime)_ Add missing return type of `LibC.VirtualQuery` ([#15036], thanks @HertzDevil) - _(runtime)_ Fix main stack top detection on musl-libc ([#15047], thanks @HertzDevil) - _(serialization)_ **[breaking]** Remove `XML::Error.errors` ([#14936], thanks @straight-shoota) - _(specs)_ **[regression]** Fix `Expectations::Be` for module type ([#14926], thanks @straight-shoota) - _(system)_ Fix return type restriction for `ENV.fetch` ([#14919], thanks @straight-shoota) - _(system)_ `#file_descriptor_close` should set `@closed` (UNIX) ([#14973], thanks @ysbaddaden) - _(system)_ reinit event loop first after fork (UNIX) ([#14975], thanks @ysbaddaden) - _(text)_ Fix avoid linking `libpcre` when unused ([#14891], thanks @kojix2) - _(text)_ Add type restriction to `String#byte_index` `offset` parameter ([#14981], thanks @straight-shoota) [#14588]: https://github.com/crystal-lang/crystal/pull/14588 [#15032]: https://github.com/crystal-lang/crystal/pull/15032 [#15043]: https://github.com/crystal-lang/crystal/pull/15043 [#14706]: https://github.com/crystal-lang/crystal/pull/14706 [#14882]: https://github.com/crystal-lang/crystal/pull/14882 [#14855]: https://github.com/crystal-lang/crystal/pull/14855 [#14895]: https://github.com/crystal-lang/crystal/pull/14895 [#14918]: https://github.com/crystal-lang/crystal/pull/14918 [#14961]: https://github.com/crystal-lang/crystal/pull/14961 [#14902]: https://github.com/crystal-lang/crystal/pull/14902 [#14971]: https://github.com/crystal-lang/crystal/pull/14971 [#14982]: https://github.com/crystal-lang/crystal/pull/14982 [#15003]: https://github.com/crystal-lang/crystal/pull/15003 [#15036]: https://github.com/crystal-lang/crystal/pull/15036 [#15047]: https://github.com/crystal-lang/crystal/pull/15047 [#14936]: https://github.com/crystal-lang/crystal/pull/14936 [#14926]: https://github.com/crystal-lang/crystal/pull/14926 [#14919]: https://github.com/crystal-lang/crystal/pull/14919 [#14973]: https://github.com/crystal-lang/crystal/pull/14973 [#14975]: https://github.com/crystal-lang/crystal/pull/14975 [#14891]: https://github.com/crystal-lang/crystal/pull/14891 [#14981]: https://github.com/crystal-lang/crystal/pull/14981 #### compiler - _(cli)_ Add error handling for linker flag sub commands ([#14932], thanks @straight-shoota) - _(codegen)_ Allow returning `Proc`s from top-level funs ([#14917], thanks @HertzDevil) - _(codegen)_ Fix CRT static-dynamic linking conflict in specs with C sources ([#14970], thanks @HertzDevil) - _(interpreter)_ Fix Linux `getrandom` failure in interpreted code ([#15035], thanks @HertzDevil) - _(interpreter)_ Fix undefined behavior in interpreter mixed union upcast ([#15042], thanks @HertzDevil) - _(semantic)_ Fix `TopLevelVisitor` adding existing `ClassDef` type to current scope ([#15067], thanks @straight-shoota) [#14932]: https://github.com/crystal-lang/crystal/pull/14932 [#14917]: https://github.com/crystal-lang/crystal/pull/14917 [#14970]: https://github.com/crystal-lang/crystal/pull/14970 [#15035]: https://github.com/crystal-lang/crystal/pull/15035 [#15042]: https://github.com/crystal-lang/crystal/pull/15042 [#15067]: https://github.com/crystal-lang/crystal/pull/15067 #### tools - _(dependencies)_ Fix `crystal tool dependencies` format flat ([#14927], thanks @straight-shoota) - _(dependencies)_ Fix `crystal tool dependencies` filters for Windows paths ([#14928], thanks @straight-shoota) - _(docs-generator)_ Fix doc comment above annotation with macro expansion ([#14849], thanks @Blacksmoke16) - _(unreachable)_ Fix `crystal tool unreachable` & co visiting circular hierarchies ([#15065], thanks @straight-shoota) [#14927]: https://github.com/crystal-lang/crystal/pull/14927 [#14928]: https://github.com/crystal-lang/crystal/pull/14928 [#14849]: https://github.com/crystal-lang/crystal/pull/14849 [#15065]: https://github.com/crystal-lang/crystal/pull/15065 ### Chores #### stdlib - **[deprecation]** Use `Time::Span` in `Benchmark.ips` ([#14805], thanks @HertzDevil) - **[deprecation]** Deprecate `::sleep(Number)` ([#14962], thanks @HertzDevil) - _(runtime)_ **[deprecation]** Deprecate `Pointer.new(Int)` ([#14875], thanks @straight-shoota) [#14805]: https://github.com/crystal-lang/crystal/pull/14805 [#14962]: https://github.com/crystal-lang/crystal/pull/14962 [#14875]: https://github.com/crystal-lang/crystal/pull/14875 #### compiler - _(interpreter)_ Remove TODO in `Crystal::Loader` on Windows ([#14988], thanks @HertzDevil) - _(interpreter:repl)_ Update REPLy version ([#14950], thanks @HertzDevil) [#14988]: https://github.com/crystal-lang/crystal/pull/14988 [#14950]: https://github.com/crystal-lang/crystal/pull/14950 ### Performance #### stdlib - _(collection)_ Always use unstable sort for simple types ([#14825], thanks @HertzDevil) - _(collection)_ Optimize `Hash#transform_{keys,values}` ([#14502], thanks @jgaskins) - _(numeric)_ Optimize arithmetic between `BigFloat` and integers ([#14944], thanks @HertzDevil) - _(runtime)_ **[regression]** Cache `Exception::CallStack.empty` to avoid repeat `Array` allocation ([#15025], thanks @straight-shoota) [#14825]: https://github.com/crystal-lang/crystal/pull/14825 [#14502]: https://github.com/crystal-lang/crystal/pull/14502 [#14944]: https://github.com/crystal-lang/crystal/pull/14944 [#15025]: https://github.com/crystal-lang/crystal/pull/15025 #### compiler - Avoid unwinding the stack on hot path in method call lookups ([#15002], thanks @ggiraldez) - _(codegen)_ Reduce calls to `Crystal::Type#remove_indirection` in module dispatch ([#14992], thanks @HertzDevil) - _(codegen)_ Compiler: enable parallel codegen with MT ([#14748], thanks @ysbaddaden) [#15002]: https://github.com/crystal-lang/crystal/pull/15002 [#14992]: https://github.com/crystal-lang/crystal/pull/14992 [#14748]: https://github.com/crystal-lang/crystal/pull/14748 ### Refactor #### stdlib - _(concurrency)_ Extract `select` from `src/channel.cr` ([#14912], thanks @straight-shoota) - _(concurrency)_ Make `Crystal::IOCP::OverlappedOperation` abstract ([#14987], thanks @HertzDevil) - _(files)_ Move `#evented_read`, `#evented_write` into `Crystal::LibEvent::EventLoop` ([#14883], thanks @straight-shoota) - _(networking)_ Simplify `Socket::Addrinfo.getaddrinfo(&)` ([#14956], thanks @HertzDevil) - _(networking)_ Add `Crystal::System::Addrinfo` ([#14957], thanks @HertzDevil) - _(runtime)_ Add `Exception::CallStack.empty` ([#15017], thanks @straight-shoota) - _(system)_ Refactor cancellation of `IOCP::OverlappedOperation` ([#14754], thanks @straight-shoota) - _(system)_ Include `Crystal::System::Group` instead of extending it ([#14930], thanks @HertzDevil) - _(system)_ Include `Crystal::System::User` instead of extending it ([#14929], thanks @HertzDevil) - _(system)_ Fix: `Crystal::SpinLock` doesn't need to be allocated on the HEAP ([#14972], thanks @ysbaddaden) - _(system)_ Don't involve evloop after fork in `System::Process.spawn` (UNIX) ([#14974], thanks @ysbaddaden) - _(system)_ Refactor `EventLoop` interface for sleeps & select timeouts ([#14980], thanks @ysbaddaden) [#14912]: https://github.com/crystal-lang/crystal/pull/14912 [#14987]: https://github.com/crystal-lang/crystal/pull/14987 [#14883]: https://github.com/crystal-lang/crystal/pull/14883 [#14956]: https://github.com/crystal-lang/crystal/pull/14956 [#14957]: https://github.com/crystal-lang/crystal/pull/14957 [#15017]: https://github.com/crystal-lang/crystal/pull/15017 [#14754]: https://github.com/crystal-lang/crystal/pull/14754 [#14930]: https://github.com/crystal-lang/crystal/pull/14930 [#14929]: https://github.com/crystal-lang/crystal/pull/14929 [#14972]: https://github.com/crystal-lang/crystal/pull/14972 [#14974]: https://github.com/crystal-lang/crystal/pull/14974 [#14980]: https://github.com/crystal-lang/crystal/pull/14980 #### compiler - _(codegen)_ Compiler: refactor codegen ([#14760], thanks @ysbaddaden) - _(interpreter)_ Refactor interpreter stack code to avoid duplicate macro expansion ([#14876], thanks @straight-shoota) [#14760]: https://github.com/crystal-lang/crystal/pull/14760 [#14876]: https://github.com/crystal-lang/crystal/pull/14876 ### Documentation #### stdlib - _(collection)_ **[breaking]** Hide `Hash::Entry` from public API docs ([#14881], thanks @Blacksmoke16) - _(collection)_ Fix typos in docs for `Set` and `Hash` ([#14889], thanks @philipp-classen) - _(llvm)_ Add `@[Experimental]` to `LLVM::DIBuilder` ([#14854], thanks @HertzDevil) - _(networking)_ Add documentation about synchronous DNS resolution ([#15027], thanks @straight-shoota) - _(networking)_ Add `uri/json` to `docs_main` ([#15069], thanks @straight-shoota) - _(runtime)_ Add docs about `Pointer`'s alignment requirement ([#14853], thanks @HertzDevil) - _(runtime)_ Reword `Pointer#memcmp`'s documentation ([#14818], thanks @HertzDevil) - _(runtime)_ Add documentation for `NoReturn` and `Void` ([#14817], thanks @nobodywasishere) [#14881]: https://github.com/crystal-lang/crystal/pull/14881 [#14889]: https://github.com/crystal-lang/crystal/pull/14889 [#14854]: https://github.com/crystal-lang/crystal/pull/14854 [#15027]: https://github.com/crystal-lang/crystal/pull/15027 [#15069]: https://github.com/crystal-lang/crystal/pull/15069 [#14853]: https://github.com/crystal-lang/crystal/pull/14853 [#14818]: https://github.com/crystal-lang/crystal/pull/14818 [#14817]: https://github.com/crystal-lang/crystal/pull/14817 ### Specs #### stdlib - Remove some uses of deprecated overloads in standard library specs ([#14963], thanks @HertzDevil) - _(collection)_ Disable `Tuple#to_static_array` spec on AArch64 ([#14844], thanks @HertzDevil) - _(serialization)_ Add JSON parsing UTF-8 spec ([#14823], thanks @Blacksmoke16) - _(text)_ Add specs for `String#index`, `#rindex` search for `Char::REPLACEMENT` ([#14946], thanks @straight-shoota) [#14963]: https://github.com/crystal-lang/crystal/pull/14963 [#14844]: https://github.com/crystal-lang/crystal/pull/14844 [#14823]: https://github.com/crystal-lang/crystal/pull/14823 [#14946]: https://github.com/crystal-lang/crystal/pull/14946 #### compiler - _(codegen)_ Support return types in codegen specs ([#14888], thanks @HertzDevil) - _(codegen)_ Fix codegen spec for `ProcPointer` of virtual type ([#14903], thanks @HertzDevil) - _(codegen)_ Support LLVM OrcV2 codegen specs ([#14886], thanks @HertzDevil) - _(codegen)_ Don't spawn subprocess if codegen spec uses flags but not the prelude ([#14904], thanks @HertzDevil) [#14888]: https://github.com/crystal-lang/crystal/pull/14888 [#14903]: https://github.com/crystal-lang/crystal/pull/14903 [#14886]: https://github.com/crystal-lang/crystal/pull/14886 [#14904]: https://github.com/crystal-lang/crystal/pull/14904 ### Infrastructure - Changelog for 1.14.0 ([#14969], thanks @straight-shoota) - Update previous Crystal release 1.13.1 ([#14810], thanks @straight-shoota) - Refactor GitHub changelog generator print special infra ([#14795], thanks @straight-shoota) - Update distribution-scripts ([#14877], thanks @straight-shoota) - Update version in `shard.yml` ([#14909], thanks @straight-shoota) - Merge `release/1.13`@1.13.2 ([#14924], thanks @straight-shoota) - Update previous Crystal release 1.13.2 ([#14925], thanks @straight-shoota) - Merge `release/1.13`@1.13.3 ([#15012], thanks @straight-shoota) - Update previous Crystal release 1.13.3 ([#15016], thanks @straight-shoota) - **[regression]** Fix `SOURCE_DATE_EPOCH` in `Makefile.win` ([#14922], thanks @HertzDevil) - _(ci)_ Update actions/checkout action to v4 - autoclosed ([#14896], thanks @renovate) - _(ci)_ Update LLVM 18 for `wasm32-test` ([#14821], thanks @straight-shoota) - _(ci)_ Pin package repos for OpenSSL packages ([#14831], thanks @straight-shoota) - _(ci)_ Upgrade XCode 15.4.0 ([#14794], thanks @straight-shoota) - _(ci)_ Update GH Actions ([#14535], thanks @renovate) - _(ci)_ Add test for OpenSSL 3.3 ([#14873], thanks @straight-shoota) - _(ci)_ Update GitHub runner to `macos-14` ([#14833], thanks @straight-shoota) - _(ci)_ Refactor SSL workflow with job matrix ([#14899], thanks @straight-shoota) - _(ci)_ Drop the non-release Windows compiler artifact ([#15000], thanks @HertzDevil) - _(ci)_ Fix compiler artifact name in WindowsCI ([#15021], thanks @straight-shoota) - _(ci)_ Restrict CI runners from runs-on to 8GB ([#15030], thanks @straight-shoota) - _(ci)_ Increase memory for stdlib CI runners from runs-on to 16GB ([#15044], thanks @straight-shoota) - _(ci)_ Use Cygwin to build libiconv on Windows CI ([#14999], thanks @HertzDevil) - _(ci)_ Use our own `libffi` repository on Windows CI ([#14998], thanks @HertzDevil) [#14969]: https://github.com/crystal-lang/crystal/pull/14969 [#14810]: https://github.com/crystal-lang/crystal/pull/14810 [#14795]: https://github.com/crystal-lang/crystal/pull/14795 [#14877]: https://github.com/crystal-lang/crystal/pull/14877 [#14909]: https://github.com/crystal-lang/crystal/pull/14909 [#14924]: https://github.com/crystal-lang/crystal/pull/14924 [#14925]: https://github.com/crystal-lang/crystal/pull/14925 [#15012]: https://github.com/crystal-lang/crystal/pull/15012 [#15016]: https://github.com/crystal-lang/crystal/pull/15016 [#14922]: https://github.com/crystal-lang/crystal/pull/14922 [#14896]: https://github.com/crystal-lang/crystal/pull/14896 [#14821]: https://github.com/crystal-lang/crystal/pull/14821 [#14831]: https://github.com/crystal-lang/crystal/pull/14831 [#14794]: https://github.com/crystal-lang/crystal/pull/14794 [#14535]: https://github.com/crystal-lang/crystal/pull/14535 [#14873]: https://github.com/crystal-lang/crystal/pull/14873 [#14833]: https://github.com/crystal-lang/crystal/pull/14833 [#14899]: https://github.com/crystal-lang/crystal/pull/14899 [#15000]: https://github.com/crystal-lang/crystal/pull/15000 [#15021]: https://github.com/crystal-lang/crystal/pull/15021 [#15030]: https://github.com/crystal-lang/crystal/pull/15030 [#15044]: https://github.com/crystal-lang/crystal/pull/15044 [#14999]: https://github.com/crystal-lang/crystal/pull/14999 [#14998]: https://github.com/crystal-lang/crystal/pull/14998