How To Fix 'LLDB: Couldn't IRGen Expression' Without Source Code

How To Fix 'LLDB: Couldn't IRGen Expression' Without Source Code

January 5, 2021
error: virtual filesystem overlay file '/Users/user/Library/Developer/Xcode/DerivedData/SDK-abcdefghijklmnopqrstuvwxyzab/Build/Intermediates.noindex/SDK.build/Release-iphonesimulator/SDK.build/all-product-headers.yaml' not found

error: couldn't IRGen expression. Please check the above error messages for possible root causes.

If you’ve followed Peter Steinberger’s journey on fixing “LLDB: Couldn’t IRGen Expression” and related issues, you’ve probably already read his blog post that explains why this happens. Matt Robinson also wrote a very detailed article about how to deal with the same issue if you’re building your frameworks using Carthage.

In a not very uncommon scenario, when the issues come from a closed source framework, and your vendor didn’t build the framework using the same Xcode / Swift compiler version that you’re using, or didn’t build it with “Build Libraries for Distribution” enabled, or didn’t build it with SWIFT_SERIALIZE_DEBUGGING_OPTIONS = NO, there’s a chance it could take a long time before you can receive an update of the framework with the fix you have requested. In this post, I’ve collected some hacks that can help you work around the problem and get the debugger working without having to wait for a fix.

Case 1: Both swiftmodule and swiftinterface are included; swiftinterface files are valid #

Solution: Force compiling with swiftinterface.

If your vendor packages their framework properly, it should only include swiftinterface files, but not any swiftmodule files. If both are included, Xcode will try to import the swiftmodule file first, which could lead to one of the above errors when using LLDB with po, vo or e. Since the swiftinterface files are all we need, we could patch the framework to remove all the swiftmodule files:

rm -f FrameworkName.framework/Modules/FrameworkName.swiftmodule/*.swiftmodule

Note that what we need to remove are the swiftmodule files in the .swiftmodule directory in the Modules directory, but not the .swiftmodule directory itself.

Case 2: Both swiftmodule and swiftinterface are included; but swiftinterface files are invalid #

Solution: Remove all swiftmodule files and preprocess invalid swiftinterface files before giving them to Xcode.

Unfortunately, not all swiftinterfaces are created validly, as there is currently no way to have them automatically validated. A common error you might get is that Xcode can’t import the interface file if the framework has a type with the same name, as the generated interface file always references to a type using its full namespace.

‘FrameworkName’ is not a member type of ‘FrameworkName’

A possible workaround is to process the swiftinterface file and remove all the occurrences of the framework name that lead to any ambiguity, until it becomes a valid swiftinterface. This is obviously not great, as you would have to keep track of all the interface changes at every update; but if you ever get an update, you probably don’t have to do this anymore.

Case 3: Only swiftmodule files are included #

Solution: Construct swiftinterface from swiftmodule.

Since “Build Libraries for Distribution” affects how the module is built in the first place, there currently seems to be no way to automatically generate a swiftinterface from the swiftmodule without the module’s source code. However, in Xcode, if we select the pre-compiled framework’s module name from an import statement, right click, and select Jumps to Definition, (and wait for ten seconds :)), Xcode will display a generated interface for that module. We can copy that piece of text and manually construct the swiftinterface ourselves. It’s just a text file with some predefined header comments like the following: (If you use this, be sure to change the swift-compiler-version, swift-module-flags and FrameworkName to match your needs.)

// swift-interface-format-version: 1.0
// swift-compiler-version: Apple Swift version 5.2 (swiftlang-1103.0.32.1 clang-1103.0.32.29)
// swift-module-flags: -target x86_64-apple-ios10.0-simulator -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name FrameworkName
@_exported import FrameworkName
// Copy and paste generated interface from Xcode here

To validate the manually constructed swiftinterface file, try to regenerate a swiftmodule from it with:

swift -frontend \
  -compile-module-from-interface \
  -o <output-swiftmodule> \
  -sdk </path/to/sdk> \
  -target <target-triple> \
  -enable-library-evolution \
  -module-name <module-name> \
  <input-swiftinterface>

For example:

swift -frontend \
  -compile-module-from-interface \
  -o x86_64.swiftmodule \
  -sdk `xcode-select -p`/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.3.sdk \
  -target x86_64-apple-ios10.0-simulator \
  -enable-library-evolution \
  -module-name FrameworkName \
  x86_64.swiftinterface

Probably it’s not long until someone writes a tool that automates all these.