# SwiftIfConfig library is replacing the compiler's #if handling

**URL:** <https://forums.swift.org/t/swiftifconfig-library-is-replacing-the-compilers-if-handling/74411>\
**Category:** Compiler\
**Created:** [September 6, 2024, 5:27am UTC](https://forums.swift.org/t/swiftifconfig-library-is-replacing-the-compilers-if-handling/74411 "2024-09-06T05:27:33Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Douglas\_Gregor](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/douglas_gregor/32/1362_2.png) [@Douglas\_Gregor](https://forums.swift.org/u/Douglas_Gregor)\
**Post date:** [September 6, 2024, 5:27am UTC](https://forums.swift.org/t/swiftifconfig-library-is-replacing-the-compilers-if-handling/74411/1 "2024-09-06T05:27:33Z")

</div>

Hi all,

Recently, I've been on a quest to reimplement how the Swift compiler handles `#if` directives. The first major piece of this is the [`SwiftIfConfig`](https://swiftpackageindex.com/swiftlang/swift-syntax/main/documentation/swiftifconfig) library, which is part of the swift-syntax package that I introduced a few months ago [in this forum thread](https://forums.swift.org/t/swiftifconfig-library-for-handling-if-clauses/73004).

Since then, I've managed to replace all of the `#if` handling in the compiler with the new library:

- [Implemented "configured regions" for the `#if` folding in IDEs and to help with code coverage](https://github.com/swiftlang/swift/pull/75014)
- [Replaced the logic for evaluating the `#if` condition itself in the C++ parser](https://github.com/swiftlang/swift/pull/75688), making most of [`ParseIfConfig.cpp`](https://github.com/swiftlang/swift/blob/main/lib/Parse/ParseIfConfig.cpp) dead code.
- [Added support for `#if` in ASTGen](https://github.com/swiftlang/swift/pull/75937) to support the use of the new parser.
- [Determined which syntax errors to print based on the `#if`s](https://github.com/swiftlang/swift/pull/76074) for the new parser.
- [Replacing the handling of "inlinable text" extraction](https://github.com/swiftlang/swift/pull/76187).

We're in the home stretch. The next major task is to remove [`IfConfigDecl`](https://github.com/swiftlang/swift/blob/main/include/swift/AST/Decl.h#L2661-L2704) from the C++ semantic AST. We no longer want to model `#if`s at all in the C++ semantic AST, because doing so is what prevents us from being able to expand `#if` usage to other parts of the grammar easily. It's also a nice simplification for the compiler, allowing us to eliminate log for dealing with inactive `#if` regions in a lot of places.

So, I went ahead and turned off the generation of `IfConfigDecl` in the compiler, and uncovered the next set of issues based on the compiler's test suite. Here they are!

## Suppressing warnings based on what's in inactive code

Consider this code:

```swift
func f() {
  let x = 10
#if DEBUG
  print(x)
#endif
}

```

The variable `x` is never used. Without the `#if DEBUG` block, the compiler would emit a warning "initialization of immutable value 'x' was never used". However, the compiler looks into the inactive `#if DEBUG` block and sees that `x` was mentioned, so it suppresses the warning.

There's a similar thing for `try` and `throw` occurrences within an inactive `#if`:

```swift
func f() {
  do {
#if DEBUG
    try g()
#endif
  } catch {
    // ... dead code ...
  }
}

```

Normally, the compiler would warn about an unreachable `catch`, but that warning is suppressed when there's a `try` or `throw` within an inactive `#if`.

I think we want to preserve this behavior. The way I'm planning to do that is to have the warning go look for any inactive `#if` clauses in the current scope (using the "configured regions" API mentioned earlier) and look for any tokens that refer to the variable name (for the unused variable warnings) or are try/throw (for the thrown-error one).

## Anachronistic SourceKit queries

There are a number of SourceKit queries that do syntactic things based on the C++ `IfConfigDecl`. These have effectively all been replaced with swift-syntax (and swift-format), which does a better job with unparsed code (e.g., code behind an inactive `#if compiler(>=6.1)`) and is generally better for IDE usage. These are the tests from the Swift test suite that start failing because they test handling of inactive `#if` regions based on `IfConfigDecl`:

```swift
Swift(macosx-arm64) :: SourceKit/SyntaxMapData/syntaxmap.swift
Swift(macosx-arm64) :: SourceKit/CodeExpand/code-expand.swift
Swift(macosx-arm64) :: SourceKit/CodeFormat/indent-pound-if.swift
Swift(macosx-arm64) :: SourceKit/DocumentStructure/structure.swift
Swift(macosx-arm64) :: IDE/coloring_configs.swift
Swift(macosx-arm64) :: IDE/coloring_unclosed_hash_if.swift

```

The main SourceKit clients no longer use these SourceKit queries, so I propose to break their current handling of `#if` in the upcoming Swift release (the next one to branch from `main`) and deprecate the queries, then remove the queries entirely in the following Swift release.

## Remove the `swift-indent` frontend mode

Until I did this experiment, I hadn't realized that it's possible to ask the Swift compiler frontend to indent the given source file. It uses the C++ semantic AST and a bunch of heuristics, and predates swift-syntax and swift-format by years. We don't actually ship this as a tool anywhere, so I think we can outright remove it now without anyone being affected. If that's not possible, we can do the same staged deprecated/removal I'm proposing for SourceKit.

Given how far we've come with SwiftIfConfig, we're really close to getting this part of the compiler onto a nice, new swift-syntax library and simplifying the main compiler code base.

Doug

---

<div class="post-metadata">

**Author:** ![compnerd](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/compnerd/32/2165_2.png) [@compnerd](https://forums.swift.org/u/compnerd)\
**Post date:** [September 6, 2024, 4:19pm UTC](https://forums.swift.org/t/swiftifconfig-library-is-replacing-the-compilers-if-handling/74411/2 "2024-09-06T16:19:46Z")

</div>

> [@Douglas\_Gregor](#):
>
> ## Remove the `swift-indent` frontend mode

I think that this can be done pretty quickly. There is very little value from this tool today, especially with swift-format being available. The implementation is also quite limited as it was never really given the needed attention. I would need to check if we still include it in the Windows toolchain, but would be more than happy to see it removed.

---

<div class="post-metadata">

**Author:** ![rintaro](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/rintaro/32/568_2.png) [@rintaro](https://forums.swift.org/u/rintaro)\
**Post date:** [September 6, 2024, 4:39pm UTC](https://forums.swift.org/t/swiftifconfig-library-is-replacing-the-compilers-if-handling/74411/3 "2024-09-06T16:39:20Z")

</div>

> [@Douglas\_Gregor](#):
>
> ## Suppressing warnings based on what's in inactive code

It's worth to mention that this is currently broken for postfix `#if` expressions. .e.g

```swift
func f() -> Foo {
  let x = 10 // warning: ... 'x' was never used...
  return foo
     #if FLAG
     .modifier(x)
     #endif
}

```

This is because AST doesn't preserve the inactive regions as `IfConfigDecl`.

> [@Douglas\_Gregor](#):
>
> The way I'm planning to do that is to have the warning go look for any inactive `#if` clauses in the current scope (using the "configured regions" API mentioned earlier) and look for any tokens that refer to the variable name (for the unused variable warnings) or are try/throw (for the thrown-error one).

This should fix the current postfix `#if` issue, and can be future proof for all the potential `#if` support expansions (e.g. array/dictionary literals) 🙌

---

<div class="post-metadata">

**Author:** ![Douglas\_Gregor](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/douglas_gregor/32/1362_2.png) [@Douglas\_Gregor](https://forums.swift.org/u/Douglas_Gregor)\
**Post date:** [September 6, 2024, 4:44pm UTC](https://forums.swift.org/t/swiftifconfig-library-is-replacing-the-compilers-if-handling/74411/4 "2024-09-06T16:44:58Z")

</div>

> [@rintaro](#):
>
> It's worth to mention that this is currently broken for postfix `#if` expressions. .e.g

Great point!

> [@rintaro](#):
>
> This should fix the current postfix `#if` issue, and can be future proof for all the potential `#if` support expansions (e.g. array/dictionary literals) 🙌

Yes. Improvements into where `#if` can be used have effectively been blocked on the C++ `IfConfigDecl` design.

Doug

---

<div class="post-metadata">

**Author:** ![Slava\_Pestov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/slava_pestov/32/30204_2.png) [@Slava\_Pestov](https://forums.swift.org/u/Slava_Pestov)\
**Post date:** [September 6, 2024, 9:35pm UTC](https://forums.swift.org/t/swiftifconfig-library-is-replacing-the-compilers-if-handling/74411/5 "2024-09-06T21:35:28Z")

</div>

> [@Douglas\_Gregor](#):
>
> We're in the home stretch. The next major task is to remove [`IfConfigDecl`](https://github.com/swiftlang/swift/blob/main/include/swift/AST/Decl.h#L2661-L2704) from the C++ semantic AST.

My only interaction with IfConfigDecl has been running into situations where it’s special cased. I’m very happy to hear it’s going away!

---

<div class="post-metadata">

**Author:** ![Douglas\_Gregor](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/douglas_gregor/32/1362_2.png) [@Douglas\_Gregor](https://forums.swift.org/u/Douglas_Gregor)\
**Post date:** [September 7, 2024, 5:50am UTC](https://forums.swift.org/t/swiftifconfig-library-is-replacing-the-compilers-if-handling/74411/6 "2024-09-07T05:50:25Z")

</div>

> [@Douglas\_Gregor](#):
>
> ## Suppressing warnings based on what's in inactive code

I took a stab at re-implementing this functionality in terms of swift-syntax and `SwiftIfConfig`. The [pull request is here](https://github.com/swiftlang/swift/pull/76327) and, as expected, suppresses warnings for "unparsed" code (such as code in `#if compiler(>=7.0)`) correctly.

Doug

---

<div class="post-metadata">

**Author:** ![benrimmington](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/benrimmington/32/695_2.png) [@benrimmington](https://forums.swift.org/u/benrimmington)\
**Post date:** [September 7, 2024, 7:21am UTC](https://forums.swift.org/t/swiftifconfig-library-is-replacing-the-compilers-if-handling/74411/7 "2024-09-07T07:21:35Z")

</div>

Using the Swift 6.0 compiler in Xcode 16 beta 6 (16A5230g), I've noticed that the following warning isn't suppressed:

```swift
#if false
  @available(System 0.0.1, *) // warning: unrecognized platform name 'System'
#endif

```

But it's suppressed when first checking for a previous compiler version:

```swift
#if compiler(>=5.8) && false
  @available(System 0.0.1, *) // OK
#endif

```

I can't find any tests for the above, so I don't know if it behaves as expected.

---

<div class="post-metadata">

**Author:** ![Douglas\_Gregor](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/douglas_gregor/32/1362_2.png) [@Douglas\_Gregor](https://forums.swift.org/u/Douglas_Gregor)\
**Post date:** [September 9, 2024, 3:56pm UTC](https://forums.swift.org/t/swiftifconfig-library-is-replacing-the-compilers-if-handling/74411/8 "2024-09-09T15:56:58Z")

</div>

> [@benrimmington](#):
>
> Using the Swift 6.0 compiler in Xcode 16 beta 6 (16A5230g), I've noticed that the following warning isn't suppressed:
> 
> ```swift
> #if false
> @available(System 0.0.1, *) // warning: unrecognized platform name 'System'
> #endif
> 
> ```

The fact that the `#if compiler(5.8)` suppresses the diagnostic is intended. In the SwiftIfConfig library, it's the distinction between "inactive" (parsed but does not contribute to the program) and "unparsed" (not even parsed), where the former can emit diagnostics and the latter cannot.

> [@benrimmington](#):
>
> I can't find any tests for the above, so I don't know if it behaves as expected.

[Unit tests](https://github.com/swiftlang/swift-syntax/blob/main/Tests/SwiftIfConfigTest/ActiveRegionTests.swift) for SwiftIfConfig test the distinction between inactive and unparsed. The compiler also has [tests to show that the `compiler` check suppresses diagnostics](https://github.com/swiftlang/swift/blob/main/test/Parse/ConditionalCompilation/compiler.swift).

Doug

---

<div class="post-metadata">

**Author:** ![Douglas\_Gregor](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/douglas_gregor/32/1362_2.png) [@Douglas\_Gregor](https://forums.swift.org/u/Douglas_Gregor)\
**Post date:** [September 9, 2024, 4:00pm UTC](https://forums.swift.org/t/swiftifconfig-library-is-replacing-the-compilers-if-handling/74411/9 "2024-09-09T16:00:09Z")

</div>

> [@Douglas\_Gregor](#):
>
> ## Anachronistic SourceKit queries

I've made the change in the behavior of these queries (so they ignore inactive/unparsed regions) in [this pull request](https://github.com/swiftlang/swift/pull/76332).

We also had some behavior that printed the contents of `#if` from the AST representation, although it's disabled for all of the important outputs (e.g., swift interfaces). I've [removed this behavior](https://github.com/swiftlang/swift/pull/76334) and expect that this chance will be invisible outside the compiler test suite.

Doug

---

<div class="post-metadata">

**Author:** ![Douglas\_Gregor](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/douglas_gregor/32/1362_2.png) [@Douglas\_Gregor](https://forums.swift.org/u/Douglas_Gregor)\
**Post date:** [September 19, 2024, 5:33pm UTC](https://forums.swift.org/t/swiftifconfig-library-is-replacing-the-compilers-if-handling/74411/10 "2024-09-19T17:33:08Z")

</div>

> [@Douglas\_Gregor](#):
>
> The next major task is to remove [`IfConfigDecl`](https://github.com/swiftlang/swift/blob/main/include/swift/AST/Decl.h#L2661-L2704) from the C++ semantic AST.

And... here is the [pull request to remove `IfConfigDecl` from the C++ semantic AST](https://github.com/swiftlang/swift/pull/76565). All of the logic to handle `#if` declarations in the compiler is now implemented via the `SwiftIfConfig` library.

We're still carrying some of the C++ implementations of `#if` handling for use when the compiler is building without a host Swift compiler. We'll get to remove those once we stop supporting building a Swift toolchain without a Swift host compiler, but they are effectively dead code right now in the main host configurations.

Doug

---

<div class="post-metadata">

**Author:** ![compnerd](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/compnerd/32/2165_2.png) [@compnerd](https://forums.swift.org/u/compnerd)\
**Post date:** [September 19, 2024, 6:24pm UTC](https://forums.swift.org/t/swiftifconfig-library-is-replacing-the-compilers-if-handling/74411/11 "2024-09-19T18:24:12Z")

</div>

[Remove `swift-indent` tool by compnerd · Pull Request #76577 · swiftlang/swift · GitHub](https://github.com/swiftlang/swift/pull/76577) will remove the `swift-indent` tool as that use case is better served with `swift-format`. I don't think that the tool was sufficiently complete nor well maintained sufficiently to really require a deprecation period and should be relatively harmless to drop. I don't think that we were currently shipping it anywhere.
