SR-15822 ClangImporter's bug: Why does it import separately clang modules and bridging C headers?


I did a poor investigation of SR-15586: import Foundation hides declarations (derived from C) in other modules on Linux for these few months.
I finally found that it is a bug caused by ClangImporter which is due to how it imports C declarations. Furthermore, the issue would occur on any platforms, even on macOS. (Filed as SR-15822: Existence of modulemap disables declarations in simple C headers in some cases.)

How the bug is produced

ClangImporter(ClangImporter::Implementation) holds the instances of ClangModuleUnit which wraps a clang module. On the other hand, it also holds BridgingHeaderLookupTable that contains C declarations derived from bridging headers, for example, from headers imported with -import-objc-header.
The problem here is that ClangImporter::Implementation has only one unique instance of clang::CompilerInstance (created in ClangImporter::create[1]). It also uses one unique instance of clang::Preprocessor which holds a unique instance of clang::HeaderSearch. (The status quo of Swift's implementation)

ClangImporter::Implementation::loadModuleClang calls the instance of clang::HeaderSearch to load a clang module[2]. At the time of it, #include guards in the headers required by the module are being enabled.

That does matter when ClangImporter::Implementation tries to import bridging headers.
ClangImporter::Implementation::importHeader uses the instance of clang::Preprocessor to parse the bridging header such as #include "c.h"[3] which is generated in ClangImporter::importBridgingHeader[4].

However, if the bridging header wants to include some headers already imported by some other clang modules, #include guards block to include the same headers.
Then, no declarations in those headers are added to BridgingHeaderLookupTable.

As a result, for example, LookupByName::doLocalLookup fails to look up some types, because it doesn't try to look up the type in other (clang) modules.

I think this feature of Clang::Importer causes some bugs such as SR-15822, SR-15586, SR-15532, and SR-15462.
The error messages don't imply the nature of the problem, but the compiler would be confused by such feature.


Why does ClangImporter import separately clang modules and bridging C headers?

  1. swift/ClangImporter.cpp at b26a575e1cc8d746ec7b96525d59983a031ba641 · apple/swift · GitHub ↩︎

  2. swift/ClangImporter.cpp at b26a575e1cc8d746ec7b96525d59983a031ba641 · apple/swift · GitHub ↩︎

  3. swift/ClangImporter.cpp at b26a575e1cc8d746ec7b96525d59983a031ba641 · apple/swift · GitHub ↩︎

  4. swift/ClangImporter.cpp at b26a575e1cc8d746ec7b96525d59983a031ba641 · apple/swift · GitHub ↩︎

1 Like

A resolution would be one of my humble suggestions below.

  1. Let all declarations be stored into one place.
  2. Use separate instances to compile/load bridging headers and clang modules.
    2-1. Let ASTContext have several instances of ClangImporter.
    2-2. Let ClangImporter have several instances of ClangImporter::Implementation.
    2-3. Let ClangImporter::Implementation have several instances of clang::CompilerInstance etc.
  3. Look up types in all clang tables/modules in any cases.

I've pitched a rough pull request as the first step of 2-3. to raise a question.

New PR: [ClangImporter] SR-15822: Let it have separate compiler instances. by YOCKOW · Pull Request #41295 · apple/swift · GitHub