Skip to content

Commit 72be0e0

Browse files
committed
[Clang importer] Import incomplete SWIFT_SHARED_REFERENCE types
Normally, Swift cannot import an incomplete type. However, when we are importing a SWIFT_SHARED_REFERENCE type, we're always dealing with pointers to the type, and there is no need for the underlying type to be complete. This permits a common pattern in C libraries where the actual underlying storage is opaque and all APIs traffic in the pointer, e.g., typedef struct MyTypeImpl *MyType; void MyTypeRetain(MyType ptr); void MyTypeRelease(MyType ptr); to use SWIFT_SHARED_REFERENCE to import such types as foreign references, rather than as OpaquePointer. Fixes rdar://155970441.
1 parent c88d8cf commit 72be0e0

File tree

11 files changed

+220
-56
lines changed

11 files changed

+220
-56
lines changed

include/swift/ClangImporter/ClangImporter.h

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,10 @@ getCxxReferencePointeeTypeOrNone(const clang::Type *type);
706706
/// Returns true if the given type is a C++ `const` reference type.
707707
bool isCxxConstReferenceType(const clang::Type *type);
708708

709+
/// Determine whether the given Clang record declaration has one of the
710+
/// attributes that makes it import as a reference types.
711+
bool hasImportAsRefAttr(const clang::RecordDecl *decl);
712+
709713
/// Determine whether this typedef is a CF type.
710714
bool isCFTypeDecl(const clang::TypedefNameDecl *Decl);
711715

