Bug > Swift > Enum > String Protocol
I was attempting to create an enumeration in which all its elements were file names, and I stumbled across something interesting. Like so:
enum FileNames: String {
case main = #file
}
This resulted in an internal error. (Segmentation Fault: 11)
I was able to figure how to get an actual error message:
enum Foo: String {
case one = "\(1)"
}
Error: Raw value for enum case must be a literal
Related Questions:
• Is #file
considered a String literal?
• Why does #file
break the enum? Should this be reported on bugs.swift.org?
• I noticed that replacing String
to Int
and #file
to #line
causes the same issue. Is this a hint?
I thought they did, but I made a mistake. It also causes the same internal error.
import UIKit
enum ColorEnum: UIColor {
case foo = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0)
}
According to Apple, #file
is considered a literal:
These also crash the compiler.
enum Foo: String? {
case breaks = nil
}
23 Characters of Mass Destruction enum I:Int?{case a=nil}
The crash has now been fixed, it has been merged officially into Swift here: Merged on GitHub Here's the bug report: SR-12998 . It has officially been implemented in Swift 5.4
Adding Support!
The use of magic literals as raw values for enum cases is being supported here: SR-13022
Yes, #file
and #line
are literal expressions but your program is still ill-formed.
The Swift language reference says :
A literal expression consists of either an ordinary literal (such as a string or a number), an array or dictionary literal, a playground literal, or one of the following special literals:
#file
— String — The name of the file in which it appears.
#line
— Int — The line number on which it appears.[...]
Let's also note the grammar:
literal-expression → literal
literal-expression → array-literal | dictionary-literal | playground-literal
literal-expression → #file | #line | #column | #function | #dsohandle
Now let us consider the grammar of the enum you are defining. I am only including the most relevant parts here you can verify the complete deduction yourself:
enum-declaration → attributes opt access-level-modifier opt raw-value-style-enum
[...]
raw-value-style-enum → enum enum-name generic-parameter-clause opt type-inheritance-clause generic-where-clause opt { raw-value-style-enum-members }
[...]
raw-value-assignment → = raw-value-literal
raw-value-literal → numeric-literal | static-string-literal | boolean-literal
It's noteworthy that only numeric-literal , static-string-literal , boolean-literal are allowed. If we look at their definitions, it's clear that those #
literals thus do not match the raw-value-literal
rule:
numeric-literal → -opt integer-literal | -opt floating-point-literal
boolean-literal → true | false
static-string-literal → string-literal-opening-delimiter quoted-text opt string-literal-closing-delimiter
static-string-literal → multiline-string-literal-opening-delimiter multiline-quoted-text opt multiline-string-literal-closing-delimiter
All relevant rules for completely defining static-string-literal are long, but it's still trivial to see that static-string-literal cannot be deduced to #file
and cannot include interpolation. (That's what makes it static.)
So the Swift compiler is indeed right in refusing your program. Still, a modern compiler shouldn't simply crash on an illegal program, so it might be worth reporting this issue.
This is definitely a bug, so worth submitting feedback to Apple (although they documented that those #file, #function, etc. are special literals )
Anyway, probably the following workaround can be helpful in some use-cases:
enum FileNames {
static let main = FileNames.file(#file)
case file(String)
}
Swift 5.4 implements the bugfix from SR-12998
Instead of the compiler crashing, it now displays descriptive error messages:
Magic Literals from Strings
enum Foo: String {
// Compiler Error: Use of '#file' literal as raw value for enum case is not supported
case b = #file
// Compiler Error: Use of '#function' literal as raw value for enum case is not supported
case c = #function
}
Magic Literals from Ints
enum B: Int {
// Compiler Error: Use of '#line' literal as raw value for enum case is not supported
case b = #line
// Compiler Error: Use of '#column' literal as raw value for enum case is not supported
case c = #column
}
Magic Literals from UnsafeRawPointers
extension UnsafeRawPointer: ExpressibleByIntegerLiteral {}
enum Wo: UnsafeRawPointer {
// Compiler Error: Use of '#dsohandle' literal as raw value for enum case is not supported
case wo = #dsohandle
}
Using the Magic Literal nil
in this way now displays a different error instead of crashing the compiler.
enum Foo: String? {
case breaks = nil
}
enum I:Int?{case a=nil}
However, the magic literal nil
still can cause the compiler to crash:
extension Optional: ExpressibleByIntegerLiteral where Wrapped == Int {}
enum Foo: Int? {
// Compiler Crashes: Segmentation fault: 11
case c = nil
}
Also the magic literal for UIColor still cause the compiler to crash:
import UIKit
extension UIColor: ExpressibleByIntegerLiteral {}
enum ColorEnum: UIColor {
// Compiler Crashes: Segmentation fault: 11
case foo = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0)
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.