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

Check for overflow when extending signed integers to unsigned integers #10000

Merged
merged 1 commit into from
Dec 6, 2020

Conversation

HertzDevil
Copy link
Contributor

Fixes #9997. Code like -1_i8.to_u16 will raise after this PR.

Crystal relies on wrapping conversions from Int32 to UInt64 in a few places, but incorrectly used the safe variants:

  • Pointer.new(address : Int): Since pointers are already fairly unsafe, this change should be fine. Without this, Crystal would complain about lib constants like LibC::MAP_FAILED = Pointer(Void).new(-1); the alternative fix is to call .to_u64! on those -1 instead.
  • Random::PCG32#jump(delta): From the original source code: Even though delta is an unsigned integer, we can pass a signed integer to go backwards, it just goes "the long way round". Thus wrapping conversion is indeed correct here.
  • A spec in spec/compiler/codegen/tuple_spec.cr: The same spec uses &+ instead of +, so .to_u64! is probably the right thing to do already even without this PR.

@straight-shoota straight-shoota added kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:stdlib:numeric labels Nov 30, 2020
if checked
if from_type.signed? && to_type.unsigned?
overflow = codegen_out_of_range(to_type, from_type, arg)
codegen_raise_overflow_cond(overflow)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somehow I feel the code below this shouldn't execute... 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't follow the comment @asterite. What do you mean?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two lines below this there's more code and it will be generated, but it shouldn't. This line is missing a return.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, maybe not? I thought the check would always raise... nevermind

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The codegen will emit a raise block and leave the builder in place to continue the normal path which in this case is the extend_int. But this method itself won't raise. It's generated code will.

@@ -211,6 +211,23 @@ describe "Code gen: arithmetic primitives" do
end
{% end %}

{% if [UInt16, UInt32, UInt64].includes?(type) %}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there a reason to exclude UInt8 and UInt128 from this list?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UInt8 is because the issue isn't concerned with same-rank conversions. UInt128 is because I'm not quite clear how much support for 128-bit integers is actually there (especially for CI).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. It bugs me slightly that we are not covering all the cases despite the implementation.

I would express that any unsigned int should raise on -1_{{type}} for all signed types. For this kind of specs I started spec/support/number.cr.

Let us know if you want to tweak these specs further if this makes sense to you. Otherwise it might come in another PR at some point.

if checked
if from_type.signed? && to_type.unsigned?
overflow = codegen_out_of_range(to_type, from_type, arg)
codegen_raise_overflow_cond(overflow)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't follow the comment @asterite. What do you mean?

@bcardiff bcardiff added this to the 1.0.0 milestone Dec 1, 2020
@straight-shoota straight-shoota merged commit 9f190bc into crystal-lang:master Dec 6, 2020
@HertzDevil HertzDevil deleted the bug/sext-overflow branch December 7, 2020 02:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:stdlib:numeric
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Overflow not triggered when converting signed integers into larger unsigned integers
4 participants