--emit-clang-header-path appears to generate invalid headers

Hi everyone,

We can emit a C++ header file on Windows using the -emit-clange-header-path flag. This flag is undocumented via swiftc --help but its documented in the swift.org article Setting Up Mixed-Language Swift and C++ Projects. For example:

swiftc /sources/a.swift --module-name SomeModule -cxx-interoperability-mode=default -emit-clang-header-path SomeModule.h

On macOS I have had success in calling into Swift dynamic libraries using the generated header file.

On Windows, the header file is generated, but it appears to be invalid.

Using both Swift 5.10.1 and release/6.0 (August 7, 2024) on Windows, I have generated C++ headers. In both cases, clang++ fails to include the header. In the case of 5.10.1, the errors emitted are:

SomeModule.h:337:17: error: expected '{'
namespace swift SWIFT_PRIVATE_ATTR {
                ^
SomeModule.h:337:17: error: a type specifier is required for all declarations
SomeModuleh:339:1: error: expected expression
namespace _impl {
^
SomeModule.h:556:2: error: expected ';' after top level declarator
} // namespace swift

Note that in the case of 5.10.1, the version of Clang in the toolchain is 16.0.0, and I ensured the system Clang (installed via LLVM) was also 16.0.0. We can provoke the errors outside a full compilation attempt with:

clang -fsyntax-only -x c++-header SomeModule.h

... And the errors are provoked both by the LLVM-installed Clang 16.0.0 and the toolchain Clang 16.0.0 executables.

A full reproduction Swift package is as follows:

Reproduction Case Files

Package.swift

// swift-tools-version: 5.10
import PackageDescription

let package = Package(
    name: "Minimal",
    products: [
        .library(name: "Minimal", type: .dynamic, targets: ["Minimal"]),
    ],
    targets: [
        .target(
            name: "Minimal",
            swiftSettings: [.interoperabilityMode(.Cxx)]
        )
    ]
)

Sources/Minimal/Foo.swift


public struct Foo {
    
    public let baz: Int
    
    public init(baz: Int) {
        self.baz = baz
        return
    }
    
    public func multiplyByBaz(otherValue: Int) -> Int {
        return self.baz * otherValue
    }
    
}

Does anyone have any ideas as to what might be happening, and / or how this issue might be resolved?

I've managed to solve this problem - Throughout the generated header, there are #include statements that reference relative paths to C++ interoperability headers contained in the Swift SDK.

By replacing these relative includes with "known good" path (for example, adjacent to the generated header wherever that header is being used), the generated header can be used.