简体   繁体   中英

Is #file considered a literal String in Swift?

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?


Color Literals Don't Work

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)
}

The Swift Programming Language (Swift 5.2)

According to Apple, #file is considered a literal:

#file 根据 Apple 描述为文字


What about nil Literals?

These also crash the compiler.

enum Foo: String? {
    case breaks = nil
}

23 Characters of Mass Destruction enum I:Int?{case a=nil}


Bug Fixed

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 fixes the crash

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}

It's not completely fixed:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM