Skip to content

ClangImporter: enhance the importer to alias declarations #81840

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 9, 2025

Conversation

compnerd
Copy link
Member

Import simple CPP macro aliases as aliases in Swift. Extend the macro importer to import the following construct:

#define alias aliasee

as the following Swift construct:

@_transparent @inline(__always)
var alias: type(of: aliasee) {
  aliasee
}

This improves the QoI for Windows where there is a universal define (UNICODE) which normally is used for translating APIs between ANSI and Unicode variants, e.g.:

#if defined(UNICODE)
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif

@compnerd
Copy link
Member Author

@swift-ci please smoke test

Copy link
Contributor

@egorzhdan egorzhdan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is exciting, thanks!

@egorzhdan egorzhdan added the c++ interop Feature: Interoperability with C++ label May 29, 2025
@compnerd
Copy link
Member Author

@swift-ci please smoke test

@compnerd compnerd requested a review from egorzhdan May 29, 2025 21:13
@compnerd compnerd added clang importer Area → compiler: The clang importer c/c++ macros labels May 29, 2025
Copy link
Contributor

@al45tair al45tair left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice; this will be a huge quality of life improvement for Windows development.

Copy link
Contributor

@egorzhdan egorzhdan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM modulo Alastair's question

@compnerd
Copy link
Member Author

@swift-ci please smoke test Linux platform

@compnerd
Copy link
Member Author

@swift-ci please test Windows platform

@compnerd
Copy link
Member Author

compnerd commented Jun 1, 2025

@swift-ci please test Windows platform

@compnerd
Copy link
Member Author

compnerd commented Jun 2, 2025

@swift-ci please smoke test

@compnerd
Copy link
Member Author

compnerd commented Jun 2, 2025

@swift-ci please smoke test

@compnerd
Copy link
Member Author

compnerd commented Jun 6, 2025

@swift-ci please smoke test

@compnerd
Copy link
Member Author

compnerd commented Jun 6, 2025

