-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Fix Double-to-Long overflow check #73016
Conversation
@MichaelJLiu Could you please open an issue and link this PR to it? Also, please include information whether the issue is present in the legacy (non-Roslyn) VB compiler. It is a part of the .NET Framework for Desktop and, usually, can be found as |
@AlekseyTs I've opened issue #73032 as requested, but I don't have permission to link it to this PR. |
You already did by mentioning the issue by its number in your comment. |
@@ -605,7 +605,8 @@ End Class | |||
New TypeAndValue(uint64Type, CULng(18)), | |||
New TypeAndValue(decimalType, CDec(-11.3)), | |||
New TypeAndValue(doubleType, CDbl(&HF000000000000000UL)), | |||
New TypeAndValue(doubleType, CDbl(&H70000000000000F0L)), | |||
New TypeAndValue(doubleType, CDbl(&H8000000000000000L)), | |||
New TypeAndValue(doubleType, CDbl(&H7FFFFFFFFFFFFC00L)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should these cases be added to ConstantExpressionConversions2
as well, at line 1169?
@MichaelJLiu, thanks for the detailed description.
For reference, the corresponding C# code succeeds (see sharplab.io): const long w = (long)9.0E+18; // ok
const long x = 0x7000000000000400L;
const double y = x;
const long z = (long)y; // ok |
@@ -605,7 +605,8 @@ End Class | |||
New TypeAndValue(uint64Type, CULng(18)), | |||
New TypeAndValue(decimalType, CDec(-11.3)), | |||
New TypeAndValue(doubleType, CDbl(&HF000000000000000UL)), | |||
New TypeAndValue(doubleType, CDbl(&H70000000000000F0L)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we should also have a separate specific test for #73032, that compiles and runs the code from that issue and checks the expected values: Console.WriteLine(CType(9.0E+18, Long))
Console.WriteLine(CType(CDbl(&H7000000000000400L), Long))
Console.WriteLine(CType(CDbl(&H7FFFFFFFFFFFFC00L), Long)) If so, please include a Refers to: src/Compilers/VisualBasic/Test/Semantic/Semantics/Conversions.vb:614 in e8946b3. [](commit_id = e8946b3, deletion_comment = False) |
Thanks @MichaelJLiu. Please avoid using force-push while the PR is being reviewed since it makes it difficult to see the differences between commits. We'll use "squash and merge" when merging. |
@AlekseyTs, @dotnet/roslyn-compiler, please review. |
@@ -605,7 +605,8 @@ End Class | |||
New TypeAndValue(uint64Type, CULng(18)), | |||
New TypeAndValue(decimalType, CDec(-11.3)), | |||
New TypeAndValue(doubleType, CDbl(&HF000000000000000UL)), | |||
New TypeAndValue(doubleType, CDbl(&H70000000000000F0L)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should keep CDbl(&H70000000000000F0L)
because it's misleading: the integer &H70000000000000F0L
can't be represented exactly by a Double. As I noted in the PR description, CDbl rounds it down to &H7000000000000000L
, which is why it failed to catch this bug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the explanation. Sounds reasonable.
@@ -1165,7 +1166,8 @@ End Class | |||
New TypeAndValue(uint64Type, CULng(18)), | |||
New TypeAndValue(decimalType, CDec(-11.3)), | |||
New TypeAndValue(doubleType, CDbl(&HF000000000000000UL)), | |||
New TypeAndValue(doubleType, CDbl(&H70000000000000F0L)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<file name="a.vb"><![CDATA[ | ||
Module Program | ||
Sub Main() | ||
System.Console.WriteLine(CType(CDbl(&H8000000000000000L), Long)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
System.Console.WriteLine(CType(CDbl(&H8000000000000000L), Long))
Instead of outputting the values, I think we should compare them with result of conversions performed at runtime. For example it is not obvious to me that the values in the output are correct. I think the overflow check for VB is set to true by default, but it would be good to set this option explicitly (WithOverflowChecks
). #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In each case, the Long argument to CDbl is the expected result of calling CType. So I could write
System.Console.WriteLine(CType(CDbl(&H8000000000000000L), Long) = &H8000000000000000L)
and verify that the output is True. Is that acceptable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is going to rely on compile time constant folding for equality, which would be desirable to avoid. The goal is to compare result of compile time constant folding with result that doesn't rely on constant folding. I have something like the following in mind:
System.Console.WriteLine(CType(CDbl(&H8000000000000000L), Long) = ConvertToLong(CDbl(&H8000000000000000L)))
Function ConvertToLong(x as Double) As Long
Return CType(x, Long)
End Function
Done with review pass (commit 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM (commit 2)
Thanks @MichaelJLiu! |
Fixes #73032.
This PR addresses a bug in the VB.NET compiler.
Description
Any constant or literal
Double
should be convertible at compile time toLong
if its value is within the range of theLong
data type (approximately ±9.223E+18). However, if the value is a large positive number between&H7000000000000400L
and&H7FFFFFFFFFFFFC00L
, then the error"BC30439: Constant expression not representable in type 'Long'."
occurs:In contrast, large negative numbers are unaffected:
Root Cause
Lines 603-609 of the CompileTimeCalculations.DetectFloatingToIntegralOverflow method check whether the input value is between
&H7000000000000000L
and&H8000000000000000UL
, exclusive:On line 606, however, the comparison
UncheckedCLng(temporary) > &H1000000000000000L
is incorrect; it should use<
instead. This can be confirmed in two ways:In the
isUnsigned
branch earlier in this same method, the analogous comparison uses the correct operator:Comments in this method reference the Shared Source CLI source file fjitdef.h, from which this code was presumably adapted. There, the comparison also uses the correct operator:
Unit Tests
I added test cases for the minimum and maximum
Double
values that are convertible toLong
:CDbl(&H8000000000000000L)
(which has always worked) andCDbl(&H7FFFFFFFFFFFFC00L)
(which was previously broken).The original test case
CDbl(&H70000000000000F0L)
failed to catch this bug becauseCDbl
rounded it down to&H7000000000000000L
.