Skip to content

Commit 35bcc99

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 35bcc99

File tree

13 files changed

+271
-46
lines changed

13 files changed

+271
-46
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 isMemberSynthesizedPerType(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 isMemberSynthesizedPerType(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: 35 additions & 26 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->isMemberSynthesizedPerType(namedMember) ||
65686569
clangModuleLoader->getOriginalForClonedMember(namedMember))
65696570
continue;
65706571

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

8037+
bool ClangImporter::Implementation::isMemberSynthesizedPerType(
8038+
const ValueDecl *decl) {
8039+
return membersSynthesizedPerType.contains(decl);
8040+
}
8041+
8042+
void ClangImporter::Implementation::markMemberSynthesizedPerType(
8043+
const ValueDecl *decl) {
8044+
membersSynthesizedPerType.insert(decl);
8045+
}
8046+
80368047
size_t ClangImporter::Implementation::getImportedBaseMemberDeclArity(
80378048
const ValueDecl *valueDecl) {
80388049
if (auto *func = dyn_cast<FuncDecl>(valueDecl)) {
@@ -8053,6 +8064,10 @@ ValueDecl *ClangImporter::getOriginalForClonedMember(const ValueDecl *decl) {
80538064
return Impl.getOriginalForClonedMember(decl);
80548065
}
80558066

8067+
bool ClangImporter::isMemberSynthesizedPerType(const ValueDecl *decl) {
8068+
return Impl.isMemberSynthesizedPerType(decl);
8069+
}
8070+
80568071
void ClangImporter::diagnoseTopLevelValue(const DeclName &name) {
80578072
Impl.diagnoseTopLevelValue(name);
80588073
}
@@ -8206,12 +8221,15 @@ importer::getValueDeclsForName(NominalTypeDecl *decl, StringRef name) {
82068221
auto clangDecl = decl->getClangDecl();
82078222
llvm::SmallVector<ValueDecl *, 1> results;
82088223

8209-
if (name.starts_with(".")) {
8224+
if (name.consume_front(".")) {
82108225
// Look for a member of decl instead of a global.
8211-
StringRef memberName = name.drop_front(1);
8212-
if (memberName.empty())
8226+
if (name.empty())
82138227
return {};
8214-
auto declName = DeclName(ctx.getIdentifier(memberName));
8228+
auto declName = DeclName(ctx.getIdentifier(name));
8229+
auto swiftLookupResults = decl->lookupDirect(declName);
8230+
if (!swiftLookupResults.empty())
8231+
return SmallVector<ValueDecl *, 1>(swiftLookupResults.begin(),
8232+
swiftLookupResults.end());
82158233
auto allResults = evaluateOrDefault(
82168234
ctx.evaluator, ClangRecordMemberLookup({decl, declName}), {});
82178235
return SmallVector<ValueDecl *, 1>(allResults.begin(), allResults.end());
@@ -8242,9 +8260,9 @@ importer::getValueDeclsForName(NominalTypeDecl *decl, StringRef name) {
82428260
return results;
82438261
}
82448262

8245-
static const clang::RecordDecl *
8246-
getRefParentOrDiag(const clang::RecordDecl *decl, ASTContext &ctx,
8247-
ClangImporter::Implementation *importerImpl) {
8263+
const clang::RecordDecl *
8264+
importer::getRefParentOrDiag(const clang::RecordDecl *decl, ASTContext &ctx,
8265+
ClangImporter::Implementation *importerImpl) {
82488266
auto refParentDecls = getRefParentDecls(decl, ctx, importerImpl);
82498267
if (refParentDecls.empty())
82508268
return nullptr;
@@ -8257,11 +8275,11 @@ getRefParentOrDiag(const clang::RecordDecl *decl, ASTContext &ctx,
82578275
assert(refParentDecl && "refParentDecl is null inside getRefParentOrDiag");
82588276
for (const auto *attr : refParentDecl->getAttrs()) {
82598277
if (const auto swiftAttr = llvm::dyn_cast<clang::SwiftAttrAttr>(attr)) {
8260-
const auto &attribute = swiftAttr->getAttribute();
8261-
if (attribute.starts_with(retainPrefix))
8262-
uniqueRetainDecls.insert(attribute.drop_front(retainPrefix.size()));
8263-
else if (attribute.starts_with(releasePrefix))
8264-
uniqueReleaseDecls.insert(attribute.drop_front(releasePrefix.size()));
8278+
auto attribute = swiftAttr->getAttribute();
8279+
if (attribute.consume_front(retainPrefix))
8280+
uniqueRetainDecls.insert(attribute);
8281+
else if (attribute.consume_front(releasePrefix))
8282+
uniqueReleaseDecls.insert(attribute);
82658283
}
82668284
}
82678285
}
@@ -8811,19 +8829,12 @@ CustomRefCountingOperationResult CustomRefCountingOperation::evaluate(
88118829
Evaluator &evaluator, CustomRefCountingOperationDescriptor desc) const {
88128830
auto swiftDecl = desc.decl;
88138831
auto operation = desc.kind;
8814-
auto &ctx = swiftDecl->getASTContext();
88158832

8816-
std::string operationStr = operation == CustomRefCountingOperationKind::retain
8817-
? "retain:"
8818-
: "release:";
8833+
StringRef operationStr = operation == CustomRefCountingOperationKind::retain
8834+
? "retain:"
8835+
: "release:";
88198836

88208837
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-
88278838
if (!decl->hasAttrs())
88288839
return {CustomRefCountingOperationResult::noAttribute, nullptr, ""};
88298840

@@ -8842,10 +8853,8 @@ CustomRefCountingOperationResult CustomRefCountingOperation::evaluate(
88428853
if (retainReleaseAttrs.size() > 1)
88438854
return {CustomRefCountingOperationResult::tooManyAttributes, nullptr, ""};
88448855

8845-
auto name = retainReleaseAttrs.front()
8846-
->getAttribute()
8847-
.drop_front(StringRef(operationStr).size())
8848-
.str();
8856+
auto name = retainReleaseAttrs.front()->getAttribute().drop_front(
8857+
operationStr.size());
88498858

88508859
if (name == "immortal")
88518860
return {CustomRefCountingOperationResult::immortal, nullptr, name};

lib/ClangImporter/ImportDecl.cpp

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2190,6 +2190,39 @@ 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+
auto baseClangDecl = dyn_cast_or_null<clang::CXXRecordDecl>(
2215+
getRefParentOrDiag(clangDecl, context, nullptr));
2216+
if (!baseClangDecl || baseClangDecl == clangDecl)
2217+
return nonInheritedRefCountingOperations();
2218+
2219+
auto baseSwiftDecl = cast<ClassDecl>(
2220+
Impl.importDecl(baseClangDecl, getActiveSwiftVersion()));
2221+
2222+
return synthesizer.addRefCountOperations(nominal, clangDecl,
2223+
baseSwiftDecl, baseClangDecl);
2224+
}
2225+
21932226
Decl *VisitRecordDecl(const clang::RecordDecl *decl) {
21942227
// Track whether this record contains fields we can't reference in Swift
21952228
// as stored properties.
@@ -2786,7 +2819,11 @@ namespace {
27862819
}
27872820

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

27912828
auto availability = Impl.SwiftContext.getSwift58Availability();
27922829
if (!availability.isAlwaysAvailable()) {
@@ -2853,8 +2890,10 @@ namespace {
28532890
}
28542891
}
28552892

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

28592898
enum class RetainReleaseOperationKind {
28602899
notAfunction,
@@ -2937,11 +2976,6 @@ namespace {
29372976
return RetainReleaseOperationKind::valid;
29382977
};
29392978

2940-
auto retainOperation = evaluateOrDefault(
2941-
Impl.SwiftContext.evaluator,
2942-
CustomRefCountingOperation(
2943-
{classDecl, CustomRefCountingOperationKind::retain}),
2944-
{});
29452979
if (retainOperation.kind ==
29462980
CustomRefCountingOperationResult::noAttribute) {
29472981
HeaderLoc loc(decl->getLocation());
@@ -3008,11 +3042,6 @@ namespace {
30083042
CustomRefCountingOperationResult::immortal);
30093043
}
30103044

3011-
auto releaseOperation = evaluateOrDefault(
3012-
Impl.SwiftContext.evaluator,
3013-
CustomRefCountingOperation(
3014-
{classDecl, CustomRefCountingOperationKind::release}),
3015-
{});
30163045
if (releaseOperation.kind ==
30173046
CustomRefCountingOperationResult::noAttribute) {
30183047
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 *> membersSynthesizedPerType;
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 isMemberSynthesizedPerType(const ValueDecl *decl);
720+
void markMemberSynthesizedPerType(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)