Skip to content

Commit 61d73d2

Browse files
authored
Merge pull request #67523 from hyp/eng/compat-version
[cxx-interop] add 'upcoming-swift' C++ interop compat version
2 parents 9865e82 + 5dc5f49 commit 61d73d2

File tree

7 files changed

+160
-10
lines changed

7 files changed

+160
-10
lines changed
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Swift and C++ interop: Guide for Swift contributors
2+
3+
This document describes how to contribute changes to Swift
4+
and C++ interoperability support in the Swift compiler.
5+
The recommended way to contribute touches on topics
6+
like source stability of C++ interop, making breaking
7+
changes, and compatibility testing.
8+
9+
Here's a short summary of how you should contribute
10+
to Swift and C++ interop:
11+
- Treat every change as potentially source breaking, unless proven otherwise.
12+
- Guard any potentially source breaking change with appropriate interop compat version checks.
13+
- Explicitly specify the interop compat version in your test when adding breaking changes.
14+
- Ensure that your GitHub PR is approved by one of the code owners before merging.
15+
16+
## Changing Swift and C++ interop
17+
18+
The initial support for Swift and C++ interop in Swift 5.9
19+
is considered to be source stable, as in a future compiler
20+
like Swift 5.10
21+
must build any Swift code that uses C++ interop
22+
and was previously built with Swift 5.9 compiler without
23+
forcing the user to make **any** changes to their code.
24+
Because of that promise that we're making to our users,
25+
we need to be very diligent when it comes to making
26+
changes to how Swift and C++ interop work, as they could
27+
potentially violate this promise. Thus it's recommended
28+
that you treat every
29+
change as source breaking, unless proven otherwise.
30+
31+
### What is C++ Interop Compat Version
32+
33+
C++ interop compat version is the Swift version
34+
value given to the `-cxx-interoperability-mode=` Swift
35+
command line option.
36+
37+
It supports values like:
38+
- `default`. Corresponds to the currently used Swift language version.
39+
For instance, by default for Swift 5 compiler the C++ interop compat version
40+
will be 5 when `-cxx-interoperability-mode=default` is passed. However,
41+
if you pass `-cxx-interoperability-mode=default` and `-swift-version 6`,
42+
then the C++ interop compat version becomes 6.
43+
44+
- `swift-X.Y`. A specific version like Swift 5.9.
45+
46+
- `upcoming-swift`. Corresponds to the C++ interop compat version that
47+
represents the development *unreleased* version of Swift and C++ interoperability.
48+
All new source breaking changes must be guarded by this version first
49+
before they migrate to a specific *released* version of Swift and C++ interoperability.
50+
51+
### Guarding Source Breaking Changes in Compiler Implementation
52+
53+
Any source breaking change must be guarded with an appropriate
54+
interop compat version check. Swift's `LangOptions` have
55+
a `cxxInteropCompatVersion` member that is the interop compat version.
56+
57+
You can use the `isCxxInteropCompatVersionAtLeast` check to validate that
58+
you're building for the upcoming version of C++ and Swift interop.
59+
For example, in the clang importer, you can call this check method on
60+
the `Impl`:
61+
62+
```
63+
// Inside of Clang Importer (Impl is `ClangImporter::Impl`)
64+
if (Impl.isCxxInteropCompatVersionAtLeast(version::getUpcomingCxxInteropCompatVersion())) {
65+
// ... breaking change ...
66+
}
67+
```
68+
69+
Elsewhere, this method can be called on the `LangOptoins` themselves:
70+
71+
```
72+
if (SwiftContext.LangOpts.isCxxInteropCompatVersionAtLeast(version::getUpcomingCxxInteropCompatVersion())) {
73+
// ... breaking change ...
74+
}
75+
```
76+
77+
### Testing the change
78+
79+
Please add new tests under `test/Interop/Cxx`, or
80+
modify existing tests there.
81+
82+
Any tests for source breaking changes
83+
should invoke the Swift compiler with
84+
the `-cxx-interoperability-mode=upcoming-swift`
85+
option.
86+
87+
## Submitting the Change as PR on GitHub
88+
89+
GitHub PR checklist for C++ interop related changes:
90+
- Please tag your PR with 'C++ Interop' tag.
91+
- Code owners should be added for review automatically.
92+
- Please ensure that one of the current code owners reviews and approves the PR before merging.
93+
94+
## Releasing a new Swift and C++ Interop Compat Version
95+
96+
You need to update all Swift
97+
sources to migrate these checks `isCxxInteropCompatVersionAtLeast(version::getUpcomingCxxInteropCompatVersion())`
98+
to the concrete version you're now releasing, for instance `isCxxInteropCompatVersionAtLeast(5, 10)`.
99+
100+
There are more things we'll need to do but we haven't released a new interop compat version yet,
101+
so this document will be updated once we do so.

include/swift/Basic/LangOptions.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,10 @@ namespace swift {
310310
/// disabled because it is not complete.
311311
bool EnableCXXInterop = false;
312312

313+
/// The C++ interoperability source compatibility version. Defaults
314+
/// to the Swift language version.
315+
version::Version cxxInteropCompatVersion;
316+
313317
bool CForeignReferenceTypes = false;
314318

315319
/// Imports getters and setters as computed properties.
@@ -655,6 +659,13 @@ namespace swift {
655659
return EffectiveLanguageVersion.isVersionAtLeast(major, minor);
656660
}
657661

662+
/// Whether the C++ interoperability compatibility version is at least
663+
/// 'major'.
664+
bool isCxxInteropCompatVersionAtLeast(unsigned major,
665+
unsigned minor = 0) const {
666+
return cxxInteropCompatVersion.isVersionAtLeast(major, minor);
667+
}
668+
658669
/// Determine whether the given feature is enabled.
659670
bool hasFeature(Feature feature) const;
660671

include/swift/Basic/Version.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,11 @@ StringRef getCurrentCompilerTag();
175175
/// depending on the vendor.
176176
StringRef getCurrentCompilerSerializationTag();
177177

178+
/// Retrieves the value of the upcoming C++ interoperability compatibility
179+
/// version that's going to be presented as some new concrete version to the
180+
/// users.
181+
unsigned getUpcomingCxxInteropCompatVersion();
182+
178183
} // end namespace version
179184
} // end namespace swift
180185

