# Feature Proposal: Hiding Public Functions from C++

**URL:** <https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301>\
**Category:** C++ interoperability\
**Tags:** swift-evolution, cxx-interop\
**Created:** [July 25, 2025, 3:39pm UTC](https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301 "2025-07-25T15:39:08Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![Xazax-hun](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xazax-hun/32/36110_2.png) [@Xazax-hun](https://forums.swift.org/u/Xazax-hun)\
**Post date:** [July 25, 2025, 3:39pm UTC](https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301/1 "2025-07-25T15:39:08Z")

</div>

Hey all,

Swift supports bidirectional interoperability with C++. We can use many C++ entities (like functions or types) from Swift and many Swift entities from C++. There are two modes to expose Swift entities to C++. We either expose declarations that are annotated with `@_expose(Cxx)` or all publicly visible declarations. The latter is the default in the compiler.

We started to see a need for hiding a public Swift type or API from C++. The main motivation is to avoid name collisions when the C++ code already defines a type with the same name that exists on the Swift side too.

We propose introducing an `@_expose(!Cxx)` annotation to hide Swift declarations from C++. I opened a PR to implement this: [[cxx-interop] Add attribute to hide Swift declarations from interop by Xazax-hun · Pull Request #82616 · swiftlang/swift · GitHub](https://github.com/swiftlang/swift/pull/82616)

Before committing to this direction, we wanted to check if the community is on board with this solution or there are alternative suggestions for the spelling (or a different way to hide declarations).

---

<div class="post-metadata">

**Author:** ![compnerd](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/compnerd/32/2165_2.png) [@compnerd](https://forums.swift.org/u/compnerd)\
**Post date:** [July 25, 2025, 4:36pm UTC](https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301/2 "2025-07-25T16:36:47Z")

</div>

> [@Xazax-hun](#):
>
> there are alternative suggestions for the spelling

I would seriously consider `~cxx` as that mirrors the `~Copyable` syntax that we already have for protocol negation.

> [@Xazax-hun](#):
>
> or a different way to hide declarations

I think that having some more details on the experience here would be helpful. Was it the norm that you wanted to prevent the interface from being visible or was it the exception? If it is the latter, I think that we should consider the inverted attribution (`@swift` or some other spelling) to indicate that the interface is limited to Swift. If it is that the norm, then it makes sense to attribute the interfaces that should be visible.

Another option to consider - if it is to deal with collisions, perhaps we should introduce the reverse mapping for expose to C++ with an alternate name. We already do this today for importing C/C++ types where they are renamed via APINotes or via bodyless declarators in Swift.

---

<div class="post-metadata">

**Author:** ![Joe\_Groff](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joe_groff/32/1929_2.png) [@Joe\_Groff](https://forums.swift.org/u/Joe_Groff)\
**Post date:** [July 25, 2025, 4:48pm UTC](https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301/3 "2025-07-25T16:48:11Z")

</div>

> [@compnerd](#):
>
> I would seriously consider `~cxx` as that mirrors the `~Copyable` syntax that we already have for protocol negation.

`~` on generic requirements does not mean "is not", it means "is not _required to be_". A `~Copyable` type may be conditionally copyable, or it could be a generic parameter bound to a `Copyable` implementation. That does not seem applicable here.

---

<div class="post-metadata">

**Author:** ![Xazax-hun](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xazax-hun/32/36110_2.png) [@Xazax-hun](https://forums.swift.org/u/Xazax-hun)\
**Post date:** [July 25, 2025, 4:49pm UTC](https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301/4 "2025-07-25T16:49:39Z")

</div>

Thanks, these are good points!

> [@compnerd](#):
>
> Was it the norm that you wanted to prevent the interface from being visible or was it the exception?

In the cases I am aware of it was the exception.

> [@compnerd](#):
>
> If it is that the norm, then it makes sense to attribute the interfaces that should be visible.

We do support that under the `-clang-header-expose-decls=has-expose-attr` frontend option. We currently do not have the corresponding driver flag but did not see much demand for this mode to be exposed more widely.

> [@compnerd](#):
>
> if it is to deal with collisions, perhaps we should introduce the reverse mapping for expose to C++ with an alternate name.

We actually support that under the syntax `@_expose(Cxx, "CxxName")`. That being said I did see cases where the users just preferred to hide the Swift declaration from C++ over renaming it. Another motivation for hiding is to temporarily work around bugs. E.g., if some declarations make the compiler generate a reverse interop header that would not compile. Fortunately, these are becoming more rare these days but it is still nice to be able to give people workarounds until a fix is released when this happens.

---

<div class="post-metadata">

**Author:** ![Xazax-hun](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xazax-hun/32/36110_2.png) [@Xazax-hun](https://forums.swift.org/u/Xazax-hun)\
**Post date:** [July 25, 2025, 4:53pm UTC](https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301/5 "2025-07-25T16:53:57Z")

</div>

> [@Xazax-hun](#):
>
> We actually support that under the syntax `@_expose(Cxx, "CxxName")`

Oh, I just realized there are also some holes in the support here. Sometimes we generate multiple names for a single entity. E.g., a property is exposed to C++ via `getProperty` and `setProperty` methods. These cannot be renamed using `@_expose` at the moment because it only provides one name.

---

<div class="post-metadata">

**Author:** ![ksluder](https://avatars.discourse-cdn.com/v4/letter/k/eada6e/32.png) [@ksluder](https://forums.swift.org/u/ksluder)\
**Post date:** [July 25, 2025, 5:19pm UTC](https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301/6 "2025-07-25T17:19:12Z")

</div>

There is a [long](https://forums.swift.org/t/formalizing-cdecl/40677)-[documented](https://forums.swift.org/t/from-cdecl-to-cdecl/77696) [need](https://forums.swift.org/t/pitch-formalize-cdecl/79557) for official support to control how Swift exposes types to other languages, especially structs that are shared with C and C++. It would be unfortunate to formalize a C++-specific `@expose` keyword without considering these other long-standing use cases.

In the meantime, how about `@available(cxx, unavailable)`? (Edit: or does this already map to something like `= deleted`?)

---

<div class="post-metadata">

**Author:** ![Hacksaw](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/hacksaw/32/32775_2.png) [@Hacksaw](https://forums.swift.org/u/Hacksaw)\
**Post date:** [July 26, 2025, 4:41am UTC](https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301/7 "2025-07-26T04:41:43Z")

</div>

Could one then do `@_expose(Cxx, "")` to hide something?

---

<div class="post-metadata">

**Author:** ![Xazax-hun](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xazax-hun/32/36110_2.png) [@Xazax-hun](https://forums.swift.org/u/Xazax-hun)\
**Post date:** [July 29, 2025, 3:30pm UTC](https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301/8 "2025-07-29T15:30:11Z")

</div>

Thanks for all the suggestions, we plan to discuss this later this week on the C++ Interoperability Workgroup meeting: [📅 C++ interoperability workgroup: upcoming meetings - #71 by egor.zhdan](https://forums.swift.org/t/c-interoperability-workgroup-upcoming-meetings/63016/71)

Feel free to join!

> [@ksluder](#):
>
> Swift exposes types to other languages, especially structs that are shared with C and C++

Good point. Do you know use cases where someone wants to expose a type to C but not to C++? I wonder if we do need this granularity. Also, do we have this problem with C? The reason why we have this problem with C++ because by default we expose all the public Swift declarations we can to C++. On the other hand, `@cdecl` is opt-in so I am not 100% sure if we need the equivalent functionality in C.

> [@ksluder](#):
>
> In the meantime, how about `@available(cxx, unavailable)`?

This is also an interesting suggestion, but this would be a way bigger change. We might need to go through Swift evolution. `@_expose` is underscored, so we can use it to test an implementation out without having to go through the full evolution process.

> [@Hacksaw](#):
>
> Could one then do `@_expose(Cxx, "")` to hide something?

I think this could work. The main concern here is the discoverability. But I think that could be mitigated via good documentation.

---

<div class="post-metadata">

**Author:** ![mrousavy](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mrousavy/32/42302_2.png) [@mrousavy](https://forums.swift.org/u/mrousavy)\
**Post date:** [July 29, 2025, 4:19pm UTC](https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301/9 "2025-07-29T16:19:18Z")

</div>

After some quick testing, `_expose(Cxx, "")` still tries to generate bridging code for said Swift class in the autogenerated `-Swift.h` header.  
I was trying to hide a Swift class from C++ because it failed to build and I don't need it available from C++ anyways..

---

<div class="post-metadata">

**Author:** ![zoecarver](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/zoecarver/32/21535_2.png) [@zoecarver](https://forums.swift.org/u/zoecarver)\
**Post date:** [July 29, 2025, 4:57pm UTC](https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301/10 "2025-07-29T16:57:16Z")

</div>

> [@ksluder](#):
>
> `@available(cxx, unavailable)`

I like this and it sort of matches the macro strategy used in clang with marking decls as unavailable in Swift with `[[unavailable]]`.

> [@ksluder](#):
>
> or does this already map to something like `= deleted`?

This is sort of unrelated and pedantic, but I don't think we should map anything to `= deleted` because it will trigger a compiler error if the overload is selected, and I doubt that's often the intent of an API author. I imagine a common use case for this is disabling overloads that don't export well to C++, and it might be common to have another overload that can also be selected in C++ (but is a worse match from overload resolution's perspective).

---

<div class="post-metadata">

**Author:** ![ksluder](https://avatars.discourse-cdn.com/v4/letter/k/eada6e/32.png) [@ksluder](https://forums.swift.org/u/ksluder)\
**Post date:** [July 29, 2025, 5:20pm UTC](https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301/11 "2025-07-29T17:20:52Z")

</div>

> [@Xazax-hun](#):
>
> Do you know use cases where someone wants to expose a type to C but not to C++? I wonder if we do need this granularity.

I’m sure someone somewhere will have a use case for exposing something to C++ but not C (or vice versa). Personally, it will be more common that I’ll want to ensure the C++ projection of a struct is `extern "C"` so that I can guarantee its layout is consistent when imported in ObjC, C++, and Metal source files.

> [@zoecarver](#):
>
> This is sort of unrelated and pedantic, but I don't think we should map anything to `= deleted` because it will trigger a compiler error if the overload is selected, and I doubt that's often the intent of an API author.

I mentioned `= delete` because it’s unclear to me how `[[unavailable]]` members affect overload resolution. Does Clang treat them as absent, implicitly deleted, explicitly deleted, or present but uncallable? All four of these options might be desirable for any given C++ projection, based on how they play with SFINAE and type deduction.

---

<div class="post-metadata">

**Author:** ![tshortli](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tshortli/32/15850_2.png) [@tshortli](https://forums.swift.org/u/tshortli)\
**Post date:** [July 29, 2025, 10:55pm UTC](https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301/12 "2025-07-29T22:55:11Z")

</div>

`@available` has many responsibilities, and I could see it gaining this one but I don't find it to be a very intuitive fit and I'd personally want to explore other solutions first. It's not a good fit because `@available` generally doesn't have the effect of hiding declarations entirely; instead, it constraints which regions of code can reference a declaration. If a syntax like `@available(cxx, unavailable)` already existed I would expect it to cause the declaration to be emitted with ` __attribute__ ((unavailable))` when it is printed in headers for C++ compatibility, not for it to be omitted from those headers entirely.

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [August 6, 2025, 11:01am UTC](https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301/13 "2025-08-06T11:01:23Z")

</div>

Interesting topic, and something we'll run into with Java interop as well eventually I guess, though we've not much clashes yet, it may be an useful thing to be able to not export some public decls to java 🤔 Since java interop isn't part of the Swift source tree it's not really using @\_expose, but I could totally see it using the same pattern here `@_expose(Java)` etc could be a nice way to expose a func that is for example `internal` but we're only extracting `public` funcs by default... So yeah, I'll keep an eye on how this proposal evolves for sure.

I get Allan's concern about available but it still seems quite tempting, but perhaps conflating too many things. At the same time, `@_expose(!Cxx)` feels a bit magical "what does not C++ mean?".

So one idea that came to mind is sticking to `@_expose` however use the `unavailable` word with it, perhaps `@_expose(Cxx, unavailable)` could be an interesting spelling for it.

---

<div class="post-metadata">

**Author:** ![Xazax-hun](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xazax-hun/32/36110_2.png) [@Xazax-hun](https://forums.swift.org/u/Xazax-hun)\
**Post date:** [August 18, 2025, 9:49am UTC](https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301/14 "2025-08-18T09:49:43Z")

</div>

Thanks for all the responses and ideas!

Currently, it looks like we will go ahead with `@_expose(!Cxx)`. Looks like this can be generalized to interop with other languages. While `!Cxx` might feel a bit magical, it looks like all the other spellings have potentially alternative confusing interpretations. `@_expose(Cxx, unavailable)` might give the impression we still generate the declaration but with an availability annotation. `@_expose(Cxx, ““)` might not be clear for the reader unless they read the documentation for the attribute. `~` might be confusing because it has a different meaning in generic context. Reusing the `availability` annotations have similar concerns to using `@_expose(Cxx, unavailable)`.

In case anyone has any strong objections please let us know. I plan to merge the PR next week if nothing comes up.

---

<div class="post-metadata">

**Author:** ![compnerd](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/compnerd/32/2165_2.png) [@compnerd](https://forums.swift.org/u/compnerd)\
**Post date:** [August 18, 2025, 4:47pm UTC](https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301/15 "2025-08-18T16:47:43Z")

</div>

What about comparability? It feels a bit less composable if we want to mark it unavailable in multiple languages. Would you then use `&&` to join the languages?

---

<div class="post-metadata">

**Author:** ![Xazax-hun](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xazax-hun/32/36110_2.png) [@Xazax-hun](https://forums.swift.org/u/Xazax-hun)\
**Post date:** [August 18, 2025, 4:58pm UTC](https://forums.swift.org/t/feature-proposal-hiding-public-functions-from-c/81301/16 "2025-08-18T16:58:56Z")

</div>

> [@compnerd](#):
>
> It feels a bit less composable if we want to mark it unavailable in multiple languages.

We currently need to do `@_expose(Cxx) @_expose(wasm)` at the moment. So I think it would work similarly for the hiding. One of the reasons why it is used like this for the positive case is the optional second argument. The same Swift function can be exported to different languages using different names this way.
