Skip to content

Instantly share code, notes, and snippets.

@rintaro
Last active May 10, 2017 10:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rintaro/a5a9d9836027d7df7a5326a3a8cf9d89 to your computer and use it in GitHub Desktop.
Save rintaro/a5a9d9836027d7df7a5326a3a8cf9d89 to your computer and use it in GitHub Desktop.

Allow #if to guard switch case clauses

Introduction

This proposal adds ability to guard switch case clauses with #if directives.

Swift-evolution thread: Not yet

Motivation

When you want to switch cases only for certain compilation condition, say switching #if os(Linux) guarded enum cases, right now you have to write switch twice:

enum Operation {
  case output(String)
#if os(Linux)
  case syscall(Syscall)
#endif
}

func execute(operation: Operation) {
#if !os(Linux)
   switch operation {
     case .output(let str):
        print(str)
   }
#else
   switch operation {
     case output(let str):
        print(str)
     case .syscall(let call):
        call.execute()
   }
#endif
}

This is annoying and error prone.

Proposed solution

This proposal allows #if to guard switch case clauses.

func execute(operation: Operation) {
    switch operation {
    case .output(let str):
        print(str)
#if os(Linux)
    case .syscall(let call):
        call.execute()
#endif
    }
}

Detailed design

This change shouldn't affect existing #if directives within case clauses. This code should works as expected:

func foo(x: MyEnum) {
    switch x {
    case .some(let str):
        doSomething(str)
#if PRINT_SOME
        print(str)
#endif
    case .other:
        doOther()
    }
}

Only if the next token after #if line is case or default, the Parser treat it as guarding case clauses.

func foo(x: MyEnum) {
    switch x {
    case .some(let str):
        doSomething(str)
#if HAS_OTHER
    case .other:
        doOther()
    }
#endif
}
func foo(x: MyEnum) {
    switch x {
    case .some(let str):
        doSomething(str)
#if HAS_OTHER
    default:
        break
#endif
}

Error cases:

    switch x {
    case .some(let str):
        doSomething(str)
#if HAS_OTHER
    case .other:
        doOther()
#else
        doMore() // error: all statements inside a switch must be covered by a 'case' or 'default'
#endif
    }
    switch x {
    case .some(let str):
        doSomething(str)
#if HAS_OTHER
        doMore()
    case .other:
        doOther() // error: 'case' label can only appear inside a 'switch' statement
#endif
    }
    switch x {
    case .some(let str):
        doSomething(str)
#if HAS_OTHER
    case .other:
        doOther()
#endif
        doMore() // error: all statements inside a switch must be covered by a 'case' or 'default'
    }

You can guard multiple cases as long as it is guarding whole cases:

    switch x {
    case .some(let str):
        doSomething(str)
#if HAS_OTHERS
    case .other:
        doOther()
    case .more:
        doMore()
#endif
    }

Source compatibility

As this is purely additive proposal, it does not affect existing code

Effect on ABI stability

This change does not affect ABI.

Alternatives considered

N/A

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment