-
Notifications
You must be signed in to change notification settings - Fork 458
Add Double and Int Convenience Properties #239
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
Conversation
@swift-ci Please test |
Build failed |
Build failed |
The failures are because the generated files differ:
Does the swift repo PR need update? |
@swift-ci Please test @vermont42 Could you squash the commits again? |
@swift-ci Please test |
@ahoppen I will do so, but I just realized that because I was using a pre-concurrency version of |
Ah, I see. Let me know when you have regenerated the files and I will trigger CI again. |
@vermont42 What you need to do, is to rebase your swift PR so that the version in your branch contains both the concurrency changes as well as your changes and then re-generate the files for this PR. If you look at the diff for the generated files in this PR they should contain no concurrency-related changes. |
@swift-ci Please test I fixed the generated files to restore the recent concurrency enhancements, and tests passed locally. In the beginning of this comment, I attempt to invoke CI, not knowing whether I have permission to do so. (No prob if I don't.) When CI passes as a result of this invocation or someone else's, I will squash commits. |
@swift-ci Please test |
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.
Looks good to me
$0 != "_" | ||
} | ||
|
||
return Int(textWithoutPrefixOrUnderscores, radix: radix) |
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'm sorry to have missed the initial review, but this would crash for valid integer literals that are too large to be represented in Int
. Is this intentional? If so, shouldn't this API be intValue
and the floating-point API doubleValue
and not integerValue
/floatingValue
(the latter implying that any valid integer or floating-point value would be returned)?
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.
According to the Int
initializer's documentation, "if the value [that the String
parameter] denotes in the given radix
is not representable, the result is nil
." I confirmed this by running the following code:
let goodInt = Int("42", radix: 10)
let badInt = Int("42424242424242424242424242424242424242424242424242424242424242424242424242", radix: 10)
print("@@@ \(goodInt) @@@ \(badInt) @@@")
The output was:
@@@ Optional(42) @@@ nil @@@
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.
Right, so force unwrapping that value will crash. Is this the intended behavior here for a valid integer literal?
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.
A valid IntegerLiteralExprSyntax
would not cause a crash.
The only way a crash would occur, via force unwrap, is if a client called integerValue
on an invalid IntegerLiteralExprSyntax
. However, there are guardrails in place to prevent clients from creating invalid IntegerLiteralExprSyntax
es. Those guardrails, not initially present in the PR, were the primary focus of feedback.
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 might be misunderstanding then. My assumption was that 'IntegerLiteralExprSyntax' can represent any valid integer literal? If so, then calling 'integerValue' on some valid 'IntegerLiteralExprSyntax' values would crash, because not all valid integer literals can be represented as an 'Int'. For example, '42424242424242424242424242424242424242424242424242424242424242424242424242' is a valid integer literal.
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.
This isn't quite accurate; IntegerLiteralExprSyntax
is not limited to what can fit into the Int
type; it's any sequence of characters that is syntactically a valid integer literal. The error you're showing occurs later on at the semantic analysis phase, but the compiler and SwiftSyntax will still parse it without error. If we try the following command:
echo 'let foo = 42424242424242424242424242424242424242424242424242424242424242424242424242' | \
swiftc -frontend -emit-syntax -
we'll see this in the output:
...
{
"id":6,
"kind":"IntegerLiteralExpr",
"layout":[{
"id":5,
"tokenKind": {
"kind":"integer_literal",
"text":"42424242424242424242424242424242424242424242424242424242424242424242424242"
},
So potentialIntegerValue
called on this would return nil
, and integerValue
would crash.
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.
Right, integer literals are untyped; they are not “Int literals” and can now be arbitrarily large (previously, they were limited to something like 2048 bits).
The same applies for the floating-point types. They should not return rounded Double
values if those values cannot represent the literal exactly.
I would recommend renaming them for clarity as well: the literals are called “integer literals” and “float literals,” so these APIs should be called “integerLiteralValue” and “floatLiteralValue.” Ideally, they would be generic over T: ExpressibleByIntegerLiteral
and T: ExpressibleByFloatLiteral
so users can get the value represented in any type they want.
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.
Many thanks to Xiaodi, Rintaro, Harlan, and Αργύριος for their thoughtful review and input on the PR. Extra thanks to Alex for opening the starter bug, creating the initial implementation, providing me the GYB strategy, and answering my questions. |
This reverts commit 94fc5ae. This couldn't handle integer literal syntax more than Int.max e.g. 0x9e3779b97f4a0000
Revert "Add Double and Int Convenience Properties (#239)"
…ak_fix Revert "Fix for newly optional return type of `withDigits`."
Building on the work of @ahoppen on SR-11580, this PR adds convenience properties to get
Double
andInt
values fromFloatLiteralExprSyntax
andIntegerLiteralExprSyntax
, respectively.The two new properties,
floatingValue
andintValue
, use failable initializers ofDouble
andInt
:init?<S>(_ text: S) where S : StringProtocol
andinit?<S>(_ text: S, radix: Int) where S : StringProtocol
. These initializers fail if theString
s passed in do not represent validDouble
s orInt
s. The question arises: what shouldfloatingValue
andintValue
return, if anything, if initialization fails? I considered the following possibilities, ultimately choosing the fourth.floatingValue
could returnDouble.nan
. This would make sense because, for example,"🍕.🌍"
is truly not a number, floating-point or otherwise. The problem with this approach is that there is no correspondingInt.nan
, which would preventfloatingValue
andintValue
from returning the same sort of value given invalid input.Decimal
does have anan
value, so ifintValue
returned aDecimal
rather than anInt
, that would solve the problem, but I, as a consumer of the API, would find this counter-intuitive because"42"
in a source file represents anInt
, not aDecimal
.The return types of
floatingValue
andintValue
could be optional. IfDouble
orInt
initialization fails,floatingValue
andintValue
could therefore returnnil
. I preferred not to impose on API consumers the burden of unwrapping.The return types of
floatingValue
andintValue
could beResult<Double, LiteralError>
andResult<Int, LiteralError>
, respectively. IfDouble
orInt
initialization fails,floatingValue
andintValue
could return.failure(.invalid)
. I preferred not to impose on API consumers the burden ofswitch
ing on the returned value.The result of the
Double
orInt
initialization can be force-unwrapped. This causes a crash if initialization fails. Crashing is considered harmful, though not by everyone. Force-unwrapping is the most convenient option for API consumers, which is why I chose it. I observe that force-unwrapping does appear elsewhere in SwiftSyntax, for example here, here, here, and here, and I suspect that the authors of this code made similar judgments about the convenience of force-unwrapping versus the crashing risk it imposes.Feedback on this code occurred in this PR, which I closed due to a
rebase
error described here.