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

Introduction

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.

Question

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


  1. https://github.com/apple/swift/blob/b26a575e1cc8d746ec7b96525d59983a031ba641/lib/ClangImporter/ClangImporter.cpp#L1143-L1144 ↩︎

  2. https://github.com/apple/swift/blob/b26a575e1cc8d746ec7b96525d59983a031ba641/lib/ClangImporter/ClangImporter.cpp#L1920-L1925 ↩︎

  3. https://github.com/apple/swift/blob/b26a575e1cc8d746ec7b96525d59983a031ba641/lib/ClangImporter/ClangImporter.cpp#L1394-L1397 ↩︎

  4. https://github.com/apple/swift/blob/b26a575e1cc8d746ec7b96525d59983a031ba641/lib/ClangImporter/ClangImporter.cpp#L1530-L1537 ↩︎

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.

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