Skip to content

Commit a1dc63e

Browse files
authored
Merge pull request #65728 from hyp/eng/safe-overloads-and-unavailable-unemitted-decls-in-cxx
[interop][SwiftToCxx] avoid emitting ambiguous C++ overloads and emit unavailable type stubs for top level types that could not be emitted in the C++ section of the generated header
2 parents e1a23b8 + 82ef9d3 commit a1dc63e

23 files changed

+475
-111
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,6 +1694,14 @@ ERROR(expose_enum_case_type_to_cxx,none,
16941694
"enum %0 can not be represented in C++ as one of its cases has an associated value with type that can't be represented in C++", (ValueDecl *))
16951695
ERROR(expose_enum_case_tuple_to_cxx,none,
16961696
"enum %0 can not yet be represented in C++ as one of its cases has multiple associated values", (ValueDecl *))
1697+
ERROR(expose_protocol_to_cxx_unsupported,none,
1698+
"protocol %0 can not yet be represented in C++", (ValueDecl *))
1699+
ERROR(expose_move_only_to_cxx,none,
1700+
"move-only %0 %1 can not yet be represented in C++", (DescriptiveDeclKind, ValueDecl *))
1701+
ERROR(unexposed_other_decl_in_cxx,none,
1702+
"%0 %1 is not yet exposed to C++", (DescriptiveDeclKind, ValueDecl *))
1703+
ERROR(unsupported_other_decl_in_cxx,none,
1704+
"Swift %0 %1 cannot be represented in C++", (DescriptiveDeclKind, ValueDecl *))
16971705

16981706
ERROR(attr_methods_only,none,
16991707
"only methods can be declared %0", (DeclAttribute))

include/swift/AST/SwiftNameTranslation.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313
#ifndef SWIFT_NAME_TRANSLATION_H
1414
#define SWIFT_NAME_TRANSLATION_H
1515

16-
#include "swift/AST/Identifier.h"
1716
#include "swift/AST/AttrKind.h"
17+
#include "swift/AST/DiagnosticEngine.h"
18+
#include "swift/AST/Identifier.h"
1819

