Skip to content

Commit 78dbbba

Browse files
authored
Merge pull request #64300 from zoecarver/user/zoecarver/97361196
[cxx-interop] Separate `CxxRecordSemantics` and `IsSafeUseOfCxxDecl` requests.
2 parents 20e262b + bcf4977 commit 78dbbba

File tree

2 files changed

+48
-33
lines changed

2 files changed

+48
-33
lines changed

include/swift/ClangImporter/ClangImporterRequests.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,12 @@ SourceLoc extractNearestSourceLoc(CxxRecordSemanticsDescriptor desc);
342342

343343
/// What pattern does this C++ API fit? Uses attributes such as
344344
/// import_owned and import_as_reference to determine the pattern.
345+
///
346+
/// Do not evaluate this request before importing has started. For example, it
347+
/// is OK to invoke this request when importing a decl, but it is not OK to
348+
/// import this request when importing names. This is because when importing
349+
/// names, Clang sema has not yet defined implicit special members, so the
350+
/// results will be flakey/incorrect.
345351
class CxxRecordSemantics
346352
: public SimpleRequest<CxxRecordSemantics,
347353
CxxRecordSemanticsKind(CxxRecordSemanticsDescriptor),

lib/ClangImporter/ClangImporter.cpp

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6312,6 +6312,19 @@ static bool hasImportAsRefAttr(const clang::RecordDecl *decl) {
63126312
});
63136313
}
63146314

6315+
// Is this a pointer to a foreign reference type.
6316+
static bool isForeignReferenceType(const clang::QualType type) {
6317+
if (!type->isPointerType())
6318+
return false;
6319+
6320+
auto pointeeType =
6321+
dyn_cast<clang::RecordType>(type->getPointeeType().getCanonicalType());
6322+
if (pointeeType == nullptr)
6323+
return false;
6324+
6325+
return hasImportAsRefAttr(pointeeType->getDecl());
6326+
}
6327+
63156328
static bool hasOwnedValueAttr(const clang::RecordDecl *decl) {
63166329
// Hard-coded special cases from the standard library (this will go away once
63176330
// API notes support namespaces).
@@ -6474,14 +6487,16 @@ CxxRecordSemanticsKind
64746487
CxxRecordSemantics::evaluate(Evaluator &evaluator,
64756488
CxxRecordSemanticsDescriptor desc) const {
64766489
const auto *decl = desc.decl;
6490+
auto &clangSema = desc.ctx.getClangModuleLoader()->getClangSema();
64776491

64786492
if (hasImportAsRefAttr(decl)) {
64796493
return CxxRecordSemanticsKind::Reference;
64806494
}
64816495

64826496
auto cxxDecl = dyn_cast<clang::CXXRecordDecl>(decl);
6483-
if (!cxxDecl)
6497+
if (!cxxDecl) {
64846498
return CxxRecordSemanticsKind::Trivial;
6499+
}
64856500

64866501
if (!hasRequiredValueTypeOperations(cxxDecl)) {
64876502
if (hasUnsafeAPIAttr(cxxDecl))
@@ -6523,58 +6538,52 @@ CxxRecordSemantics::evaluate(Evaluator &evaluator,
65236538
bool IsSafeUseOfCxxDecl::evaluate(Evaluator &evaluator,
65246539
SafeUseOfCxxDeclDescriptor desc) const {
65256540
const clang::Decl *decl = desc.decl;
6526-
const clang::CXXRecordDecl *recordDecl = nullptr;
6527-
bool cxxMethodIsSafe = true;
65286541

65296542
if (auto method = dyn_cast<clang::CXXMethodDecl>(decl)) {
6543+
// The user explicitly asked us to import this method.
65306544
if (hasUnsafeAPIAttr(method))
65316545
return true;
65326546

6547+
// If it's a static method, it cannot project anything. It's fine.
65336548
if (method->isOverloadedOperator() || method->isStatic() ||
65346549
isa<clang::CXXConstructorDecl>(decl))
65356550
return true;
65366551

6552+
if (isForeignReferenceType(method->getReturnType()))
6553+
return true;
6554+
6555+
// If it returns a pointer or reference, that's a projection.
65376556
if (method->getReturnType()->isPointerType() ||
65386557
method->getReturnType()->isReferenceType())
6539-
cxxMethodIsSafe = false;
6558+
return false;
65406559

6560+
// Try to figure out the semantics of the return type. If it's a
6561+
// pointer/iterator, it's unsafe.
65416562
if (auto returnType = dyn_cast<clang::RecordType>(
65426563
method->getReturnType().getCanonicalType())) {
65436564
if (auto cxxRecordReturnType =
65446565
dyn_cast<clang::CXXRecordDecl>(returnType->getDecl())) {
6545-
auto semanticsKind = evaluateOrDefault(
6546-
evaluator, CxxRecordSemantics({cxxRecordReturnType, desc.ctx}), {});
6547-
6548-
if (semanticsKind == CxxRecordSemanticsKind::UnsafePointerMember ||
6549-
// Pretend all methods that return iterators are unsafe so protocol
6550-
// conformances work.
6551-
semanticsKind == CxxRecordSemanticsKind::Iterator)
6552-
cxxMethodIsSafe = false;
6566+
if (hasIteratorAPIAttr(cxxRecordReturnType) ||
6567+
isIterator(cxxRecordReturnType)) {
6568+
return false;
6569+
}
6570+
6571+
// Mark this as safe to help our diganostics down the road.
6572+
if (!cxxRecordReturnType->getDefinition()) {
6573+
return true;
6574+
}
6575+
6576+
if (!cxxRecordReturnType->hasUserDeclaredCopyConstructor() &&
6577+
!cxxRecordReturnType->hasUserDeclaredMoveConstructor() &&
6578+
hasPointerInSubobjects(cxxRecordReturnType)) {
6579+
return false;
6580+
}
65536581
}
65546582
}
6555-
6556-
recordDecl = method->getParent();
6557-
} else if (auto cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(decl)) {
6558-
recordDecl = cxxRecordDecl;
6559-
} else {
6560-
llvm_unreachable("decl must be a C++ method or C++ record.");
65616583
}
65626584

6563-
auto semanticsKind = evaluateOrDefault(
6564-
evaluator, CxxRecordSemantics({recordDecl, desc.ctx}), {});
6565-
6566-
// Always unsafe.
6567-
if (semanticsKind == CxxRecordSemanticsKind::MissingLifetimeOperation)
6568-
return false;
6569-
6570-
// Always OK.
6571-
if (semanticsKind == CxxRecordSemanticsKind::Reference)
6572-
return true;
6573-
6574-
6575-
// All other record semantics kinds are some varient of an "owned" type, so
6576-
// dis-allow potential projections.
6577-
return cxxMethodIsSafe;
6585+
// Otherwise, it's safe.
6586+
return true;
65786587
}
65796588

65806589
void swift::simple_display(llvm::raw_ostream &out,

0 commit comments

Comments
 (0)