Skip to content

Commit bceb82e

Browse files
committed
Merge branch 'main' of github.com:apple/swift into maxd/main-merge
2 parents d100148 + 6d97905 commit bceb82e

File tree

54 files changed

+2223
-1118
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+2223
-1118
lines changed

SwiftCompilerSources/Sources/AST/DiagnosticEngine.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,17 @@ public struct DiagnosticEngine {
9393
}
9494
}
9595
}
96-
for arg in args {
96+
// 'reversed()' because the closure should be wrapped in that order.
97+
for arg in args.reversed() {
9798
closure = { [closure, arg] in
9899
arg._withBridgedDiagnosticArgument { bridgedArg in
99100
bridgedArgs.append(bridgedArg)
100101
closure()
101102
}
102103
}
103104
}
104-
for fixIt in fixIts {
105+
// 'reversed()' because the closure should be wrapped in that order.
106+
for fixIt in fixIts.reversed() {
105107
closure = { [closure, fixIt] in
106108
fixIt.withBridgedDiagnosticFixIt { bridgedFixIt in
107109
bridgedFixIts.append(bridgedFixIt)
File renamed without changes.
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# Getting started with C++ Interoperability
2+
3+
This document is desgined to get you started with bidirectional API-level interoperability between Swift and C++.
4+
5+
## Table of Contents
6+
7+
- [Creating a Module to contain your C++ source code](#creating-a-module-to-contain-your-c-source-code)
8+
- [Adding C++ to an Xcode project](#adding-c-to-an-xcode-project)
9+
- [Creating a Swift Package](#Creating-a-Swift-Package)
10+
- [Building with CMake](#building-with-cmake)
11+
12+
## Creating a Module to contain your C++ source code
13+
14+
- Create a new C++ implementation and header file
15+
- For this example we will call the files Cxx, so we should have a Cxx.cpp and Cxx.hpp.
16+
- Next create an empty file and call it `module.modulemap`, in this file create the module for your source code, and define your C++ header (`requires cplusplus` isn't required but it's convention for C++ modules, especially if they use C++ features).
17+
18+
```
19+
//In module.modulemap
20+
module Cxx {
21+
//note that your header should be the file that containts your method implementations
22+
header "Cxx.hpp"
23+
requires cplusplus
24+
}
25+
```
26+
27+
- Move the newly created files (Cxx.cpp, Cxx.hpp, module.modulemap) into a separate directory (this should remain in your project directory)
28+
29+
<img width="252" alt="Screen Shot 2022-02-26 at 9 14 06 PM" src="https://user-images.githubusercontent.com/62521716/155867937-9d9d6c62-4418-414d-bc4e-5d12c2055022.png">
30+
31+
## Adding C++ to an Xcode project
32+
- In your xcode project, follow the steps [Creating a Module to contain your C++ source code](#creating-a-module-to-contain-your-c-source-code) in your project directory
33+
34+
Add the C++ module to the include path and enable C++ interop:
35+
- Navigate to your project directory
36+
- In `Project` navigate to `Build Settings` -> `Swift Compiler`
37+
- Under `Custom Flags` -> `Other Swift Flags` add`-Xfrontend -enable-cxx-interop`
38+
- Under `Search Paths` -> `Import Paths` add your search path to the C++ module (i.e, `./ProjectName/Cxx`).
39+
40+
```
41+
//Add to Other Swift Flags and Import Paths respectively
42+
-Xfrontend -enable-cxx-interop
43+
-I./ProjectName/Cxx
44+
```
45+
46+
- This should now allow your to import your C++ Module into any `.swift` file.
47+
48+
```
49+
//In ViewController.swift
50+
import UIKit
51+
import Cxx
52+
53+
class ViewController: UIViewController {
54+
override func viewDidLoad() {
55+
super.viewDidLoad()
56+
let result = cxxFunction(7)
57+
print(result)
58+
}
59+
}
60+
```
61+
62+
```
63+
//In Cxx.cpp
64+
65+
#include "Cxx.hpp"
66+
int cxxFunction(int n) {
67+
return n;
68+
}
69+
70+
```
71+
72+
```
73+
//In Cxx.hpp
74+
75+
#ifndef Cxx_hpp
76+
#define Cxx_hpp
77+
78+
int cxxFunction(int n);
79+
80+
#endif
81+
82+
```
83+
84+
85+
## Creating a Swift Package
86+
After creating your Swift package project, follow the steps [Creating a Module to contain your C++ source code](#creating-a-module-to-contain-your-c-source-code) in your `Source` directory
87+
88+
- In your Package Manifest, you need to configure the Swift target's dependencies and compiler flags
89+
- In this example the name of the package is `Cxx_Interop`
90+
- Swift code will be in `Sources/Cxx_Interop` called `main.swift`
91+
- C++ source code follows the example shown in [Creating a Module to contain your C++ source code](#creating-a-module-to-contain-your-c-source-code)
92+
- Under targets, add the name of your C++ module and the directory containing the Swift code as a target.
93+
- In the target defining your Swift target, add a`dependencies` to the C++ Module, the `path`, `source`, and `swiftSettings` with `unsafeFlags` with the source to the C++ Module, and enable `-enable-cxx-interop`
94+
95+
```
96+
//In Package Manifest
97+
98+
import PackageDescription
99+
100+
let package = Package(
101+
name: "Cxx_Interop",
102+
platforms: [.macOS(.v12)],
103+
products: [
104+
.library(
105+
name: "Cxx",
106+
targets: ["Cxx"]),
107+
.library(
108+
name: "Cxx_Interop",
109+
targets: ["Cxx_Interop"]),
110+
],
111+
targets: [
112+
.target(
113+
name: "Cxx",
114+
dependencies: []
115+
),
116+
.executableTarget(
117+
name: "Cxx_Interop",
118+
dependencies: ["Cxx"],
119+
path: "./Sources/Cxx_Interop",
120+
sources: [ "main.swift" ],
121+
swiftSettings: [.unsafeFlags([
122+
"-I", "Sources/Cxx",
123+
"-Xfrontend", "-enable-cxx-interop",
124+
])]
125+
),
126+
]
127+
)
128+
129+
```
130+
131+
- We are now able to import our C++ Module into our swift code, and import the package into existing projects
132+
133+
```
134+
//In main.swift
135+
136+
import Cxx
137+
138+
public struct Cxx_Interop {
139+
140+
public func callCxxFunction(n: Int32) -> Int32 {
141+
return cxxFunction(n: n)
142+
}
143+
}
144+
145+
print(Cxx_Interop().callCxxFunction(n: 7))
146+
//outputs: 7
147+
148+
```
149+
150+
## Building with CMake
151+
After creating your project follow the steps [Creating a Module to contain your C++ source code](#creating-a-module-to-contain-your-c-source-code)
152+
153+
- Create a `CMakeLists.txt` file and configure for your project
154+
- In`add_library` invoke `cxx-support` with the path to the C++ implementation file
155+
- Add the `target_include_directories` with `cxx-support` and path to the C++ Module `${CMAKE_SOURCE_DIR}/Sources/Cxx`
156+
- Add the `add_executable` to the specific files/directory you would like to generate source, with`SHELL:-Xfrontend -enable-cxx-interop`.
157+
- In the example below we will be following the file structure used in [Creating a Swift Package](#Creating-a-Swift-Package)
158+
159+
```
160+
//In CMakeLists.txt
161+
162+
cmake_minimum_required(VERSION 3.18)
163+
164+
project(Cxx_Interop LANGUAGES CXX Swift)
165+
166+
set(CMAKE_CXX_STANDARD 11)
167+
set(CMAKE_CXX_STANDARD_REQUIRED YES)
168+
set(CMAKE_CXX_EXTENSIONS OFF)
169+
170+
add_library(cxx-support ./Sources/Cxx/Cxx.cpp)
171+
target_compile_options(cxx-support PRIVATE
172+
-I${SWIFT_CXX_TOOLCHAIN}/usr/include/c++/v1
173+
-fno-exceptions
174+
-fignore-exceptions
175+
-nostdinc++)
176+
target_include_directories(cxx-support PUBLIC
177+
${CMAKE_SOURCE_DIR}/Sources/Cxx)
178+
179+
add_executable(Cxx_Interop ./Sources/Cxx_Interop/main.swift)
180+
target_compile_options(Cxx_Interop PRIVATE
181+
"SHELL:-Xfrontend -enable-cxx-interop"
182+
target_link_libraries(Cxx_Interop PRIVATE cxx-support)
183+
184+
```
185+
186+
```
187+
//In main.swift
188+
189+
import Cxx
190+
191+
public struct Cxx_Interop {
192+
public static func main() {
193+
let result = cxxFunction(7)
194+
print(result)
195+
}
196+
}
197+
198+
Cxx_Interop.main()
199+
200+
```
201+
202+
- In your projects direcetoy, run `cmake` to generate the systems build files
203+
204+
- To generate an Xcode project run `cmake -GXcode`
205+
- To generate with Ninja run `cmake -GNinja`
206+
207+
- For more information on `cmake` see the 'GettingStarted' documentation: (https://github.com/apple/swift/blob/main/docs/HowToGuides/GettingStarted.md)
208+
209+

include/swift/AST/DiagnosticsParse.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ ERROR(initializer_has_name,PointsToFirstBadToken,
424424

425425
// Destructor
426426
ERROR(destructor_decl_outside_class,none,
427-
"deinitializers may only be declared within a class", ())
427+
"deinitializers may only be declared within a class or actor", ())
428428
ERROR(expected_lbrace_destructor,PointsToFirstBadToken,
429429
"expected '{' for deinitializer", ())
430430
ERROR(destructor_has_name,PointsToFirstBadToken,
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#ifndef SWIFT_CXXMETHODBRIDGING_H
2+
#define SWIFT_CXXMETHODBRIDGING_H
3+
4+
#include "swift/AST/Decl.h"
5+
#include "clang/AST/DeclCXX.h"
6+
#include "llvm/ADT/StringRef.h"
7+
8+
#include <string>
9+
namespace swift {
10+
struct CXXMethodBridging {
11+
enum class Kind { unkown, getter, setter, subscript };
12+
13+
enum class NameKind { unkown, snake, lower, camel, title };
14+
15+
CXXMethodBridging(const clang::CXXMethodDecl *method) : method(method) {}
16+
17+
Kind classify() {
18+
if (nameIsBlacklist())
19+
return Kind::unkown;
20+
21+
// this should be handled as snake case. See: rdar://89453010
22+
// case. In the future we could
23+
// import these too, though.
24+
auto nameKind = classifyNameKind();
25+
if (nameKind != NameKind::title && nameKind != NameKind::camel &&
26+
nameKind != NameKind::lower)
27+
return Kind::unkown;
28+
29+
if (getClangName().startswith_insensitive("set")) {
30+
// Setters only have one parameter.
31+
if (method->getNumParams() != 1)
32+
return Kind::unkown;
33+
34+
// rdar://89453106 (We need to handle imported properties that return a
35+
// reference)
36+
if (method->getParamDecl(0)->getType()->isReferenceType())
37+
return Kind::unkown;
38+
39+
return Kind::setter;
40+
}
41+
42+
// Getters and subscripts cannot return void.
43+
if (method->getReturnType()->isVoidType())
44+
return Kind::unkown;
45+
46+
if (getClangName().startswith_insensitive("get")) {
47+
// Getters cannot take arguments.
48+
if (method->getNumParams() != 0)
49+
return Kind::unkown;
50+
51+
// rdar://89453106 (We need to handle imported properties that return a
52+
// reference)
53+
if (method->getReturnType()->isReferenceType())
54+
return Kind::unkown;
55+
56+
return Kind::getter;
57+
}
58+
59+
// rdar://89453187 (Add subscripts clarification to CXXMethod Bridging to
60+
// clean up importDecl)
61+
return Kind::unkown;
62+
}
63+
64+
NameKind classifyNameKind() {
65+
bool allLower = llvm::all_of(getClangName(), islower);
66+
67+
if (getClangName().empty())
68+
return NameKind::unkown;
69+
70+
if (getClangName().contains('_'))
71+
return allLower ? NameKind::snake : NameKind::unkown;
72+
73+
if (allLower)
74+
return NameKind::lower;
75+
76+
return islower(getClangName().front()) ? NameKind::camel : NameKind::title;
77+
}
78+
79+
llvm::StringRef getClangName() {
80+
if (!method->getDeclName().isIdentifier())
81+
return "";
82+
83+
return method->getName();
84+
}
85+
86+
// this should be handled as snake case. See: rdar://89453010
87+
std::string importNameAsCamelCaseName() {
88+
std::string output;
89+
auto kind = classify();
90+
if (kind == Kind::getter || kind == Kind::setter) {
91+
output = getClangName().drop_front(3).str();
92+
} else {
93+
output = getClangName().str();
94+
}
95+
96+
if (output.empty())
97+
return output;
98+
99+
// No work to do.
100+
if (classifyNameKind() == NameKind::lower)
101+
return output;
102+
103+
// The first character is always lowercase.
104+
output.front() = std::tolower(output.front());
105+
106+
// We already lowercased the first element, so start at one. Look at the
107+
// current element and the next one. To handle cases like UTF8String, start
108+
// making all the uppercase characters lower, until we see an upper case
109+
// character followed by a lower case character (i.e., "St").
110+
for (size_t i = 1; i < output.size(); i++) {
111+
size_t next = i + 1;
112+
113+
// If we see two upper case characters (or an upper case character and a
114+
// number) make the current character lower case.
115+
if (std::isupper(output[i]) &&
116+
(std::isupper(output[next]) || std::isdigit(output[next]))) {
117+
output[i] = std::tolower(output[i]);
118+
// If we found an upper case character followed by a lower case
119+
// character, we went far enough. We're done.
120+
} else if (std::isupper(output[i]) && std::islower(output[next])) {
121+
break;
122+
// If we got to the end of the string, we're done.
123+
} else if (std::isupper(output[i]) && next + 1 > output.size()) {
124+
output[i] = std::tolower(output[i]);
125+
break;
126+
}
127+
}
128+
129+
return output;
130+
}
131+
132+
std::string importNameAsTitleCaseName() {
133+
auto output = importNameAsCamelCaseName();
134+
output.front() = std::toupper(output.front());
135+
return output;
136+
}
137+
138+
private:
139+
const clang::CXXMethodDecl *method = nullptr;
140+
141+
bool nameIsBlacklist() {
142+
auto loweredName = getClangName().lower();
143+
// Names that start with "get" or "set" but aren't getters or setters.
144+
return loweredName == "getter" || loweredName == "setter" ||
145+
loweredName == "get" || loweredName == "set";
146+
}
147+
};
148+
} // namespace swift
149+
#endif // SWIFT_CXXMETHODBRIDGING_H

0 commit comments

Comments
 (0)