Johannes Müller 08 Jan 2024

Crystal 1.11.0 is released!

We are announcing a new Crystal release with several new features and bug fixes.

Pre-built packages are available on GitHub Releases and our official distribution channels. See crystal-lang.org/install for installation instructions.

Stats

This release includes 178 changes since 1.10.1 by 28 contributors. We thank all the contributors for all the effort put into improving the language! ❤️

Changes

Below we list the most remarkable changes in the language, compiler and stdlib. This is a pretty big release with lots of things going on, so hold tight 🚀

For details, visit the full changelog.

LLVM 18

One of the biggest steps forward is support for upcoming LLVM 18 which allows linking LLVM dynamically on Windows (#14101). Additionally, LLVM 18 now provides everything we need in the upstream C API, removing the need for our wrapper extension llvm_ext. It’s still necessary for older LLVM versions, so we’ll keep it around for a while. But the future tool chain is getting simplified. Read more in #13946.

Thanks @HertzDevil

Compiler Optimization Levels

The compiler gains four distinct optimization levels:

  • -O0: No optimization
  • -O1: Low optimization
  • -O2: Middle optimization
  • -O3: High optimization

Each level activates the respective LLVM RunPasses and CodeGenOptLevel optimizations.

-O3 corresponds to the existing release mode and -O0 corresponds to the default non-release mode. -O0 remains the default and --release is equivalent to -O3 --single-module.

Effectively, this introduces two optimization choices between the previous full or nothing. And it’s now possible to use high optimization without --single-module.

Read more in #13464.

Thanks @kostya

Alignment primitives

The language has two new reflection primitives: alignof and instance_alignof return a type’s memory alignment (#14087). This allows implementing type-aware allocators in native Crystal with properly aligned pointers. They are siblings of sizeof and instance_sizeof and can be used in the same way.

class Foo
  def initialize(@x : Int8, @y : Int64, @z : Int16)
  end
end

Foo.new(1, 2, 3)

instance_alignof(Foo) # => 8

Effect on existing code

The introduction of these primitives makes it impossible to define methods of the same names. So def alignof or def instance_alignof are now invalid syntax. We don’t expect there to be a big impact in practice.

Thanks @HertzDevil

The Link annotation has a new parameter dll for specifying dynamic link libraries on Windows (#14131).

@[Link(dll: "foo.dll")]
lib LibFoo
end

Thanks @HertzDevil

Macro @caller context

Macros now have a reference to their calling context via the special instance variable @caller (#14055).

macro foo
  {{ @caller.line_number }}
end

foo # => 5

Thanks @Blacksmoke16

New collection methods

Enumerable#present? is a direct inversion of #empty? avoiding some quirks with the similar, but not-quite, #any? (#13847).

Thanks @straight-shoota

Enumerable#each_step and Iterable#each_step are direct methods for creating step iterators (#13610).

Thanks @baseballlover723

Enumerable(T)#to_set(& : T -> U) : Set(U) forall U and #to_a(& : T -> U) forall U allow materialising an Enumerable into a pre-defined collection, which gives more flexibility than the standard #to_set and #to_a methods (#12654, #12653).

Thanks @caspiano

Numeric enhancements

BigFloat#** now works for all Int::Primitive arguments and supports the full exponent range for BitInt arguments (#13971, #13881)

Floating point to string conversion in printf uses the Ryu algorithm (#8441).

New methods Float::Primitive.to_hexfloat, .parse_hexfloat, and .parse_hexfloat? allow conversion to and from the hexfloat format (#14027).

More math features:

Thanks @HertzDevil

Enhancements for crystal spec

crystal spec gets two new commands for introspection:

crystal spec --dry-run prints all active specs without actually executing any spec code (#13804).

Thanks @nobodywasishere

crystal spec --list-tags lists all tags defined in the spec suite (#13616).

Thanks @baseballlover723

Enhancements for crystal tool unreachable

The basic implementation of crystal tool unreachable from Crystal 1.10 gets some useful enhancements.

  • The --tallies option prints all methods and the total number of calls. Those with a zero tally are unreachable (#13969).
  • The --check flag exits with a failure status if there is any unreachable code (#13930).
  • Annotations show up in the output (#13927).
  • New output format: CSV (#13926).
  • Paths in the output are relativized, making it more succinct (#13929).

Thanks @straight-shoota

Inherited macros in API docs

Inherited macros are now exposed in the API docs. They had previously been hidden, in contrast to inherited defs (#13810).

Thanks @Blacksmoke16

Text

Misc

  • The capacity of String::Buffer and IO::Memory was unintentionally limited to 1GB. They now support the full range up to Int32::MAX, i.e. 2GB (#13989). Thanks @straight-shoota
  • There was a nasty bug in Number#format which could mess with the integral part. It is now fixed in #14061. Thanks @HertzDevil
  • Vendored shards markd and reply are no longer referenced by paths relative to the compiler source tree. This means they can be local dependencies (i.e. in lib) when using the compiler as a library (#13992). Thanks @nobodywasishere
  • There are two new constants which provide information on the compiler host and target: Crystal::HOST_TRIPLE and TARGET_TRIPLE (#13823). Thanks @HertzDevil

Shards 0.17.4

The bundled shards release was updated to 0.17.4 which brings a couple minor bugfixes. (#14133).

Thanks @straight-shoota

Experimental: ReferenceStorage and .pre_initialize

We’ve started an effort to make it easier to use custom allocation mechanisms in Crystal and decouple allocation from initialization. The main tool is Reference.pre_initialize which performs the rudimentary object initialization, before actually calling #initialize.

Reference.unsafe_construct is a higher level API on top of that. ReferenceStorage represents a static buffer for a reference allocation.

These APIs are experimental and might be subject to change. We expect more features in this direction in future releases. Join the discussion about custom reference allocation at #13481.

NOTE: ReferenceStorage was removed again in 1.11.1 due to compatibility issues with older versions of the standard library (#14207). It will come back with an improved implementation.

Thanks @HertzDevil

Deprecations

  • Splat operators in macro expressions are deprecated. Use .splat instead (#13939)
  • LLVM.start_multithreaded and .stop_multithreaded. They have no effect (#13949)
  • LLVMExtSetCurrentDebugLocation from llvm_ext.cc for LLVM 9+ (#13965)
  • Char::Reader#@end (#13920)

We have been able to do all of this thanks to the continued support of 84codes and every other sponsor. To maintain and increase the development pace, donations and sponsorships are essential. OpenCollective is available for that. Reach out to crystal@manas.tech if you’d like to become a direct sponsor or find other ways to support Crystal. We thank you in advance!