Skip to content

Commit 59564f4

Browse files
author
Gabor Horvath
committed
[cxx-interop] Fix miscompilation for inferred shared references
When a base class is annotated as shared reference we can occasionally infer that the derived types also need to be shared references. Unfortunately, we did not generate the correct code for some of those scenarios. When the reference counted base is not at the offset zero we need to do offset adjustments before we pass the pointer to the reference counting functions. We did not do those offset calculations. I looked into implementing the codegen for the offset calculation directly in Swift but it needed significantly more work than I anticipated. We need to invoke the frontend to get the path to the base class and we also need to deal with virtual inheritance, alignment and some other considerations. This PR ends up generating a Clang shim instead and the derived to base conversion happens in this shim. As a result, we piggy-back on Clang making all the correct offset calculations. This patch also had to change how certain aspects of shared references are implemented to be compatible with this approach: * Instead of always looking at the base classes to querry the retain/release operations we now propagate the corresponding annotations once per types. This also has the beneficial effects that we traverse the inheritance hierarchy less often. * To generate the correct diagnostics, I reuse the result of the refcount operation query. * We do not want these generated functions to be inherited, so added a set to exempt them from cloning. * Tweaked the lookup logic for retain/release a bit as these generated clang methods are not found by lookup. We rely on looking up the imported methods instead. rdar://166227787 rdar://165635002
1 parent cef53c6 commit 59564f4

File tree

14 files changed

+272
-45
lines changed

14 files changed

+272
-45
lines changed

include/swift/AST/ClangModuleLoader.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,13 @@ class ClangModuleLoader : public ModuleLoader {
216216
DeclContext *newContext,
217217
ClangInheritanceInfo inheritance) = 0;
218218

219-
/// Returnes the original method if \param decl is a clone from a base class
219+
/// Returns the original method if \param decl is a clone from a base class
220220
virtual ValueDecl *getOriginalForClonedMember(const ValueDecl *decl) = 0;
221221

222+
/// Returns true if we synthesize this member for every type so no need to
223+
/// clone it for the derived classes.
224+
virtual bool isPerTypeSynthesizedMember(const ValueDecl *decl) = 0;
225+
222226
/// Emits diagnostics for any declarations named name
223227
/// whose direct declaration context is a TU.
224228
virtual void diagnoseTopLevelValue(const DeclName &name) = 0;

include/swift/ClangImporter/ClangImporter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,7 @@ class ClangImporter final : public ClangModuleLoader {
650650
ClangInheritanceInfo inheritance) override;
651651

652652
ValueDecl *getOriginalForClonedMember(const ValueDecl *decl) override;
653+
bool isPerTypeSynthesizedMember(const ValueDecl *decl) override;
653654

654655
/// Emits diagnostics for any declarations named name
655656
/// whose direct declaration context is a TU.

include/swift/ClangImporter/ClangImporterRequests.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@ struct CustomRefCountingOperationResult {
500500

501501
CustomRefCountingOperationResultKind kind;
502502
ValueDecl *operation;
503-
std::string name;
503+
StringRef name;
504504
};
505505

506506
class CustomRefCountingOperation

lib/ClangImporter/ClangImporter.cpp

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6565,6 +6565,7 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
65656565
auto namedMember = dyn_cast<ValueDecl>(member);
65666566
if (!namedMember || !namedMember->hasName() ||
65676567
namedMember->getName().getBaseName() != name ||
6568+
clangModuleLoader->isPerTypeSynthesizedMember(namedMember) ||
65686569
clangModuleLoader->getOriginalForClonedMember(namedMember))
65696570
continue;
65706571

@@ -8033,6 +8034,17 @@ ValueDecl *ClangImporter::Implementation::getOriginalForClonedMember(
80338034
return nullptr;
80348035
}
80358036

8037+
bool ClangImporter::Implementation::isPerTypeSynthesizedMember(
8038+
const ValueDecl *decl) {
8039+
auto result = perTypeSynthesizedMembers.find(decl);
8040+
return result != perTypeSynthesizedMembers.end();
8041+
}
8042+
8043+
void ClangImporter::Implementation::markPerTypeSynthesizedMember(
8044+
const ValueDecl *decl) {
8045+
perTypeSynthesizedMembers.insert(decl);
8046+
}
8047+
80368048
size_t ClangImporter::Implementation::getImportedBaseMemberDeclArity(
80378049
const ValueDecl *valueDecl) {
80388050
if (auto *func = dyn_cast<FuncDecl>(valueDecl)) {
@@ -8053,6 +8065,10 @@ ValueDecl *ClangImporter::getOriginalForClonedMember(const ValueDecl *decl) {
80538065
return Impl.getOriginalForClonedMember(decl);
80548066
}
80558067

8068+
bool ClangImporter::isPerTypeSynthesizedMember(const ValueDecl *decl) {
8069+
return Impl.isPerTypeSynthesizedMember(decl);
8070+
}
8071+
80568072
void ClangImporter::diagnoseTopLevelValue(const DeclName &name) {
80578073
Impl.diagnoseTopLevelValue(name);
80588074
}
@@ -8212,6 +8228,10 @@ importer::getValueDeclsForName(NominalTypeDecl *decl, StringRef name) {
82128228
if (memberName.empty())
82138229
return {};
82148230
auto declName = DeclName(ctx.getIdentifier(memberName));
8231+
auto swiftLookupResults = decl->lookupDirect(declName);
8232+
if (!swiftLookupResults.empty())
8233+
return SmallVector<ValueDecl *, 1>(swiftLookupResults.begin(),
8234+
swiftLookupResults.end());
82158235
auto allResults = evaluateOrDefault(
82168236
ctx.evaluator, ClangRecordMemberLookup({decl, declName}), {});
82178237
return SmallVector<ValueDecl *, 1>(allResults.begin(), allResults.end());
@@ -8242,9 +8262,9 @@ importer::getValueDeclsForName(NominalTypeDecl *decl, StringRef name) {
82428262
return results;
82438263
}
82448264

8245-
static const clang::RecordDecl *
8246-
getRefParentOrDiag(const clang::RecordDecl *decl, ASTContext &ctx,
8247-
ClangImporter::Implementation *importerImpl) {
8265+
const clang::RecordDecl *
8266+
importer::getRefParentOrDiag(const clang::RecordDecl *decl, ASTContext &ctx,
8267+
ClangImporter::Implementation *importerImpl) {
82488268
auto refParentDecls = getRefParentDecls(decl, ctx, importerImpl);
82498269
if (refParentDecls.empty())
82508270
return nullptr;
@@ -8811,19 +8831,12 @@ CustomRefCountingOperationResult CustomRefCountingOperation::evaluate(
88118831
Evaluator &evaluator, CustomRefCountingOperationDescriptor desc) const {
88128832
auto swiftDecl = desc.decl;
88138833
auto operation = desc.kind;
8814-
auto &ctx = swiftDecl->getASTContext();
88158834

8816-
std::string operationStr = operation == CustomRefCountingOperationKind::retain
8817-
? "retain:"
8818-
: "release:";
8835+
StringRef operationStr = operation == CustomRefCountingOperationKind::retain
8836+
? "retain:"
8837+
: "release:";
88198838

88208839
auto decl = cast<clang::RecordDecl>(swiftDecl->getClangDecl());
8821-
8822-
if (!hasImportAsRefAttr(decl)) {
8823-
if (auto parentRefDecl = getRefParentOrDiag(decl, ctx, nullptr))
8824-
decl = parentRefDecl;
8825-
}
8826-
88278840
if (!decl->hasAttrs())
88288841
return {CustomRefCountingOperationResult::noAttribute, nullptr, ""};
88298842

@@ -8842,10 +8855,8 @@ CustomRefCountingOperationResult CustomRefCountingOperation::evaluate(
88428855
if (retainReleaseAttrs.size() > 1)
88438856
return {CustomRefCountingOperationResult::tooManyAttributes, nullptr, ""};
88448857

8845-
auto name = retainReleaseAttrs.front()
8846-
->getAttribute()
8847-
.drop_front(StringRef(operationStr).size())
8848-
.str();
8858+
auto name = retainReleaseAttrs.front()->getAttribute().drop_front(
8859+
operationStr.size());
88498860

88508861
if (name == "immortal")
88518862
return {CustomRefCountingOperationResult::immortal, nullptr, name};

lib/ClangImporter/ImportDecl.cpp

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2190,6 +2190,40 @@ namespace {
21902190
return true;
21912191
}
21922192

2193+
std::pair<CustomRefCountingOperationResult,
2194+
CustomRefCountingOperationResult>
2195+
addRefCountOperationsIfRequired(ClassDecl *nominal,
2196+
clang::RecordDecl *clangType) {
2197+
auto &context = Impl.SwiftContext;
2198+
auto nonInheritedRefCountingOperations = [&] {
2199+
auto retainResult = evaluateOrDefault(
2200+
context.evaluator,
2201+
CustomRefCountingOperation(
2202+
{nominal, CustomRefCountingOperationKind::retain}),
2203+
{});
2204+
auto releaseResult = evaluateOrDefault(
2205+
context.evaluator,
2206+
CustomRefCountingOperation(
2207+
{nominal, CustomRefCountingOperationKind::release}),
2208+
{});
2209+
return std::make_pair(retainResult, releaseResult);
2210+
};
2211+
auto clangDecl = dyn_cast<clang::CXXRecordDecl>(clangType);
2212+
if (!clangDecl) {
2213+
return nonInheritedRefCountingOperations();
2214+
}
2215+
auto baseClangDecl = dyn_cast_or_null<clang::CXXRecordDecl>(
2216+
getRefParentOrDiag(clangDecl, context, nullptr));
2217+
if (!baseClangDecl || baseClangDecl == clangDecl)
2218+
return nonInheritedRefCountingOperations();
2219+
2220+
auto baseSwiftDecl = cast<ClassDecl>(
2221+
Impl.importDecl(baseClangDecl, getActiveSwiftVersion()));
2222+
2223+
return synthesizer.addRefCountOperations(nominal, clangDecl,
2224+
baseSwiftDecl, baseClangDecl);
2225+
}
2226+
21932227
Decl *VisitRecordDecl(const clang::RecordDecl *decl) {
21942228
// Track whether this record contains fields we can't reference in Swift
21952229
// as stored properties.
@@ -2786,7 +2820,11 @@ namespace {
27862820
}
27872821

27882822
if (auto classDecl = dyn_cast<ClassDecl>(result)) {
2789-
validateForeignReferenceType(decl, classDecl);
2823+
auto operations = addRefCountOperationsIfRequired(
2824+
classDecl, const_cast<clang::RecordDecl *>(decl));
2825+
2826+
validateForeignReferenceType(decl, classDecl, operations.first,
2827+
operations.second);
27902828

27912829
auto availability = Impl.SwiftContext.getSwift58Availability();
27922830
if (!availability.isAlwaysAvailable()) {
@@ -2853,8 +2891,10 @@ namespace {
28532891
}
28542892
}
28552893

2856-
void validateForeignReferenceType(const clang::RecordDecl *decl,
2857-
ClassDecl *classDecl) {
2894+
void validateForeignReferenceType(
2895+
const clang::RecordDecl *decl, ClassDecl *classDecl,
2896+
CustomRefCountingOperationResult retainOperation,
2897+
CustomRefCountingOperationResult releaseOperation) {
28582898

28592899
enum class RetainReleaseOperationKind {
28602900
notAfunction,
@@ -2937,11 +2977,6 @@ namespace {
29372977
return RetainReleaseOperationKind::valid;
29382978
};
29392979

2940-
auto retainOperation = evaluateOrDefault(
2941-
Impl.SwiftContext.evaluator,
2942-
CustomRefCountingOperation(
2943-
{classDecl, CustomRefCountingOperationKind::retain}),
2944-
{});
29452980
if (retainOperation.kind ==
29462981
CustomRefCountingOperationResult::noAttribute) {
29472982
HeaderLoc loc(decl->getLocation());
@@ -3008,11 +3043,6 @@ namespace {
30083043
CustomRefCountingOperationResult::immortal);
30093044
}
30103045

3011-
auto releaseOperation = evaluateOrDefault(
3012-
Impl.SwiftContext.evaluator,
3013-
CustomRefCountingOperation(
3014-
{classDecl, CustomRefCountingOperationKind::release}),
3015-
{});
30163046
if (releaseOperation.kind ==
30173047
CustomRefCountingOperationResult::noAttribute) {
30183048
HeaderLoc loc(decl->getLocation());

lib/ClangImporter/ImporterImpl.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#include "clang/Serialization/ModuleFileExtension.h"
4949
#include "llvm/ADT/APSInt.h"
5050
#include "llvm/ADT/DenseMap.h"
51+
#include "llvm/ADT/DenseSet.h"
5152
#include "llvm/ADT/Hashing.h"
5253
#include "llvm/ADT/IntrusiveRefCntPtr.h"
5354
#include "llvm/ADT/SmallBitVector.h"
@@ -692,6 +693,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
692693

693694
// Map all cloned methods back to the original member
694695
llvm::DenseMap<ValueDecl *, ValueDecl *> clonedMembers;
696+
llvm::DenseSet<const ValueDecl *> perTypeSynthesizedMembers;
695697

696698
// Keep track of methods that are unavailale in each class.
697699
// We need this set because these methods will be imported lazily. We don't
@@ -714,6 +716,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
714716
ClangInheritanceInfo inheritance);
715717

716718
ValueDecl *getOriginalForClonedMember(const ValueDecl *decl);
719+
bool isPerTypeSynthesizedMember(const ValueDecl *decl);
720+
void markPerTypeSynthesizedMember(const ValueDecl *decl);
717721

718722
static size_t getImportedBaseMemberDeclArity(const ValueDecl *valueDecl);
719723

@@ -2231,6 +2235,11 @@ getImplicitObjectParamAnnotation(const clang::FunctionDecl *FD) {
22312235
return nullptr;
22322236
}
22332237

2238+
/// Find a unique base class that is annotated as SHARED_REFERENCE if any.
2239+
const clang::RecordDecl *
2240+
getRefParentOrDiag(const clang::RecordDecl *decl, ASTContext &ctx,
2241+
ClangImporter::Implementation *importerImpl);
2242+
22342243
} // end namespace importer
22352244
} // end namespace swift
22362245

0 commit comments

Comments
 (0)