lib/Basic/Version.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,5 +306,9 @@ StringRef getCurrentCompilerSerializationTag() {
306306
#endif
307307
}
308308

309+
unsigned getUpcomingCxxInteropCompatVersion() {
310+
return SWIFT_VERSION_MAJOR + 1;
311+
}
312+
309313
} // end namespace version
310314
} // end namespace swift

lib/ClangImporter/ImporterImpl.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,17 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
589589
return Instance.get();
590590
}
591591

592+
/// Whether the C++ interoperability compatibility version is at least
593+
/// 'major'.
594+
///
595+
/// Use the
596+
/// `isCxxInteropCompatVersionAtLeast(version::getUpcomingCxxInteropCompatVersion())`
597+
/// check when making a source breaking C++ interop change.
598+
bool isCxxInteropCompatVersionAtLeast(unsigned major,
599+
unsigned minor = 0) const {
600+
return SwiftContext.LangOpts.isCxxInteropCompatVersionAtLeast(major, minor);
601+
}
602+
592603
private:
593604
/// The Importer may be configured to load modules of a different OS Version
594605
/// than the underlying Swift compilation. This is the `TargetOptions`

lib/Frontend/CompilerInvocation.cpp

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -486,15 +486,20 @@ enum class CxxCompatMode {
486486
off
487487
};
488488

489-
static CxxCompatMode validateCxxInteropCompatibilityMode(StringRef mode) {
489+
static std::pair<CxxCompatMode, version::Version>
490+
validateCxxInteropCompatibilityMode(StringRef mode) {
490491
if (mode == "off")
491-
return CxxCompatMode::off;
492+
return {CxxCompatMode::off, {}};
492493
if (mode == "default")
493-
return CxxCompatMode::enabled;
494-
// FIXME: Drop swift-5.9.
494+
return {CxxCompatMode::enabled, {}};
495+
if (mode == "upcoming-swift")
496+
return {CxxCompatMode::enabled,
497+
version::Version({version::getUpcomingCxxInteropCompatVersion()})};
498+
// Swift-5.9 corresponds to the Swift 5 language mode when
499+
// Swift 5 is the default language version.
495500
if (mode == "swift-5.9")
496-
return CxxCompatMode::enabled;
497-
return CxxCompatMode::invalid;
501+
return {CxxCompatMode::enabled, version::Version({5})};
502+
return {CxxCompatMode::invalid, {}};
498503
}
499504

500505
static void diagnoseCxxInteropCompatMode(Arg *verArg, ArgList &Args,
@@ -1066,16 +1071,29 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
10661071
}
10671072

10681073
auto interopCompatMode = validateCxxInteropCompatibilityMode(A->getValue());
1069-
Opts.EnableCXXInterop |= (interopCompatMode == CxxCompatMode::enabled);
1074+
Opts.EnableCXXInterop |=
1075+
(interopCompatMode.first == CxxCompatMode::enabled);
1076+
if (Opts.EnableCXXInterop) {
1077+
Opts.cxxInteropCompatVersion = interopCompatMode.second;
1078+
// The default is tied to the current language version.
1079+
if (Opts.cxxInteropCompatVersion.empty())
1080+
Opts.cxxInteropCompatVersion =
1081+
Opts.EffectiveLanguageVersion.asMajorVersion();
1082+
}
10701083

1071-
if (interopCompatMode == CxxCompatMode::invalid)
1084+
if (interopCompatMode.first == CxxCompatMode::invalid)
10721085
diagnoseCxxInteropCompatMode(A, Args, Diags);
10731086
}
1074-
1087+
10751088
if (Args.hasArg(OPT_enable_experimental_cxx_interop)) {
10761089
Diags.diagnose(SourceLoc(), diag::enable_interop_flag_deprecated);
10771090
Diags.diagnose(SourceLoc(), diag::swift_will_maintain_compat);
10781091
Opts.EnableCXXInterop |= true;
1092+
// Using the deprecated option only forces the 'swift-5.9' compat
1093+
// mode.
1094+
if (Opts.cxxInteropCompatVersion.empty())
1095+
Opts.cxxInteropCompatVersion =
1096+
validateCxxInteropCompatibilityMode("swift-5.9").second;
10791097
}
10801098

10811099
Opts.EnableObjCInterop =

test/Interop/Cxx/driver/invalid-interop-compat-mode.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
// RUN: split-file %s %t
33
// RUN: not %target-swift-frontend -typecheck -I %t/Inputs %t/test.swift -cxx-interoperability-mode=swift-5.8 2>&1 | %FileCheck %s
44

5-
// Note: swift-5.9 is still supported, but will be removed.
65
// RUN: %target-swift-frontend -typecheck -I %t/Inputs %t/test.swift -cxx-interoperability-mode=swift-5.9
6+
// RUN: %target-swift-frontend -typecheck -I %t/Inputs %t/test.swift -cxx-interoperability-mode=upcoming-swift
77

88
//--- Inputs/module.modulemap
99
module Test {

0 commit comments

Comments
 (0)