Crystal 0.34.0 has been released!

We’ve written a blog post summarizing the changes in this release: https://crystal-lang.org/2020/04/06/crystal-0.34.0-released.html

The full changelog is here: https://github.com/crystal-lang/crystal/releases/tag/0.34.0

All the usual installation methods still apply: https://crystal-lang.org/install/

Docker images and 64 bits Linux packages are available as usual

The brew formula PR should be submitted shortly… updating stuff locally first :-)

16 Likes

The brew formula PR has just been submitted: https://github.com/Homebrew/homebrew-core/pull/52644

1 Like

Will my debugger changes be added to 0.35 I hope? :)

6 Likes

I recompiled a program without the -Ddisableoverflow flag and it gave all kinds of runtime overflow errors.

So, what do I have to do to get it to compile again to handle the math without overflowing?

Use larger types, the wrapping operators like &+ or &*, or look into the Big* types.

1 Like

In order to get the same behaviour as with -Ddisable_overflow you will have to use wrapping operators (&+ etc.), as announced in the release notes of 0.34.0 and 0.31.0.

Can you supplies examples to show when/where/how to do this.

I have code designed to run strictly within unsigned 64-bit number space.

@jzakiya

If we want to avoid OverflowError exception we must use the &+ method

puts Int64::MAX
puts Int64::MAX &+ 1

Result:

9223372036854775807
-9223372036854775808

If we use the + method it will throw an OverflowError exception:

Unhandled exception: Arithmetic overflow (OverflowError)
  from /eval:3:17 in '__crystal_main'
  from /usr/lib/crystal/crystal/main.cr:106:5 in 'main_user_code'
  from /usr/lib/crystal/crystal/main.cr:92:7 in 'main'
  from /usr/lib/crystal/crystal/main.cr:115:3 in 'main'
  from __libc_start_main
  from _start
  from ???

https://play.crystal-lang.org/#/r/8uay

1 Like

I used &- in the code and it wouldn’t compile.

But most of my code is using u64 numbers, not signed.

Can you show more normal code usage.

I’m getting this error message repeated:

Unhandled exception in spawn: Arithmetic overflow (OverflowError)
  from /home/jzakiya/crystal/share/crystal/src/array.cr:575:29 in '->'
  from /home/jzakiya/crystal/share/crystal/src/fiber.cr:255:3 in 'run'

These are lines 254:255

te = (Time.monotonic - ts).total_seconds.round(6)
puts "setup time = #{te} secs"   # display sieve setup time

Don’t rely in line numbers for this exception because thyy might be wrong.

A safe bet is to share your entire code.

1 Like

It’s the same for unsigned integers:

UInt64::MAX &+ 1 # => 0
UInt64::MAX + 1  # OverflowError

FYI: The brew formula has been merged :tada:

5 Likes

Here’s the whole code.
Compiled with no -Ddisable_overflow flag in 0.34 it starts to bomb with inputs greater than ~10_000_000

It’s strange. On Mac, if I run the code I get the exact location where the error happens:

Unhandled exception in spawn: Arithmetic overflow (OverflowError)
  from foo.cr:193:59 in 'twins_sieve'
  from foo.cr:248:14 in '->'
  from /usr/local/Cellar/crystal/0.34.0/src/fiber.cr:255:3 in 'run'
  from /usr/local/Cellar/crystal/0.34.0/src/fiber.cr:92:34 in '->'

Do you get the same error? Maybe in linux it doesn’t work exactly like that…

In any case, the relevant code is this:

((2_u64 << ((kn - 1) & bmask)) - 1)

The expression on the right side which is being subtracted 1 from ends up being 0_u64. Then 0_u64 - 1 will overflow (actually underflow, but we use the same exception object).

If it’s okay that it should overflow, you can use &- 1 instead of - 1.

Thanks @asterite, that was the problem.

No, the error shown in previous post doesn’t show the source code line.
I guess this is a bug/deficiency of the Linux compiler?

Here’s the fix that runs correctly compiled with 0.34 on my Linux distro.

seg[(kn-1) >> s] |= ~((2_u64 << ((kn-1) & bmask)) &- 1)
                                                  ^^

I’m going to see if I can find equivalent code without overflow issues.

Please provide better description/documentation of this new “feature” that’s easier for users to understand. You can even use this code snippet if you want to explicitly show code susceptible to overflow (it errors after kn gets too large), and then how to fix it.

1 Like

FYI, I recompiled the different (now working) versions of my code with 0.34 and compared to 0.33, and the results are:

  • the 0.34 versions are consistently slower than for 0.33
  • the 0.34 (stripped) binaries are 20k - 30k bytes larger

So for me, for this problem, 0.34 is a regression vs 0.33, and offers no benefits.

Unfortunately, the change in the overflow handling is significantly slower.

Are you comparing 0.33.0 with -Ddisable_overflow against 0.34.0? That’s likely influenced by other changes between 0.33.0 and 0.34.0. You need to compare default 0.33.0 (without -Ddisable_overflow.) against 0.34.0.

The overflow feature was not introduced in 0.34.0 but 0.31.0. So it’s not a “new” feature. We just stopped supporting the legacy behaviour. Since 0.31.0 your code required the disable flag to retain the old operator semantics. It was announced this flag would just be temporary feature to ease the porting process.

When you replace all operators with the wrapping variant (&+) you should technically end up with essentially the same compiled code as before 0.31.0. So 1 &+ 1 in 0.31.0+ is equivalent to 1 + 1 in 0.30.0 (and 0.31.0 - 0.33.0 with -Ddisable_overflow).

1 Like

When I compile with 0.34 with or w/o -Ddisable_overflow flag it produces the same (larger) binary vs 0.33, and is still slower.

Please compile/run the code on your systems and see for yourselves.

-Ddisable_overflow is ignored in 0.34.0 I think. If you want the old speed (with unsafe operatorion–are those overflows it reported expected?) you could use &+ etc. everywhere. It should be as fast.

In terms of size you could iterate over the commits between 0.33.0 and 0.34.0 and see which ones seemed to add it and debate their merits.
Cheers!

OK, the following are equivalent code.

1) will overflow
seg[(kn - 1) >> s] |= ~((2_u64 << ((kn - 1) & bmask)) &- 1)

2) won't overlfow
seg[(kn - 1) >> s] |= 0xffffffffffffffff << (((kn - 1) & bmask) + 1)

Both 1) and 2) will compile/run with 0.34 without -Ddisable_overflow.

For 0.33, 1) needs -Ddisable_overflow, 2) doesn’t, but runs either way.
However, compiling 0.33 w/o -Ddisable_overflow the (stripped) binary is 20KB larger than with it.

Please run on your systems to see these size/performance differences.

Clarification:
I meant the original code for 1) w/o &- needs flag for 0.33.