Add Double and Int Convenience Properties#239
Add Double and Int Convenience Properties#239akyrtzi merged 3 commits intoswiftlang:masterfrom vermont42:literal-values2
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 |
| $0 != "_" | ||
| } | ||
|
|
||
| return Int(textWithoutPrefixOrUnderscores, radix: radix) |
There was a problem hiding this comment.
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.
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.
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.
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 IntegerLiteralExprSyntaxes. Those guardrails, not initially present in the PR, were the primary focus of feedback.
There was a problem hiding this comment.
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.
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.
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.
|
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
DoubleandIntvalues fromFloatLiteralExprSyntaxandIntegerLiteralExprSyntax, respectively.The two new properties,
floatingValueandintValue, use failable initializers ofDoubleandInt:init?<S>(_ text: S) where S : StringProtocolandinit?<S>(_ text: S, radix: Int) where S : StringProtocol. These initializers fail if theStrings passed in do not represent validDoubles orInts. The question arises: what shouldfloatingValueandintValuereturn, if anything, if initialization fails? I considered the following possibilities, ultimately choosing the fourth.floatingValuecould 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 preventfloatingValueandintValuefrom returning the same sort of value given invalid input.Decimaldoes have ananvalue, so ifintValuereturned aDecimalrather 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
floatingValueandintValuecould be optional. IfDoubleorIntinitialization fails,floatingValueandintValuecould therefore returnnil. I preferred not to impose on API consumers the burden of unwrapping.The return types of
floatingValueandintValuecould beResult<Double, LiteralError>andResult<Int, LiteralError>, respectively. IfDoubleorIntinitialization fails,floatingValueandintValuecould return.failure(.invalid). I preferred not to impose on API consumers the burden ofswitching on the returned value.The result of the
DoubleorIntinitialization 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
rebaseerror described here.