1920
namespace swift {
2021
class ValueDecl;
@@ -76,8 +77,13 @@ enum RepresentationError {
7677
UnrepresentableIndirectEnum,
7778
UnrepresentableEnumCaseType,
7879
UnrepresentableEnumCaseTuple,
80+
UnrepresentableProtocol,
81+
UnrepresentableMoveOnly,
7982
};
8083

84+
/// Constructs a diagnostic that describes the given C++ representation error.
85+
Diagnostic diagnoseRepresenationError(RepresentationError error, ValueDecl *vd);
86+
8187
struct DeclRepresentation {
8288
RepresentationKind kind;
8389
llvm::Optional<RepresentationError> error;

lib/AST/SwiftNameTranslation.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "swift/AST/SwiftNameTranslation.h"
1818
#include "swift/AST/ASTContext.h"
1919
#include "swift/AST/Decl.h"
20+
#include "swift/AST/DiagnosticsSema.h"
2021
#include "swift/AST/LazyResolver.h"
2122
#include "swift/AST/Module.h"
2223
#include "swift/AST/ParameterList.h"
@@ -163,6 +164,9 @@ swift::cxx_translation::getNameForCxx(const ValueDecl *VD,
163164
if (customNamesOnly)
164165
return StringRef();
165166

167+
if (isa<ConstructorDecl>(VD))
168+
return "init";
169+
166170
if (auto *mod = dyn_cast<ModuleDecl>(VD)) {
167171
if (mod->isStdlibModule())
168172
return "swift";
@@ -220,6 +224,11 @@ swift::cxx_translation::getDeclRepresentation(const ValueDecl *VD) {
220224
genericSignature = AFD->getGenericSignature().getCanonicalSignature();
221225
}
222226
if (const auto *typeDecl = dyn_cast<NominalTypeDecl>(VD)) {
227+
if (isa<ProtocolDecl>(typeDecl))
228+
return {Unsupported, UnrepresentableProtocol};
229+
// Swift's consume semantics are not yet supported in C++.
230+
if (typeDecl->isMoveOnly())
231+
return {Unsupported, UnrepresentableMoveOnly};
223232
if (typeDecl->isGeneric()) {
224233
if (isa<ClassDecl>(VD))
225234
return {Unsupported, UnrepresentableGeneric};
@@ -283,3 +292,42 @@ bool swift::cxx_translation::isVisibleToCxx(const ValueDecl *VD,
283292
}
284293
return false;
285294
}
295+
296+
Diagnostic
297+
swift::cxx_translation::diagnoseRepresenationError(RepresentationError error,
298+
ValueDecl *vd) {
299+
switch (error) {
300+
case UnrepresentableObjC:
301+
return Diagnostic(diag::expose_unsupported_objc_decl_to_cxx,
302+
vd->getDescriptiveKind(), vd);
303+
case UnrepresentableAsync:
304+
return Diagnostic(diag::expose_unsupported_async_decl_to_cxx,
305+
vd->getDescriptiveKind(), vd);
306+
case UnrepresentableIsolatedInActor:
307+
return Diagnostic(diag::expose_unsupported_actor_isolated_to_cxx,
308+
vd->getDescriptiveKind(), vd);
309+
case UnrepresentableRequiresClientEmission:
310+
return Diagnostic(diag::expose_unsupported_client_emission_to_cxx,
311+
vd->getDescriptiveKind(), vd);
312+
case UnrepresentableGeneric:
313+
return Diagnostic(diag::expose_generic_decl_to_cxx,
314+
vd->getDescriptiveKind(), vd);
315+
case UnrepresentableGenericRequirements:
316+
return Diagnostic(diag::expose_generic_requirement_to_cxx,
317+
vd->getDescriptiveKind(), vd);
318+
case UnrepresentableThrows:
319+
return Diagnostic(diag::expose_throwing_to_cxx, vd->getDescriptiveKind(),
320+
vd);
321+
case UnrepresentableIndirectEnum:
322+
return Diagnostic(diag::expose_indirect_enum_cxx, vd);
323+
case UnrepresentableEnumCaseType:
324+
return Diagnostic(diag::expose_enum_case_type_to_cxx, vd);
325+
case UnrepresentableEnumCaseTuple:
326+
return Diagnostic(diag::expose_enum_case_tuple_to_cxx, vd);
327+
case UnrepresentableProtocol:
328+
return Diagnostic(diag::expose_protocol_to_cxx_unsupported, vd);
329+
case UnrepresentableMoveOnly:
330+
return Diagnostic(diag::expose_move_only_to_cxx, vd->getDescriptiveKind(),
331+
vd);
332+
}
333+
}

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,20 @@ static bool looksLikeInitMethod(ObjCSelector selector) {
100100
return !(firstPiece.size() > 4 && clang::isLowercase(firstPiece[4]));
101101
}
102102

103+
// Enters and leaves a new lexical scope when emitting
104+
// members of a Swift type.
105+
struct CxxEmissionScopeRAII {
106+
DeclAndTypePrinter &printer;
107+
CxxDeclEmissionScope &prevScope;
108+
CxxDeclEmissionScope scope;
109+
110+
CxxEmissionScopeRAII(DeclAndTypePrinter &printer)
111+
: printer(printer), prevScope(printer.getCxxDeclEmissionScope()) {
112+
printer.setCxxDeclEmissionScope(scope);
113+
}
114+
~CxxEmissionScopeRAII() { printer.setCxxDeclEmissionScope(prevScope); }
115+
};
116+
103117
class DeclAndTypePrinter::Implementation
104118
: private DeclVisitor<DeclAndTypePrinter::Implementation>,
105119
private TypeVisitor<DeclAndTypePrinter::Implementation, void,
@@ -188,6 +202,12 @@ class DeclAndTypePrinter::Implementation
188202
}
189203

190204
private:
205+
void recordEmittedDeclInCurrentCxxLexicalScope(const ValueDecl *vd) {
206+
assert(outputLang == OutputLanguageMode::Cxx);
207+
owningPrinter.getCxxDeclEmissionScope().emittedDeclarationNames.insert(
208+
cxx_translation::getNameForCxx(vd));
209+
}
210+
191211
/// Prints a protocol adoption list: <code>&lt;NSCoding, NSCopying&gt;</code>
192212
///
193213
/// This method filters out non-ObjC protocols.
@@ -215,6 +235,11 @@ class DeclAndTypePrinter::Implementation
215235
/// Prints the members of a class, extension, or protocol.
216236
template <bool AllowDelayed = false, typename R>
217237
void printMembers(R &&members) {
238+
CxxEmissionScopeRAII cxxScopeRAII(owningPrinter);
239+
// FIXME: Actually track emitted members in nested
240+
// lexical scopes.
241+
// FIXME: Emit unavailable C++ decls for not emitted
242+
// nested members.
218243
bool protocolMembersOptional = false;
219244
for (const Decl *member : members) {
220245
auto VD = dyn_cast<ValueDecl>(member);
@@ -301,6 +326,7 @@ class DeclAndTypePrinter::Implementation
301326
ClangValueTypePrinter::forwardDeclType(os, CD, owningPrinter);
302327
ClangClassTypePrinter(os).printClassTypeDecl(
303328
CD, [&]() { printMembers(CD->getMembers()); }, owningPrinter);
329+
recordEmittedDeclInCurrentCxxLexicalScope(CD);
304330
return;
305331
}
306332

@@ -363,6 +389,7 @@ class DeclAndTypePrinter::Implementation
363389
}
364390
},
365391
owningPrinter);
392+
recordEmittedDeclInCurrentCxxLexicalScope(SD);
366393
}
367394

368395
void visitExtensionDecl(ExtensionDecl *ED) {
@@ -843,6 +870,7 @@ class DeclAndTypePrinter::Implementation
843870
printMembers(ED->getMembers());
844871
},
845872
owningPrinter);
873+
recordEmittedDeclInCurrentCxxLexicalScope(ED);
846874
}
847875

