Skip to content

Commit a66cc52

Browse files
committed
[interop][SwiftToCxx] add static_assert type checking for generic functions in pre-C++20 mode
In the future we should also use enable_if to correctly resolve overloads
1 parent 4b52ae0 commit a66cc52

11 files changed

+182
-9
lines changed

lib/PrintAsClang/ClangSyntaxPrinter.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@ bool ClangSyntaxPrinter::printNominalTypeOutsideMemberDeclTemplateSpecifiers(
8383
return false;
8484
}
8585

86+
bool ClangSyntaxPrinter::printNominalTypeOutsideMemberDeclInnerStaticAssert(
87+
const NominalTypeDecl *typeDecl) {
88+
if (!typeDecl->isGeneric())
89+
return true;
90+
printGenericSignatureInnerStaticAsserts(
91+
typeDecl->getGenericSignature().getCanonicalSignature());
92+
return false;
93+
}
94+
8695
void ClangSyntaxPrinter::printNominalClangTypeReference(
8796
const clang::Decl *typeDecl) {
8897
auto &clangCtx = typeDecl->getASTContext();
@@ -297,6 +306,20 @@ void ClangSyntaxPrinter::printGenericSignature(
297306
os << "\n#endif // __cpp_concepts\n";
298307
}
299308

309+
void ClangSyntaxPrinter::printGenericSignatureInnerStaticAsserts(
310+
const CanGenericSignature &signature) {
311+
os << "#ifndef __cpp_concepts\n";
312+
llvm::interleave(
313+
signature.getInnermostGenericParams(), os,
314+
[&](const GenericTypeParamType *genericParamType) {
315+
os << "static_assert(swift::isUsableInGenericContext<";
316+
printGenericTypeParamTypeName(genericParamType);
317+
os << ">, \"type cannot be used in a Swift generic context\");";
318+
},
319+
"\n");
320+
os << "\n#endif // __cpp_concepts\n";
321+
}
322+
300323
void ClangSyntaxPrinter::printGenericSignatureParams(
301324
const CanGenericSignature &signature) {
302325
os << '<';

lib/PrintAsClang/ClangSyntaxPrinter.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,19 @@ class ClangSyntaxPrinter {
7878
bool printNominalTypeOutsideMemberDeclTemplateSpecifiers(
7979
const NominalTypeDecl *typeDecl);
8080

81+
/// Print out additional C++ `static_assert` clauses that
82+
/// are required to emit a generic member definition outside a C++ class that
83+
/// is generated for the given Swift type declaration.
84+
///
85+
/// \returns true if nothing was printed.
86+
///
87+
/// Examples:
88+
/// 1) For Swift's `String` type, it will print nothing.
89+
/// 2) For Swift's `Array<T>` type, it will print
90+
/// `static_assert(swift::isUsableInGenericContext<T_0_0>);\n`
91+
bool printNominalTypeOutsideMemberDeclInnerStaticAssert(
92+
const NominalTypeDecl *typeDecl);
93+
8194
/// Print out the C++ class access qualifier for the given Swift type
8295
/// declaration.
8396
///
@@ -163,6 +176,11 @@ class ClangSyntaxPrinter {
163176
/// its requirements.
164177
void printGenericSignature(const CanGenericSignature &signature);
165178

179+
/// Print the `static_assert` statements used for legacy type-checking for
180+
/// generics in C++14/C++17 mode.
181+
void
182+
printGenericSignatureInnerStaticAsserts(const CanGenericSignature &signature);
183+
166184
/// Print the C++ template parameters that should be passed for a given
167185
/// generic signature.
168186
void printGenericSignatureParams(const CanGenericSignature &signature);

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,8 +1379,8 @@ class DeclAndTypePrinter::Implementation
13791379
os << " {\n";
13801380
funcPrinter.printCxxThunkBody(
13811381
FD, funcABI.getSignature(), funcABI.getSymbolName(),
1382-
FD->getModuleContext(), resultTy, FD->getParameters(),
1383-
funcTy->isThrowing(), funcTy);
1382+
/*typeDeclContext=*/nullptr, FD->getModuleContext(), resultTy,
1383+
FD->getParameters(), funcTy->isThrowing(), funcTy);
13841384
os << "}\n";
13851385
}
13861386

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,9 +1043,16 @@ void DeclAndTypeClangFunctionPrinter::printGenericReturnSequence(
10431043

10441044
void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
10451045
const AbstractFunctionDecl *FD, const LoweredFunctionSignature &signature,
1046-
StringRef swiftSymbolName, const ModuleDecl *moduleContext, Type resultTy,
1047-
const ParameterList *params, bool hasThrows,
1048-
const AnyFunctionType *funcType) {
1046+
StringRef swiftSymbolName, const NominalTypeDecl *typeDeclContext,
1047+
const ModuleDecl *moduleContext, Type resultTy, const ParameterList *params,
1048+
bool hasThrows, const AnyFunctionType *funcType) {
1049+
if (typeDeclContext)
1050+
ClangSyntaxPrinter(os).printNominalTypeOutsideMemberDeclInnerStaticAssert(
1051+
typeDeclContext);
1052+
if (FD->isGeneric()) {
1053+
auto Signature = FD->getGenericSignature().getCanonicalSignature();
1054+
ClangSyntaxPrinter(os).printGenericSignatureInnerStaticAsserts(Signature);
1055+
}
10491056
if (hasThrows) {
10501057
os << " void* opaqueError = nullptr;\n";
10511058
os << " void* _ctx = nullptr;\n";
@@ -1281,8 +1288,9 @@ void DeclAndTypeClangFunctionPrinter::printCxxMethod(
12811288

12821289
os << " {\n";
12831290
// FIXME: should it be objTy for resultTy?
1284-
printCxxThunkBody(FD, signature, swiftSymbolName, FD->getModuleContext(),
1285-
resultTy, FD->getParameters(), FD->hasThrows(),
1291+
printCxxThunkBody(FD, signature, swiftSymbolName, typeDeclContext,
1292+
FD->getModuleContext(), resultTy, FD->getParameters(),
1293+
FD->hasThrows(),
12861294
FD->getInterfaceType()->castTo<AnyFunctionType>());
12871295
os << " }\n";
12881296
}
@@ -1340,7 +1348,7 @@ void DeclAndTypeClangFunctionPrinter::printCxxPropertyAccessorMethod(
13401348
}
13411349
os << " {\n";
13421350
// FIXME: should it be objTy for resultTy?
1343-
printCxxThunkBody(accessor, signature, swiftSymbolName,
1351+
printCxxThunkBody(accessor, signature, swiftSymbolName, typeDeclContext,
13441352
accessor->getModuleContext(), resultTy,
13451353
accessor->getParameters());
13461354
os << " }\n";
@@ -1366,7 +1374,7 @@ void DeclAndTypeClangFunctionPrinter::printCxxSubscriptAccessorMethod(
13661374
}
13671375
os << " {\n";
13681376
// FIXME: should it be objTy for resultTy?
1369-
printCxxThunkBody(accessor, signature, swiftSymbolName,
1377+
printCxxThunkBody(accessor, signature, swiftSymbolName, typeDeclContext,
13701378
accessor->getModuleContext(), resultTy,
13711379
accessor->getParameters());
13721380
os << " }\n";

lib/PrintAsClang/PrintClangFunction.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class DeclAndTypeClangFunctionPrinter {
105105
void printCxxThunkBody(const AbstractFunctionDecl *FD,
106106
const LoweredFunctionSignature &signature,
107107
StringRef swiftSymbolName,
108+
const NominalTypeDecl *typeDeclContext,
108109
const ModuleDecl *moduleContext, Type resultTy,
109110
const ParameterList *params, bool hasThrows = false,
110111
const AnyFunctionType *funcType = nullptr);

test/Interop/SwiftToCxx/generics/generic-enum-in-cxx.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ public func inoutConcreteOpt(_ x: inout GenericOpt<UInt16>) {
153153
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
154154
// CHECK-NEXT: #endif
155155
// CHECK-NEXT: inline void inoutGenericOpt(GenericOpt<T_0_0>& x, const T_0_0& y) noexcept {
156+
// CHECK-NEXT: #ifndef __cpp_concepts
157+
// CHECK-NEXT: static_assert(swift::isUsableInGenericContext<T_0_0>, "type cannot be used in a Swift generic context");
158+
// CHECK-NEXT: #endif
156159
// CHECK-NEXT: return _impl::$s8Generics15inoutGenericOptyyAA0cD0OyxGz_xtlF(_impl::_impl_GenericOpt<T_0_0>::getOpaquePointer(x), swift::_impl::getOpaquePointer(y), swift::TypeMetadataTrait<T_0_0>::getTypeMetadata());
157160
// CHECK-NEXT: }
158161

@@ -169,6 +172,9 @@ public func inoutConcreteOpt(_ x: inout GenericOpt<UInt16>) {
169172
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
170173
// CHECK-NEXT: #endif
171174
// CHECK-NEXT: inline GenericOpt<T_0_0> makeGenericOpt(const T_0_0& x) noexcept SWIFT_WARN_UNUSED_RESULT {
175+
// CHECK-NEXT: #ifndef __cpp_concepts
176+
// CHECK-NEXT: static_assert(swift::isUsableInGenericContext<T_0_0>, "type cannot be used in a Swift generic context");
177+
// CHECK-NEXT: #endif
172178
// CHECK-NEXT: return _impl::_impl_GenericOpt<T_0_0>::returnNewValue([&](char * _Nonnull result) {
173179
// CHECK-NEXT: _impl::$s8Generics14makeGenericOptyAA0cD0OyxGxlF(result, swift::_impl::getOpaquePointer(x), swift::TypeMetadataTrait<T_0_0>::getTypeMetadata());
174180
// CHECK-NEXT: });
@@ -185,6 +191,9 @@ public func inoutConcreteOpt(_ x: inout GenericOpt<UInt16>) {
185191
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
186192
// CHECK-NEXT: #endif
187193
// CHECK-NEXT: inline void takeGenericOpt(const GenericOpt<T_0_0>& x) noexcept {
194+
// CHECK-NEXT: #ifndef __cpp_concepts
195+
// CHECK-NEXT: static_assert(swift::isUsableInGenericContext<T_0_0>, "type cannot be used in a Swift generic context");
196+
// CHECK-NEXT: #endif
188197
// CHECK-NEXT: return _impl::$s8Generics14takeGenericOptyyAA0cD0OyxGlF(_impl::_impl_GenericOpt<T_0_0>::getOpaquePointer(x), swift::TypeMetadataTrait<T_0_0>::getTypeMetadata());
189198
// CHECK-NEXT: }
190199

@@ -236,6 +245,9 @@ public func inoutConcreteOpt(_ x: inout GenericOpt<UInt16>) {
236245
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
237246
// CHECK-NEXT: #endif
238247
// CHECK-NEXT: inline void GenericOpt<T_0_0>::method() const {
248+
// CHECK-NEXT: #ifndef __cpp_concepts
249+
// CHECK-NEXT: static_assert(swift::isUsableInGenericContext<T_0_0>, "type cannot be used in a Swift generic context");
250+
// CHECK-NEXT: #endif
239251
// CHECK-NEXT: return _impl::$s8Generics10GenericOptO6methodyyF(swift::TypeMetadataTrait<GenericOpt<T_0_0>>::getTypeMetadata(), _getOpaquePointer());
240252
// CHECK-NEXT: }
241253

@@ -260,5 +272,8 @@ public func inoutConcreteOpt(_ x: inout GenericOpt<UInt16>) {
260272
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
261273
// CHECK-NEXT: #endif
262274
// CHECK-NEXT: inline swift::Int GenericOpt<T_0_0>::getComputedProp() const {
275+
// CHECK-NEXT: #ifndef __cpp_concepts
276+
// CHECK-NEXT: static_assert(swift::isUsableInGenericContext<T_0_0>, "type cannot be used in a Swift generic context");
277+
// CHECK-NEXT: #endif
263278
// CHECK-NEXT: return _impl::$s8Generics10GenericOptO12computedPropSivg(swift::TypeMetadataTrait<GenericOpt<T_0_0>>::getTypeMetadata(), _getOpaquePointer());
264279
// CHECK-NEXT: }

test/Interop/SwiftToCxx/generics/generic-function-cxx-type-invalid.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
// RUN: %target-swift-frontend %S/generic-function-in-cxx.swift -typecheck -module-name Functions -clang-header-expose-decls=all-public -emit-clang-header-path %t/functions.h
44

55
// RUN: %target-interop-build-clangxx -std=gnu++20 -c %s -I %t -o /dev/null -DUSE_TYPE=int
6+
// RUN: %target-interop-build-clangxx -std=gnu++17 -c %s -I %t -o /dev/null -DUSE_TYPE=int
67
// RUN: not %target-interop-build-clangxx -std=gnu++20 -c %s -I %t -o /dev/null -DUSE_TYPE=CxxStruct
8+
// RUN: not %target-interop-build-clangxx -std=gnu++17 -c %s -I %t -o /dev/null -DUSE_TYPE=CxxStruct 2>&1 | %FileCheck -check-prefix=STATIC_ASSERT_ERROR %s
79

810
#include <cassert>
911
#include "functions.h"
@@ -17,3 +19,5 @@ int main() {
1719
genericPrintFunction(value);
1820
return 0;
1921
}
22+
23+
// STATIC_ASSERT_ERROR: type cannot be used in a Swift generic context

test/Interop/SwiftToCxx/generics/generic-function-in-cxx.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ public func createTestSmallStruct(_ x: UInt32) -> TestSmallStruct {
128128
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
129129
// CHECK-NEXT: #endif
130130
// CHECK-NEXT: inline void genericPrintFunction(const T_0_0& x) noexcept {
131+
// CHECK-NEXT: #ifndef __cpp_concepts
132+
// CHECK-NEXT: static_assert(swift::isUsableInGenericContext<T_0_0>, "type cannot be used in a Swift generic context");
133+
// CHECK-NEXT: #endif
131134
// CHECK-NEXT: return _impl::$s9Functions20genericPrintFunctionyyxlF(swift::_impl::getOpaquePointer(x), swift::TypeMetadataTrait<T_0_0>::getTypeMetadata());
132135
// CHECK-NEXT: }
133136

@@ -137,6 +140,10 @@ public func createTestSmallStruct(_ x: UInt32) -> TestSmallStruct {
137140
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0> && swift::isUsableInGenericContext<T_0_1>
138141
// CHECK-NEXT: #endif
139142
// CHECK-NEXT: inline void genericPrintFunctionMultiGeneric(swift::Int x, const T_0_0& t1, const T_0_0& t1p, swift::Int y, const T_0_1& t2) noexcept {
143+
// CHECK-NEXT: #ifndef __cpp_concepts
144+
// CHECK-NEXT: static_assert(swift::isUsableInGenericContext<T_0_0>, "type cannot be used in a Swift generic context");
145+
// CHECK-NEXT: static_assert(swift::isUsableInGenericContext<T_0_1>, "type cannot be used in a Swift generic context");
146+
// CHECK-NEXT: #endif
140147
// CHECK-NEXT: return _impl::$s9Functions32genericPrintFunctionMultiGenericyySi_xxSiq_tr0_lF(x, swift::_impl::getOpaquePointer(t1), swift::_impl::getOpaquePointer(t1p), y, swift::_impl::getOpaquePointer(t2), swift::TypeMetadataTrait<T_0_0>::getTypeMetadata(), swift::TypeMetadataTrait<T_0_1>::getTypeMetadata());
141148
// CHECK-NEXT: }
142149

@@ -146,6 +153,9 @@ public func createTestSmallStruct(_ x: UInt32) -> TestSmallStruct {
146153
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
147154
// CHECK-NEXT: #endif
148155
// CHECK-NEXT: inline void genericPrintFunctionTwoArg(const T_0_0& x, swift::Int y) noexcept {
156+
// CHECK-NEXT: #ifndef __cpp_concepts
157+
// CHECK-NEXT: static_assert(swift::isUsableInGenericContext<T_0_0>, "type cannot be used in a Swift generic context");
158+
// CHECK-NEXT: #endif
149159
// CHECK-NEXT: return _impl::$s9Functions26genericPrintFunctionTwoArgyyx_SitlF(swift::_impl::getOpaquePointer(x), y, swift::TypeMetadataTrait<T_0_0>::getTypeMetadata());
150160
// CHECK-NEXT: }
151161

@@ -154,6 +164,9 @@ public func createTestSmallStruct(_ x: UInt32) -> TestSmallStruct {
154164
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
155165
// CHECK-NEXT: #endif
156166
// CHECK-NEXT: inline T_0_0 genericRet(const T_0_0& x) noexcept SWIFT_WARN_UNUSED_RESULT {
167+
// CHECK-NEXT: #ifndef __cpp_concepts
168+
// CHECK-NEXT: static_assert(swift::isUsableInGenericContext<T_0_0>, "type cannot be used in a Swift generic context");
169+
// CHECK-NEXT: #endif
157170
// CHECK-NEXT: #pragma clang diagnostic push
158171
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
159172
// CHECK-NEXT: if constexpr (std::is_base_of<::swift::_impl::RefCountedClass, T_0_0>::value) {
@@ -184,6 +197,9 @@ public func createTestSmallStruct(_ x: UInt32) -> TestSmallStruct {
184197
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
185198
// CHECK-NEXT: #endif
186199
// CHECK-NEXT: inline void genericSwap(T_0_0& x, T_0_0& y) noexcept {
200+
// CHECK-NEXT: #ifndef __cpp_concepts
201+
// CHECK-NEXT: static_assert(swift::isUsableInGenericContext<T_0_0>, "type cannot be used in a Swift generic context");
202+
// CHECK-NEXT: #endif
187203
// CHECK-NEXT: return _impl::$s9Functions11genericSwapyyxz_xztlF(swift::_impl::getOpaquePointer(x), swift::_impl::getOpaquePointer(y), swift::TypeMetadataTrait<T_0_0>::getTypeMetadata());
188204
// CHECK-NEXT: }
189205

@@ -192,6 +208,9 @@ public func createTestSmallStruct(_ x: UInt32) -> TestSmallStruct {
192208
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
193209
// CHECK-NEXT: #endif
194210
// CHECK-NEXT: inline T_0_0 TestSmallStruct::genericMethodPassThrough(const T_0_0& x) const {
211+
// CHECK-NEXT: #ifndef __cpp_concepts
212+
// CHECK-NEXT: static_assert(swift::isUsableInGenericContext<T_0_0>, "type cannot be used in a Swift generic context");
213+
// CHECK-NEXT: #endif
195214
// CHECK-NEXT: #pragma clang diagnostic push
196215
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
197216
// CHECK-NEXT: if constexpr (std::is_base_of<::swift::_impl::RefCountedClass, T_0_0>::value) {
@@ -221,5 +240,8 @@ public func createTestSmallStruct(_ x: UInt32) -> TestSmallStruct {
221240
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
222241
// CHECK-NEXT: #endif
223242
// CHECK-NEXT: inline void TestSmallStruct::genericMethodMutTake(const T_0_0& x) {
243+
// CHECK-NEXT: #ifndef __cpp_concepts
244+
// CHECK-NEXT: static_assert(swift::isUsableInGenericContext<T_0_0>, "type cannot be used in a Swift generic context");
245+
// CHECK-NEXT: #endif
224246
// CHECK-NEXT: return _impl::$s9Functions15TestSmallStructV20genericMethodMutTakeyyxlF(swift::_impl::getOpaquePointer(x), swift::TypeMetadataTrait<T_0_0>::getTypeMetadata(), _getOpaquePointer());
225247
// CHECK-NEXT: }

0 commit comments

Comments
 (0)