20:49:21   154 |     var endTime = timespec(tv_sec: 0, tv_nsec: 0)
20:49:21   155 | 
20:49:21   156 |     clock_gettime(CLOCK_MONOTONIC, &startTime)
20:49:21       |                   `- error: ambiguous use of 'CLOCK_MONOTONIC'
20:49:21   157 |     body()
20:49:21   158 |     clock_gettime(CLOCK_MONOTONIC, &endTime)
20:49:21  
20:49:21  _time.CLOCK_MONOTONIC:2:12: note: found this candidate in module '_time'
20:49:21  1 | @available(macOS 10.12, *)
20:49:21  2 | public var CLOCK_MONOTONIC: clockid_t { get }
20:49:21    |            `- note: found this candidate in module '_time'
20:49:21  
20:49:21  _time.CLOCK_MONOTONIC:1:12: note: found this candidate in module '_time'
20:49:21  1 | public var CLOCK_MONOTONIC: clockid_t { get }
20:49:21    |            `- note: found this candidate in module '_time'
20:49:21

Looking at /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_time.h, we see:

typedef enum {
_CLOCK_REALTIME __CLOCK_AVAILABILITY = 0,
#define CLOCK_REALTIME _CLOCK_REALTIME
_CLOCK_MONOTONIC __CLOCK_AVAILABILITY = 6,
#define CLOCK_MONOTONIC _CLOCK_MONOTONIC
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
_CLOCK_MONOTONIC_RAW __CLOCK_AVAILABILITY = 4,
#define CLOCK_MONOTONIC_RAW _CLOCK_MONOTONIC_RAW
_CLOCK_MONOTONIC_RAW_APPROX __CLOCK_AVAILABILITY = 5,
#define CLOCK_MONOTONIC_RAW_APPROX _CLOCK_MONOTONIC_RAW_APPROX
_CLOCK_UPTIME_RAW __CLOCK_AVAILABILITY = 8,
#define CLOCK_UPTIME_RAW _CLOCK_UPTIME_RAW
_CLOCK_UPTIME_RAW_APPROX __CLOCK_AVAILABILITY = 9,
#define CLOCK_UPTIME_RAW_APPROX _CLOCK_UPTIME_RAW_APPROX
#endif
_CLOCK_PROCESS_CPUTIME_ID __CLOCK_AVAILABILITY = 12,
#define CLOCK_PROCESS_CPUTIME_ID _CLOCK_PROCESS_CPUTIME_ID
_CLOCK_THREAD_CPUTIME_ID __CLOCK_AVAILABILITY = 16
#define CLOCK_THREAD_CPUTIME_ID _CLOCK_THREAD_CPUTIME_ID
} clockid_t;

Looking at /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_time.apinotes, we see:

Name: _time
Enumerators:
- Name: _CLOCK_MONOTONIC
  SwiftName: CLOCK_MONOTONIC

With this change we would now synthesize the following:

var CLOCK_MONOTONIC: CInt {
  _CLOCK_MONOTONIC
}

But due to the APINotes, we would import _CLOCK_MONOTONIC as CLOCK_MONOTONIC, resulting in this clash.

@al45tair
Copy link
Contributor

al45tair commented Jun 6, 2025

With this change we would now synthesize the following:

var CLOCK_MONOTONIC: CInt {
  _CLOCK_MONOTONIC
}

But due to the APINotes, we would import _CLOCK_MONOTONIC as CLOCK_MONOTONIC, resulting in this clash.

:-(

It would be much easier if we could fix the compiler to tolerate that somehow, rather than having to change the API notes file. Would @_disfavouredOverload work to fix this? Or is that going to cause other problems. Alternatively, I wonder if we can look up in the API notes and ignore things that are already listed there?

@compnerd
Copy link
Member Author

compnerd commented Jun 6, 2025

@swift-ci please smoke test Linux platform

@hnrklssn
Copy link
Contributor

hnrklssn commented Jun 6, 2025

The APINotes have already been applied by clang before we import the nodes, right? It seems to me like we should be able to inspect the aliased clang decl and check for swift_name attributes that clash with the macro name.

@compnerd
Copy link
Member Author

compnerd commented Jun 6, 2025

@swift-ci please smoke test

@compnerd
Copy link
Member Author

compnerd commented Jun 6, 2025

@swift-ci please smoke test

@compnerd
Copy link
Member Author

compnerd commented Jun 7, 2025

@swift-ci please test windows platform

@compnerd
Copy link
Member Author

compnerd commented Jun 7, 2025

@swift-ci please smoke test macOS platform

3 similar comments
@compnerd
Copy link
Member Author

compnerd commented Jun 7, 2025

@swift-ci please smoke test macOS platform

@compnerd
Copy link
Member Author

compnerd commented Jun 7, 2025

@swift-ci please smoke test macOS platform

@compnerd
Copy link
Member Author

compnerd commented Jun 7, 2025

@swift-ci please smoke test macOS platform

compnerd added a commit to compnerd/swift-foundation-icu that referenced this pull request Jun 8, 2025
When building on non-Darwin targets, we are not guaranteed that there is
no ICU library on the system (Windows ships an ICU since Windows 1703
(RedStone 2), SDK 10.0.15063. This now is implicitly pulled in
OneCore.lib which vends the memory API contract; Linux normally ships a
copy of ICU; android ships a copy of ICU since Android 6.0 (API Level
23) but is not part of the supported APIs). The availability of ICU on
the system and in Swift causes a conflict. As there is no stable ABI for
ICU, this is a problem and can cause spurious failures. Such a failure
has been observed on Windows when statically linking Foundation.

Unfortunately, the symbol renaming support in ICU relies on the
`U_ICU_ENTRY_POINT_RENAME` macro which is defined thusly:

  ```
  #define U_DEF_ICU_ENTRY_POINT_RENAME(x, y, z) x ## y ## z
  #define U_DEF2_ICU_ENTRY_POINT_RENAME(x, y, z) U_DEF_ICU_ENTRY_POINT_RENAME(x, y, z)
  #define U_ICU_ENTRY_POINT_RENAME(name) U_DEF2_ICU_ENTRY_POINT_RENAME(name, ICU_VERSION_SUFFIX, U_LIB_SUFFIX_C_NAME)
  ```

This macro is too complex for the clang importer to import into Swift,
making this not possible to use directly. Instead, we manually perform
the expansion of the CPP macros, using the `swift_` prefix instead to
isolate and namespace our implementation.

Unfortunately, even the aliasing macro is too complex for the clang
importer to import directly. Because changing all the call sites would
not be tractable (nor maintainable), this will require an additional
change (swiftlang/swift#81840) to support aliasing macros to be imported
into Swift.

Although this change currently only enables the symbol renaming on
static builds, once everything is merged and tested, it should be safe
to control this behaviour on all non-Darwin targets. The change to the
static linking targets solves the immediate problem on Windows where the
early swift-driver implicitly will pick up the system ICU and fault at
runtime.

Although in theory only the dynamically linked version should be
susceptible to the interpositioning issue, if any of the system
libraries causes ICU to be included (e.g. a linker script on Unix or an
import library pulling in another version), we can also hit this issue
in the statically linked scenario. When dynamically linking, on Windows
at least, it would not be a problem if the symbol is bound properly at
link time as the symbolic resolution involves two-level namespacing,
which is not available on ELF hosts.

On Darwin, we cannot perform the renaming as the library is vended by
the system and this would constitute an ABI break.
compnerd added a commit to compnerd/swift-foundation-icu that referenced this pull request Jun 8, 2025
When building on non-Darwin targets, we are not guaranteed that there is
no ICU library on the system (Windows ships an ICU since Windows 1703
(RedStone 2), SDK 10.0.15063. This now is implicitly pulled in
OneCore.lib which vends the memory API contract; Linux normally ships a
copy of ICU; android ships a copy of ICU since Android 6.0 (API Level
23) but is not part of the supported APIs). The availability of ICU on
the system and in Swift causes a conflict. As there is no stable ABI for
ICU, this is a problem and can cause spurious failures. Such a failure
has been observed on Windows when statically linking Foundation.

Unfortunately, the symbol renaming support in ICU relies on the
`U_ICU_ENTRY_POINT_RENAME` macro which is defined thusly:

  ```
  #define U_DEF_ICU_ENTRY_POINT_RENAME(x, y, z) x ## y ## z
  #define U_DEF2_ICU_ENTRY_POINT_RENAME(x, y, z) U_DEF_ICU_ENTRY_POINT_RENAME(x, y, z)
  #define U_ICU_ENTRY_POINT_RENAME(name) U_DEF2_ICU_ENTRY_POINT_RENAME(name, ICU_VERSION_SUFFIX, U_LIB_SUFFIX_C_NAME)
  ```

This macro is too complex for the clang importer to import into Swift,
making this not possible to use directly. Instead, we manually perform
the expansion of the CPP macros, using the `swift_` prefix instead to
isolate and namespace our implementation.

Unfortunately, even the aliasing macro is too complex for the clang
importer to import directly. Because changing all the call sites would
not be tractable (nor maintainable), this will require an additional
change (swiftlang/swift#81840) to support aliasing macros to be imported
into Swift.

Although in theory only the dynamically linked version should be
susceptible to the interpositioning issue, if any of the system
libraries causes ICU to be included (e.g. a linker script on Unix or an
import library pulling in another version), we can also hit this issue
in the statically linked scenario. When dynamically linking, on Windows
at least, it would not be a problem if the symbol is bound properly at
link time as the symbolic resolution involves two-level namespacing,
which is not available on ELF hosts.

On Darwin, we cannot perform the renaming as the library is vended by
the system and this would constitute an ABI break.
@compnerd
Copy link
Member Author

compnerd commented Jun 8, 2025

@swift-ci please smoke test

@compnerd
Copy link
Member Author

compnerd commented Jun 8, 2025

@swift-ci please smoke test

Import simple CPP macro aliases as aliases in Swift. Extend the macro
importer to import the following construct:

  ```
  #define alias aliasee
  ```

as the following Swift construct:

  ```
  @_transparent @inline(__always)
  var alias: type(of: aliasee) {
    aliasee
  }
  ```

This improves the QoI for Windows where there is a universal define
(`UNICODE`) which normally is used for translating APIs between ANSI and
Unicode variants, e.g.:

  ```
  #if defined(UNICODE)
  #define MessageBox MessageBoxW
  #else
  #define MessageBox MessageBoxA
  #endif
  ```

Global variables which are non-const also have a setter synthesized:

  ```
  @_transparent @inline(__always)
  var alias: type(of: aliasee) {
    get { return aliasee }
    set { aliasee = newValue }
  }
  ```
@compnerd
Copy link
Member Author

compnerd commented Jun 8, 2025

@swift-ci please smoke test

@compnerd compnerd merged commit 33a324e into swiftlang:main Jun 9, 2025
3 checks passed
@compnerd compnerd deleted the aliases branch June 9, 2025 01:07
@kateinoigakukun
Copy link
Member

@compnerd I noticed that the latest nightly toolchain seems to have introduced an issue with import var Darwin.stderr on macOS SDK, and I guess this change might be related. Could you please take a look when you have a chance? Thanks in advance!

$ printf "import var Darwin.stderr\n" | /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-06-09-a.xctoolchain/usr/bin/swiftc -typecheck - -sdk $(xcrun --show-sdk-path)
<stdin>:1:12: error: ambiguous name 'stderr' in module 'Darwin'
1 | import var Darwin.stderr
  |            `- error: ambiguous name 'stderr' in module 'Darwin'