848876
void visitEnumDecl(EnumDecl *ED) {
@@ -984,6 +1012,39 @@ class DeclAndTypePrinter::Implementation
9841012
sel.getSelectorPieces().front().str() == "init";
9851013
}
9861014

1015+
/// Returns true if the given function overload is safe to emit in the current
1016+
/// C++ lexical scope.
1017+
bool canPrintOverloadOfFunction(const AbstractFunctionDecl *funcDecl) const {
1018+
assert(outputLang == OutputLanguageMode::Cxx);
1019+
auto &overloads =
1020+
owningPrinter.getCxxDeclEmissionScope().emittedFunctionOverloads;
1021+
auto cxxName = cxx_translation::getNameForCxx(funcDecl);
1022+
auto overloadIt = overloads.find(cxxName);
1023+
if (overloadIt == overloads.end()) {
1024+
overloads.insert(std::make_pair(
1025+
cxxName,
1026+
llvm::SmallVector<const AbstractFunctionDecl *>({funcDecl})));
1027+
return true;
1028+
}
1029+
auto selfArity =
1030+
funcDecl->getParameters() ? funcDecl->getParameters()->size() : 0;
1031+
for (const auto *overload : overloadIt->second) {
1032+
auto arity =
1033+
overload->getParameters() ? overload->getParameters()->size() : 0;
1034+
// Avoid printing out an overload with the same and arity, as that might
1035+
// be an ambiguous overload on the C++ side.
1036+
// FIXME: we should take types into account, not all overloads with the
1037+
// same arity are ambiguous in C++.
1038+
if (selfArity == arity) {
1039+
owningPrinter.getCxxDeclEmissionScope()
1040+
.additionalUnrepresentableDeclarations.push_back(funcDecl);
1041+
return false;
1042+
}
1043+
}
1044+
overloadIt->second.push_back(funcDecl);
1045+
return true;
1046+
}
1047+
9871048
void printAbstractFunctionAsMethod(AbstractFunctionDecl *AFD,
9881049
bool isClassMethod,
9891050
bool isNSUIntegerSubscript = false,
@@ -1023,6 +1084,12 @@ class DeclAndTypePrinter::Implementation
10231084
if (!dispatchInfo)
10241085
return;
10251086
}
1087+
// FIXME: handle getters/setters ambiguities here too.
1088+
if (!isa<AccessorDecl>(AFD)) {
1089+
if (!canPrintOverloadOfFunction(AFD))
1090+
return;
1091+
}
1092+
10261093
owningPrinter.prologueOS << cFuncPrologueOS.str();
10271094

10281095
printDocumentationComment(AFD);
@@ -1746,10 +1813,16 @@ class DeclAndTypePrinter::Implementation
17461813
llvm::raw_string_ostream cFuncPrologueOS(cFuncDecl);
17471814
auto funcABI = Implementation(cFuncPrologueOS, owningPrinter, outputLang)
17481815
.printSwiftABIFunctionSignatureAsCxxFunction(FD);
1749-
if (!funcABI)
1816+
if (!funcABI) {
1817+
owningPrinter.getCxxDeclEmissionScope()
1818+
.additionalUnrepresentableDeclarations.push_back(FD);
1819+
return;
1820+
}
1821+
if (!canPrintOverloadOfFunction(FD))
17501822
return;
17511823
owningPrinter.prologueOS << cFuncPrologueOS.str();
17521824
printAbstractFunctionAsCxxFunctionThunk(FD, *funcABI);
1825+
recordEmittedDeclInCurrentCxxLexicalScope(FD);
17531826
return;
17541827
}
17551828
if (FD->getDeclContext()->isTypeContext())
@@ -2835,6 +2908,12 @@ bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) {
28352908
!excludeForObjCImplementation(VD);
28362909
}
28372910

2911+
bool DeclAndTypePrinter::isVisible(const ValueDecl *vd) const {
2912+
return outputLang == OutputLanguageMode::Cxx
2913+
? cxx_translation::isVisibleToCxx(vd, minRequiredAccess)
2914+
: isVisibleToObjC(vd, minRequiredAccess);
2915+
}
2916+
28382917
void DeclAndTypePrinter::print(const Decl *D) {
28392918
getImpl().print(D);
28402919
}

lib/PrintAsClang/DeclAndTypePrinter.h

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@ class PrimitiveTypeMapping;
3030
class ValueDecl;
3131
class SwiftToClangInteropContext;
3232

33+
/// Tracks which C++ declarations have been emitted in a lexical
34+
/// C++ scope.
35+
struct CxxDeclEmissionScope {
36+
/// Additional Swift declarations that are unrepresentable in C++.
37+
std::vector<const ValueDecl *> additionalUnrepresentableDeclarations;
38+
/// Records the C++ declaration names already emitted in this lexical scope.
39+
llvm::StringSet<> emittedDeclarationNames;
40+
/// Records the names of the function overloads already emitted in this
41+
/// lexical scope.
42+
llvm::StringMap<llvm::SmallVector<const AbstractFunctionDecl *, 2>>
43+
emittedFunctionOverloads;
44+
};
45+
3346
/// Responsible for printing a Swift Decl or Type in Objective-C, to be
3447
/// included in a Swift module's ObjC compatibility header.
3548
class DeclAndTypePrinter {
@@ -45,6 +58,7 @@ class DeclAndTypePrinter {
4558
raw_ostream &prologueOS;
4659
raw_ostream &outOfLineDefinitionsOS;
4760
const DelayedMemberSet &delayedMembers;
61+
CxxDeclEmissionScope *cxxDeclEmissionScope;
4862
PrimitiveTypeMapping &typeMapping;
4963
SwiftToClangInteropContext &interopContext;
5064
AccessLevel minRequiredAccess;
@@ -63,26 +77,39 @@ class DeclAndTypePrinter {
6377
DeclAndTypePrinter(ModuleDecl &mod, raw_ostream &out, raw_ostream &prologueOS,
6478
raw_ostream &outOfLineDefinitionsOS,
6579
DelayedMemberSet &delayed,
80+
CxxDeclEmissionScope &topLevelEmissionScope,
6681
PrimitiveTypeMapping &typeMapping,
6782
SwiftToClangInteropContext &interopContext,
6883
AccessLevel access, bool requiresExposedAttribute,
6984
llvm::StringSet<> &exposedModules,
7085
OutputLanguageMode outputLang)
7186
: M(mod), os(out), prologueOS(prologueOS),
7287
outOfLineDefinitionsOS(outOfLineDefinitionsOS), delayedMembers(delayed),
73-
typeMapping(typeMapping), interopContext(interopContext),
74-
minRequiredAccess(access),
88+
cxxDeclEmissionScope(&topLevelEmissionScope), typeMapping(typeMapping),
89+
interopContext(interopContext), minRequiredAccess(access),
7590
requiresExposedAttribute(requiresExposedAttribute),
7691
exposedModules(exposedModules), outputLang(outputLang) {}
7792

7893
PrimitiveTypeMapping &getTypeMapping() { return typeMapping; }
7994

8095
SwiftToClangInteropContext &getInteropContext() { return interopContext; }
8196

97+
CxxDeclEmissionScope &getCxxDeclEmissionScope() {
98+
return *cxxDeclEmissionScope;
99+
}
100+
101+
void setCxxDeclEmissionScope(CxxDeclEmissionScope &scope) {
102+
cxxDeclEmissionScope = &scope;
103+
}
104+
82105
/// Returns true if \p VD should be included in a compatibility header for
83106
/// the options the printer was constructed with.
84107
bool shouldInclude(const ValueDecl *VD);
85108

109+
/// Returns true if \p vd is visible given the current access level and thus
110+
/// can be included in the generated header.
111+
bool isVisible(const ValueDecl *vd) const;
112+
86113
void print(const Decl *D);
87114
void print(Type ty);
88115

0 commit comments

Comments
 (0)