@@ -804,16 +808,18 @@ std::optional<T> matchSwiftAttrConsideringInheritance(
804808

805809
if (const auto *recordDecl = llvm::dyn_cast<clang::CXXRecordDecl>(decl)) {
806810
std::optional<T> result;
807-
recordDecl->forallBases([&](const clang::CXXRecordDecl *base) -> bool {
808-
if (auto baseMatch = matchSwiftAttr<T>(base, patterns)) {
809-
result = baseMatch;
810-
return false;
811-
}
811+
if (recordDecl->isCompleteDefinition()) {
812+
recordDecl->forallBases([&](const clang::CXXRecordDecl *base) -> bool {
813+
if (auto baseMatch = matchSwiftAttr<T>(base, patterns)) {
814+
result = baseMatch;
815+
return false;
816+
}
812817

813-
return true;
814-
});
818+
return true;
819+
});
815820

816-
return result;
821+
return result;
822+
}
817823
}
818824

819825
return std::nullopt;

lib/ClangImporter/ClangImporter.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5312,7 +5312,8 @@ ClangTypeEscapability::evaluate(Evaluator &evaluator,
53125312
if (desc.annotationOnly)
53135313
return CxxEscapability::Unknown;
53145314
auto cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(recordDecl);
5315-
if (!cxxRecordDecl || cxxRecordDecl->isAggregate()) {
5315+
if (recordDecl->getDefinition() &&
5316+
(!cxxRecordDecl || cxxRecordDecl->isAggregate())) {
53165317
if (cxxRecordDecl) {
53175318
for (auto base : cxxRecordDecl->bases()) {
53185319
auto baseEscapability = evaluateEscapability(
@@ -6346,8 +6347,9 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
63466347
}
63476348

63486349
// If this is a C++ record, look through any base classes.
6349-
if (auto cxxRecord =
6350-
dyn_cast<clang::CXXRecordDecl>(recordDecl->getClangDecl())) {
6350+
const clang::CXXRecordDecl *cxxRecord;
6351+
if ((cxxRecord = dyn_cast<clang::CXXRecordDecl>(recordDecl->getClangDecl())) &&
6352+
cxxRecord->isCompleteDefinition()) {
63516353
// Capture the arity of already found members in the
63526354
// current record, to avoid adding ambiguous members
63536355
// from base classes.
@@ -7813,7 +7815,7 @@ ClangImporter::createEmbeddedBridgingHeaderCacheKey(
78137815
"ChainedHeaderIncludeTree -> EmbeddedHeaderIncludeTree");
78147816
}
78157817

7816-
static bool hasImportAsRefAttr(const clang::RecordDecl *decl) {
7818+
bool importer::hasImportAsRefAttr(const clang::RecordDecl *decl) {
78177819
return decl->hasAttrs() && llvm::any_of(decl->getAttrs(), [](auto *attr) {
78187820
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
78197821
return swiftAttr->getAttribute() == "import_reference" ||

lib/ClangImporter/ImportDecl.cpp

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,10 @@ using MirroredMethodEntry =
846846
std::tuple<const clang::ObjCMethodDecl*, ProtocolDecl*, bool /*isAsync*/>;
847847

848848
static bool areRecordFieldsComplete(const clang::CXXRecordDecl *decl) {
849+
// If the type is incomplete, then the fields are not complete.
850+
if (!decl->isCompleteDefinition())
851+
return false;
852+
849853
for (const auto *f : decl->fields()) {
850854
auto *fieldRecord = f->getType()->getAsCXXRecordDecl();
851855
if (fieldRecord) {
@@ -2099,18 +2103,21 @@ namespace {
20992103
if (decl->isInterface())
21002104
return nullptr;
21012105

2102-
if (!decl->getDefinition()) {
2106+
bool incompleteTypeAsReference = false;
2107+
if (auto def = decl->getDefinition()) {
2108+
// Continue with the definition of the type.
2109+
decl = def;
2110+
} else if (recordHasReferenceSemantics(decl)) {
2111+
// Incomplete types are okay if the resulting type has reference
2112+
// semantics.
2113+
incompleteTypeAsReference = true;
2114+
} else {
21032115
Impl.addImportDiagnostic(
21042116
decl,
21052117
Diagnostic(diag::incomplete_record, Impl.SwiftContext.AllocateCopy(
21062118
decl->getNameAsString())),
21072119
decl->getLocation());
2108-
}
21092120

2110-
// FIXME: Figure out how to deal with incomplete types, since that
2111-
// notion doesn't exist in Swift.
2112-
decl = decl->getDefinition();
2113-
if (!decl) {
21142121
forwardDeclaration = true;
21152122
return nullptr;
21162123
}
@@ -2126,7 +2133,7 @@ namespace {
21262133
}
21272134

21282135
// Don't import nominal types that are over-aligned.
2129-
if (Impl.isOverAligned(decl)) {
2136+
if (decl->isCompleteDefinition() && Impl.isOverAligned(decl)) {
21302137
Impl.addImportDiagnostic(
21312138
decl, Diagnostic(
21322139
diag::record_over_aligned,
@@ -2137,6 +2144,9 @@ namespace {
21372144

21382145
auto isNonTrivialDueToAddressDiversifiedPtrAuth =
21392146
[](const clang::RecordDecl *decl) {
2147+
if (!decl->isCompleteDefinition())
2148+
return true;
2149+
21402150
for (auto *field : decl->fields()) {
21412151
if (!field->getType().isNonTrivialToPrimitiveCopy()) {
21422152
continue;
@@ -2192,7 +2202,8 @@ namespace {
21922202
*correctSwiftName);
21932203

21942204
auto dc =
2195-
Impl.importDeclContextOf(decl, importedName.getEffectiveContext());
2205+
Impl.importDeclContextOf(decl, importedName.getEffectiveContext(),
2206+
incompleteTypeAsReference);
21962207
if (!dc) {
21972208
Impl.addImportDiagnostic(
21982209
decl, Diagnostic(
@@ -2239,9 +2250,11 @@ namespace {
22392250
// solution would be to turn them into members and add conversion
22402251
// functions.
22412252
if (auto cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(decl)) {
2242-
for (auto base : cxxRecordDecl->bases()) {
2243-
if (auto *baseRecordDecl = base.getType()->getAsCXXRecordDecl()) {
2244-
Impl.importDecl(baseRecordDecl, getVersion());
2253+
if (cxxRecordDecl->isCompleteDefinition()) {
2254+
for (auto base : cxxRecordDecl->bases()) {
2255+
if (auto *baseRecordDecl = base.getType()->getAsCXXRecordDecl()) {
2256+
Impl.importDecl(baseRecordDecl, getVersion());
2257+
}
22452258
}
22462259
}
22472260
}
@@ -2449,15 +2462,18 @@ namespace {
24492462

24502463
const clang::CXXRecordDecl *cxxRecordDecl =
24512464
dyn_cast<clang::CXXRecordDecl>(decl);
2452-
bool hasBaseClasses = cxxRecordDecl && !cxxRecordDecl->bases().empty();
2465+
bool hasBaseClasses = cxxRecordDecl &&
2466+
cxxRecordDecl->isCompleteDefinition() &&
2467+
!cxxRecordDecl->bases().empty();
24532468
if (hasBaseClasses) {
24542469
hasUnreferenceableStorage = true;
24552470
hasMemberwiseInitializer = false;
24562471
}
24572472

24582473
bool needsEmptyInitializer = true;
24592474
if (cxxRecordDecl) {
2460-
needsEmptyInitializer = !cxxRecordDecl->isAbstract() &&
2475+
needsEmptyInitializer = cxxRecordDecl->isCompleteDefinition() &&
2476+
!cxxRecordDecl->isAbstract() &&
24612477
(!cxxRecordDecl->hasDefaultConstructor() ||
24622478
cxxRecordDecl->ctors().empty());
24632479
}
@@ -2501,7 +2517,8 @@ namespace {
25012517
// only when the same is possible in C++. While we could check for that
25022518
// exactly, checking whether the C++ class is an aggregate
25032519
// (C++ [dcl.init.aggr]) has the same effect.
2504-
bool isAggregate = !cxxRecordDecl || cxxRecordDecl->isAggregate();
2520+
bool isAggregate = decl->isCompleteDefinition() &&
2521+
(!cxxRecordDecl || cxxRecordDecl->isAggregate());
25052522
if ((hasReferenceableFields && hasMemberwiseInitializer && isAggregate) ||
25062523
forceMemberwiseInitializer) {
25072524
// The default zero initializer suppresses the implicit value
@@ -2905,7 +2922,13 @@ namespace {
29052922
if (!Impl.SwiftContext.LangOpts.EnableCXXInterop)
29062923
return VisitRecordDecl(decl);
29072924

2908-
if (!decl->getDefinition()) {
2925+
if (auto def = decl->getDefinition()) {
2926+
// Continue with the definition of the type.
2927+
decl = def;
2928+
} else if (recordHasReferenceSemantics(decl)) {
2929+
// Incomplete types are okay if the resulting type has reference
2930+
// semantics.
2931+
} else {
29092932
Impl.addImportDiagnostic(
29102933
decl,
29112934
Diagnostic(diag::incomplete_record, Impl.SwiftContext.AllocateCopy(
@@ -2921,10 +2944,7 @@ namespace {
29212944
Impl.diagnose(HeaderLoc(attr.second),
29222945
diag::private_fileid_attr_here);
29232946
}
2924-
}
29252947

2926-
decl = decl->getDefinition();
2927-
if (!decl) {
29282948
forwardDeclaration = true;
29292949
return nullptr;
29302950
}
@@ -3141,12 +3161,14 @@ namespace {
31413161
void
31423162
addExplicitProtocolConformances(NominalTypeDecl *decl,
31433163
const clang::CXXRecordDecl *clangDecl) {
3144-
// Propagate conforms_to attribute from public base classes.
3145-
for (auto base : clangDecl->bases()) {
3146-
if (base.getAccessSpecifier() != clang::AccessSpecifier::AS_public)
3147-
continue;
3148-
if (auto *baseClangDecl = base.getType()->getAsCXXRecordDecl())
3149-
addExplicitProtocolConformances(decl, baseClangDecl);
3164+
if (clangDecl->isCompleteDefinition()) {
3165+
// Propagate conforms_to attribute from public base classes.
3166+
for (auto base : clangDecl->bases()) {
3167+
if (base.getAccessSpecifier() != clang::AccessSpecifier::AS_public)
3168+
continue;
3169+
if (auto *baseClangDecl = base.getType()->getAsCXXRecordDecl())
3170+
addExplicitProtocolConformances(decl, baseClangDecl);
3171+
}
31503172
}
31513173

31523174
if (!clangDecl->hasAttrs())
@@ -10261,7 +10283,8 @@ ClangImporter::Implementation::importDeclForDeclContext(
1026110283
DeclContext *
1026210284
ClangImporter::Implementation::importDeclContextOf(
1026310285
const clang::Decl *decl,
10264-
EffectiveClangContext context)
10286+
EffectiveClangContext context,
10287+
bool allowForwardDeclaration)
1026510288
{
1026610289
DeclContext *importedDC = nullptr;
1026710290
switch (context.getKind()) {
@@ -10290,7 +10313,7 @@ ClangImporter::Implementation::importDeclContextOf(
1029010313
}
1029110314

1029210315
if (dc->isTranslationUnit()) {
10293-
if (auto *module = getClangModuleForDecl(decl))
10316+
if (auto *module = getClangModuleForDecl(decl, allowForwardDeclaration))
1029410317
return module;
1029510318
else
1029610319
return nullptr;
@@ -10554,7 +10577,9 @@ void ClangImporter::Implementation::loadAllMembersOfRecordDecl(
1055410577
}
1055510578

1055610579
// If this is a C++ record, look through the base classes too.
10557-
if (auto cxxRecord = dyn_cast<clang::CXXRecordDecl>(clangRecord)) {
10580+
const clang::CXXRecordDecl *cxxRecord;
10581+
if ((cxxRecord = dyn_cast<clang::CXXRecordDecl>(clangRecord)) &&
10582+
cxxRecord->isCompleteDefinition()) {
1055810583
for (auto base : cxxRecord->bases()) {
1055910584
if (skipIfNonPublic && base.getAccessSpecifier() != clang::AS_public)
1056010585
continue;

lib/ClangImporter/ImporterImpl.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1227,7 +1227,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
12271227
/// \returns The imported declaration context, or null if it could not
12281228
/// be converted.
12291229
DeclContext *importDeclContextOf(const clang::Decl *D,
1230-
EffectiveClangContext context);
1230+
EffectiveClangContext context,
1231+
bool allowForwardDeclaration = false);
12311232

12321233
/// Determine whether the given declaration is considered
12331234
/// 'unavailable' in Swift.

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2560,7 +2560,7 @@ llvm::SmallVector<clang::CXXMethodDecl *, 4>
25602560
SwiftDeclSynthesizer::synthesizeStaticFactoryForCXXForeignRef(
25612561
const clang::CXXRecordDecl *cxxRecordDecl) {
25622562

2563-
if (cxxRecordDecl->isAbstract())
2563+
if (!cxxRecordDecl->isCompleteDefinition() || cxxRecordDecl->isAbstract())
25642564
return {};
25652565

25662566
clang::ASTContext &clangCtx = cxxRecordDecl->getASTContext();

lib/ClangImporter/SwiftLookupTable.cpp

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1881,27 +1881,19 @@ void importer::addEntryToLookupTable(SwiftLookupTable &table,
18811881
if (shouldSuppressDeclImport(named))
18821882
return;
18831883

1884-
// Leave incomplete struct/enum/union types out of the table; Swift only
1885-
// handles pointers to them.
1886-
// FIXME: At some point we probably want to be importing incomplete types,
1887-
// so that pointers to different incomplete types themselves have distinct
1888-
// types. At that time it will be necessary to make the decision of whether
1889-
// or not to import an incomplete type declaration based on whether it's
1890-
// actually the struct backing a CF type:
1891-
//
1892-
// typedef struct CGColor *CGColorRef;
1893-
//
1894-
// The best way to do this is probably to change CFDatabase.def to include
1895-
// struct names when relevant, not just pointer names. That way we can check
1896-
// both CFDatabase.def and the objc_bridge attribute and cover all our bases.
1884+
// Leave incomplete struct/enum/union types out of the table, unless they
1885+
// are types that will be imported as reference types (e.g., CF types or
1886+
// those that use SWIFT_SHARED_REFERENCE).
18971887
if (auto *tagDecl = dyn_cast<clang::TagDecl>(named)) {
18981888
// We add entries for ClassTemplateSpecializations that don't have
18991889
// definition. It's possible that the decl will be instantiated by
19001890
// SwiftDeclConverter later on. We cannot force instantiating
19011891
// ClassTemplateSPecializations here because we're currently writing the
19021892
// AST, so we cannot modify it.
19031893
if (!isa<clang::ClassTemplateSpecializationDecl>(named) &&
1904-
!tagDecl->getDefinition()) {
1894+
!tagDecl->getDefinition() &&
1895+
!(isa<clang::RecordDecl>(tagDecl) &&
1896+
hasImportAsRefAttr(cast<clang::RecordDecl>(tagDecl)))) {
19051897
return;
19061898
}
19071899
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
Name: ReferenceCounted
3+
Tags:
4+
- Name: OpaqueRefImpl
5+
SwiftImportAs: reference
6+
SwiftRetainOp: OPRetain
7+
SwiftReleaseOp: OPRelease
8+
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#include <stdlib.h>
2+
#include <stdio.h>
3+
4+
#include "reference-counted.h"
5+
6+
typedef struct IncompleteImpl {
7+
unsigned refCount;
8+
double weight;
9+
} *Incomplete;
10+
11+
12+
Incomplete Incomplete_create(double weight) {
13+
Incomplete result = (Incomplete)malloc(sizeof(struct IncompleteImpl));
14+
result->refCount = 1;
15+
result->weight = weight;
16+
return result;
17+
}
18+
19+
void INRetain(Incomplete i) {
20+
i->refCount++;
21+
}
22+
23+
void INRelease(Incomplete i) {
24+
i->refCount--;
25+
if (i->refCount == 0) {
26+
printf("Destroyed instance containing weight %f\n", i->weight);
27+
free(i);
28+
}
29+
}
30+
31+
double Incomplete_getWeight(Incomplete i) {
32+
return i->weight;
33+
}
34+
35+
struct OpaqueRefImpl {
36+
unsigned refCount;
37+
};
38+
39+
OpaqueRef Opaque_create(void) {
40+
OpaqueRef result = (OpaqueRef)malloc(sizeof(struct OpaqueRefImpl));
41+
result->refCount = 1;
42+
return result;
43+
}
44+
45+
void OPRetain(OpaqueRef i) {
46+
i->refCount++;
47+
}
48+
49+
void OPRelease(OpaqueRef i) {
50+
i->refCount--;
51+
if (i->refCount == 0) {
52+
printf("Destroyed OpaqueRef instance\n");
53+
free(i);
54+
}
55+
}

test/Interop/Cxx/foreign-reference/Inputs/module.modulemap

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ module WitnessTable {
3535

3636
module ReferenceCounted {
3737
header "reference-counted.h"
38-
requires cplusplus
3938
}
4039

4140
module ReferenceCountedObjCProperty {

0 commit comments

Comments
 (0)