Skip to content

Commit bcf4977

Browse files
committed
[cxx-interop] Separate CxxRecordSemantics and IsSafeUseOfCxxDecl requests.
1 parent 1e4e7f7 commit bcf4977

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
@@ -6235,6 +6235,19 @@ static bool hasImportAsRefAttr(const clang::RecordDecl *decl) {
62356235
});
62366236
}
62376237

6238+
// Is this a pointer to a foreign reference type.
6239+
static bool isForeignReferenceType(const clang::QualType type) {
6240+
if (!type->isPointerType())
6241+
return false;
6242+
6243+
auto pointeeType =
6244+
dyn_cast<clang::RecordType>(type->getPointeeType().getCanonicalType());
6245+
if (pointeeType == nullptr)
6246+
return false;
6247+
6248+
return hasImportAsRefAttr(pointeeType->getDecl());
6249+
}
6250+
62386251
static bool hasOwnedValueAttr(const clang::RecordDecl *decl) {
62396252
// Hard-coded special cases from the standard library (this will go away once
62406253
// API notes support namespaces).
@@ -6397,14 +6410,16 @@ CxxRecordSemanticsKind
63976410
CxxRecordSemantics::evaluate(Evaluator &evaluator,
63986411
CxxRecordSemanticsDescriptor desc) const {
63996412
const auto *decl = desc.decl;
6413+
auto &clangSema = desc.ctx.getClangModuleLoader()->getClangSema();
64006414

64016415
if (hasImportAsRefAttr(decl)) {
64026416
return CxxRecordSemanticsKind::Reference;
64036417
}
64046418

64056419
auto cxxDecl = dyn_cast<clang::CXXRecordDecl>(decl);
6406-
if (!cxxDecl)
6420+
if (!cxxDecl) {
64076421
return CxxRecordSemanticsKind::Trivial;
6422+
}
64086423

64096424
if (!hasRequiredValueTypeOperations(cxxDecl)) {
64106425
if (hasUnsafeAPIAttr(cxxDecl))
@@ -6446,58 +6461,52 @@ CxxRecordSemantics::evaluate(Evaluator &evaluator,
64466461
bool IsSafeUseOfCxxDecl::evaluate(Evaluator &evaluator,
64476462
SafeUseOfCxxDeclDescriptor desc) const {
64486463
const clang::Decl *decl = desc.decl;
6449-
const clang::CXXRecordDecl *recordDecl = nullptr;
6450-
bool cxxMethodIsSafe = true;
64516464

64526465
if (auto method = dyn_cast<clang::CXXMethodDecl>(decl)) {
6466+
// The user explicitly asked us to import this method.
64536467
if (hasUnsafeAPIAttr(method))
64546468
return true;
64556469

6470+
// If it's a static method, it cannot project anything. It's fine.
64566471
if (method->isOverloadedOperator() || method->isStatic() ||
64576472
isa<clang::CXXConstructorDecl>(decl))
64586473
return true;
64596474

6475+
if (isForeignReferenceType(method->getReturnType()))
6476+
return true;
6477+
6478+
// If it returns a pointer or reference, that's a projection.
64606479
if (method->getReturnType()->isPointerType() ||
64616480
method->getReturnType()->isReferenceType())
6462-
cxxMethodIsSafe = false;
6481+
return false;
64636482

6483+
// Try to figure out the semantics of the return type. If it's a
6484+
// pointer/iterator, it's unsafe.
64646485
if (auto returnType = dyn_cast<clang::RecordType>(
64656486
method->getReturnType().getCanonicalType())) {
64666487
if (auto cxxRecordReturnType =
64676488
dyn_cast<clang::CXXRecordDecl>(returnType->getDecl())) {
6468-
auto semanticsKind = evaluateOrDefault(
6469-
evaluator, CxxRecordSemantics({cxxRecordReturnType, desc.ctx}), {});
6470-
6471-
if (semanticsKind == CxxRecordSemanticsKind::UnsafePointerMember ||
6472-
// Pretend all methods that return iterators are unsafe so protocol
6473-
// conformances work.
6474-
semanticsKind == CxxRecordSemanticsKind::Iterator)
6475-
cxxMethodIsSafe = false;
6489+
if (hasIteratorAPIAttr(cxxRecordReturnType) ||
6490+
isIterator(cxxRecordReturnType)) {
6491+
return false;
6492+
}
6493+
6494+
// Mark this as safe to help our diganostics down the road.
6495+
if (!cxxRecordReturnType->getDefinition()) {
6496+
return true;
6497+
}
6498+
6499+
if (!cxxRecordReturnType->hasUserDeclaredCopyConstructor() &&
6500+
!cxxRecordReturnType->hasUserDeclaredMoveConstructor() &&
6501+
hasPointerInSubobjects(cxxRecordReturnType)) {
6502+
return false;
6503+
}
64766504
}
64776505
}
6478-
6479-
recordDecl = method->getParent();
6480-
} else if (auto cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(decl)) {
6481-
recordDecl = cxxRecordDecl;
6482-
} else {
6483-
llvm_unreachable("decl must be a C++ method or C++ record.");
64846506
}
64856507

6486-
auto semanticsKind = evaluateOrDefault(
6487-
evaluator, CxxRecordSemantics({recordDecl, desc.ctx}), {});
6488-
6489-
// Always unsafe.
6490-
if (semanticsKind == CxxRecordSemanticsKind::MissingLifetimeOperation)
6491-
return false;
6492-
6493-
// Always OK.
6494-
if (semanticsKind == CxxRecordSemanticsKind::Reference)
6495-
return true;
6496-
6497-
6498-
// All other record semantics kinds are some varient of an "owned" type, so
6499-
// dis-allow potential projections.
6500-
return cxxMethodIsSafe;
6508+
// Otherwise, it's safe.
6509+
return true;
65016510
}
65026511

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

0 commit comments

Comments
 (0)