2 |

_stdio.stderr:2:12: note: found this candidate
1 | @available(macOS 10.9, iOS 7.0, watchOS 2.0, tvOS 9.0, visionOS 1.0, *)
2 | public var stderr: UnsafeMutablePointer<FILE> { get set }
  |            `- note: found this candidate

_stdio.stderr:1:12: note: found this candidate
1 | public var stderr: UnsafeMutablePointer<FILE> { get set }
  |            `- note: found this candidate

MaxDesiatov added a commit that referenced this pull request Jun 12, 2025
This reverts commit 33a324e, reversing
changes made to 0afeafa due to a regression:

```
$ printf "import var Darwin.stderr\n" | /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2025-06-09-a.xctoolchain/usr/bin/swiftc -typecheck - -sdk $(xcrun --show-sdk-path)
<stdin>:1:12: error: ambiguous name 'stderr' in module 'Darwin'
1 | import var Darwin.stderr
  |            `- error: ambiguous name 'stderr' in module 'Darwin'
2 |

_stdio.stderr:2:12: note: found this candidate
1 | @available(macOS 10.9, iOS 7.0, watchOS 2.0, tvOS 9.0, visionOS 1.0, *)
2 | public var stderr: UnsafeMutablePointer<FILE> { get set }
  |            `- note: found this candidate

_stdio.stderr:1:12: note: found this candidate
1 | public var stderr: UnsafeMutablePointer<FILE> { get set }
  |            `- note: found this candidate
```

# Conflicts:
#	test/ClangImporter/Inputs/custom-modules/module.modulemap
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c/c++ macros c++ interop Feature: Interoperability with C++ clang importer Area → compiler: The clang importer
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants