Skip to content

Commit d5b3079

Browse files
authored
Merge pull request #85379 from j-hui/dont-import-template-type-arguments-round-2
[cxx-interop] Make ClangDeclExplicitSafety request non-recursive [cxx-interop] Do not import template type arguments [cxx-interop] Check template argument safety in ClangDeclExplicitSafety
2 parents 2414958 + 6b3f8c7 commit d5b3079

File tree

7 files changed

+235
-131
lines changed

7 files changed

+235
-131
lines changed

include/swift/AST/Evaluator.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,10 @@ class Evaluator {
304304
void clearCache() { cache.clear(); }
305305

306306
/// Is the given request, or an equivalent, currently being evaluated?
307+
///
308+
/// WARN: do not rely on this function to avoid request cycles. Doing so can
309+
/// lead to bugs that are very difficult to debug, especially when request
310+
/// caching is involved.
307311
template <typename Request>
308312
bool hasActiveRequest(const Request &request) const {
309313
return activeRequests.count(ActiveRequest(request));

include/swift/ClangImporter/ClangImporterRequests.h

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -624,37 +624,37 @@ class CxxValueSemantics
624624
void simple_display(llvm::raw_ostream &out, CxxValueSemanticsDescriptor desc);
625625
SourceLoc extractNearestSourceLoc(CxxValueSemanticsDescriptor desc);
626626

627-
struct CxxDeclExplicitSafetyDescriptor final {
627+
struct ClangDeclExplicitSafetyDescriptor final {
628628
const clang::Decl *decl;
629629
bool isClass;
630630

631-
CxxDeclExplicitSafetyDescriptor(const clang::Decl *decl, bool isClass)
631+
ClangDeclExplicitSafetyDescriptor(const clang::Decl *decl, bool isClass)
632632
: decl(decl), isClass(isClass) {}
633633

634634
friend llvm::hash_code
635-
hash_value(const CxxDeclExplicitSafetyDescriptor &desc) {
635+
hash_value(const ClangDeclExplicitSafetyDescriptor &desc) {
636636
return llvm::hash_combine(desc.decl, desc.isClass);
637637
}
638638

639-
friend bool operator==(const CxxDeclExplicitSafetyDescriptor &lhs,
640-
const CxxDeclExplicitSafetyDescriptor &rhs) {
639+
friend bool operator==(const ClangDeclExplicitSafetyDescriptor &lhs,
640+
const ClangDeclExplicitSafetyDescriptor &rhs) {
641641
return lhs.decl == rhs.decl && lhs.isClass == rhs.isClass;
642642
}
643643

644-
friend bool operator!=(const CxxDeclExplicitSafetyDescriptor &lhs,
645-
const CxxDeclExplicitSafetyDescriptor &rhs) {
644+
friend bool operator!=(const ClangDeclExplicitSafetyDescriptor &lhs,
645+
const ClangDeclExplicitSafetyDescriptor &rhs) {
646646
return !(lhs == rhs);
647647
}
648648
};
649649

650650
void simple_display(llvm::raw_ostream &out,
651-
CxxDeclExplicitSafetyDescriptor desc);
652-
SourceLoc extractNearestSourceLoc(CxxDeclExplicitSafetyDescriptor desc);
651+
ClangDeclExplicitSafetyDescriptor desc);
652+
SourceLoc extractNearestSourceLoc(ClangDeclExplicitSafetyDescriptor desc);
653653

654654
/// Determine the safety of the given Clang declaration.
655655
class ClangDeclExplicitSafety
656656
: public SimpleRequest<ClangDeclExplicitSafety,
657-
ExplicitSafety(CxxDeclExplicitSafetyDescriptor),
657+
ExplicitSafety(ClangDeclExplicitSafetyDescriptor),
658658
RequestFlags::Cached> {
659659
public:
660660
using SimpleRequest::SimpleRequest;
@@ -668,7 +668,7 @@ class ClangDeclExplicitSafety
668668

669669
// Evaluation.
670670
ExplicitSafety evaluate(Evaluator &evaluator,
671-
CxxDeclExplicitSafetyDescriptor desc) const;
671+
ClangDeclExplicitSafetyDescriptor desc) const;
672672
};
673673

674674
#define SWIFT_TYPEID_ZONE ClangImporter

include/swift/ClangImporter/ClangImporterTypeIDZone.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,5 @@ SWIFT_REQUEST(ClangImporter, CxxValueSemantics,
4949
CxxValueSemanticsKind(CxxValueSemanticsDescriptor), Cached,
5050
NoLocationInfo)
5151
SWIFT_REQUEST(ClangImporter, ClangDeclExplicitSafety,
52-
ExplicitSafety(CxxDeclExplicitSafetyDescriptor), Cached,
52+
ExplicitSafety(ClangDeclExplicitSafetyDescriptor), Cached,
5353
NoLocationInfo)

lib/ClangImporter/ClangImporter.cpp

Lines changed: 121 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
#include "clang/Serialization/ObjectFilePCHContainerReader.h"
9494
#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
9595
#include "clang/Tooling/DependencyScanning/ScanAndUpdateArgs.h"
96+
#include "llvm/ADT/DenseSet.h"
9697
#include "llvm/ADT/IntrusiveRefCntPtr.h"
9798
#include "llvm/ADT/STLExtras.h"
9899
#include "llvm/ADT/SmallVector.h"
@@ -8704,7 +8705,7 @@ SourceLoc swift::extractNearestSourceLoc(SafeUseOfCxxDeclDescriptor desc) {
87048705
}
87058706

87068707
void swift::simple_display(llvm::raw_ostream &out,
8707-
CxxDeclExplicitSafetyDescriptor desc) {
8708+
ClangDeclExplicitSafetyDescriptor desc) {
87088709
out << "Checking if '";
87098710
if (auto namedDecl = dyn_cast<clang::NamedDecl>(desc.decl))
87108711
out << namedDecl->getNameAsString();
@@ -8713,7 +8714,7 @@ void swift::simple_display(llvm::raw_ostream &out,
87138714
out << "' is explicitly safe.\n";
87148715
}
87158716

8716-
SourceLoc swift::extractNearestSourceLoc(CxxDeclExplicitSafetyDescriptor desc) {
8717+
SourceLoc swift::extractNearestSourceLoc(ClangDeclExplicitSafetyDescriptor desc) {
87178718
return SourceLoc();
87188719
}
87198720

@@ -8772,103 +8773,132 @@ CustomRefCountingOperationResult CustomRefCountingOperation::evaluate(
87728773
return {CustomRefCountingOperationResult::tooManyFound, nullptr, name};
87738774
}
87748775

8775-
/// Check whether the given Clang type involves an unsafe type.
8776-
static bool hasUnsafeType(Evaluator &evaluator, clang::QualType clangType) {
8777-
// Handle pointers.
8778-
auto pointeeType = clangType->getPointeeType();
8779-
if (!pointeeType.isNull()) {
8780-
// Function pointers are okay.
8781-
if (pointeeType->isFunctionType())
8782-
return false;
8783-
8784-
// Pointers to record types are okay if they come in as foreign reference
8785-
// types.
8786-
if (auto recordDecl = pointeeType->getAsRecordDecl()) {
8787-
if (hasImportAsRefAttr(recordDecl))
8788-
return false;
8776+
ExplicitSafety ClangDeclExplicitSafety::evaluate(
8777+
Evaluator &evaluator, ClangDeclExplicitSafetyDescriptor desc) const {
8778+
// FIXME: Also similar to hasPointerInSubobjects
8779+
// FIXME: should probably also subsume IsSafeUseOfCxxDecl
8780+
8781+
if (desc.isClass)
8782+
// Safety for class types is handled a bit differently than other types.
8783+
// If it is not explicitly marked unsafe, it is always explicitly safe.
8784+
return hasSwiftAttribute(desc.decl, "unsafe") ? ExplicitSafety::Unsafe
8785+
: ExplicitSafety::Safe;
8786+
8787+
// Clang record types are considered explicitly unsafe if any of their fields,
8788+
// base classes, and template type parameters are unsafe. We use a stack for
8789+
// this recursive traversal.
8790+
//
8791+
// Invariant: if any Decl in the stack is unsafe, then desc.decl is unsafe.
8792+
llvm::SmallVector<const clang::Decl *, 4> stack;
8793+
8794+
// Keep track of which Decls we've seen to avoid cycles.
8795+
llvm::SmallDenseSet<const clang::Decl *, 4> seen;
8796+
8797+
// Check whether a type is unsafe. This function may also push to the stack.
8798+
auto isUnsafe = [&](clang::QualType type) -> bool {
8799+
auto pointeeType = type->getPointeeType();
8800+
if (!pointeeType.isNull()) {
8801+
if (pointeeType->isFunctionType())
8802+
return false; // Function pointers are not unsafe
8803+
auto *recordDecl = pointeeType->getAsRecordDecl();
8804+
if (recordDecl && hasImportAsRefAttr(recordDecl))
8805+
return false; // Pointers are ok if imported as foreign reference types
8806+
return true; // All other pointers are considered unsafe.
8807+
}
8808+
if (auto *decl = type->getAsTagDecl()) {
8809+
// We need to check the safety of the TagDecl corresponding to this type
8810+
if (seen.insert(decl).second)
8811+
// Only visit decl if we have not seen it before, to avoid cycles
8812+
stack.push_back(decl);
8813+
}
8814+
return false; // This type does not look unsafe on its own
8815+
};
8816+
8817+
stack.push_back(desc.decl);
8818+
seen.insert(desc.decl);
8819+
while (!stack.empty()) {
8820+
const clang::Decl *decl = stack.back();
8821+
stack.pop_back();
8822+
8823+
// Found unsafe; whether decl == desc.decl or not, desc.decl is unsafe
8824+
// (see invariant, above)
8825+
if (hasSwiftAttribute(decl, "unsafe"))
8826+
return ExplicitSafety::Unsafe;
8827+
8828+
if (hasSwiftAttribute(decl, "safe"))
8829+
continue;
8830+
8831+
// Enums are always safe
8832+
if (isa<clang::EnumDecl>(decl))
8833+
continue;
8834+
8835+
auto *recordDecl = dyn_cast<clang::RecordDecl>(decl);
8836+
if (!recordDecl) {
8837+
if (decl == desc.decl)
8838+
// If desc.decl is not a RecordDecl or EnumDecl, safety is unspecified.
8839+
return ExplicitSafety::Unspecified;
8840+
// If we encountered non-Record non-Enum decl during recursive traversal,
8841+
// we need to continue checking safety of other decls.
8842+
continue;
87898843
}
8790-
8791-
// All other pointers are considered unsafe.
8792-
return true;
8793-
}
87948844

8795-
// Handle records recursively.
8796-
if (auto recordDecl = clangType->getAsTagDecl()) {
8797-
// If we reached this point the types is not imported as a shared reference,
8798-
// so we don't need to check the bases whether they are shared references.
8799-
auto safety = evaluateOrDefault(
8800-
evaluator, ClangDeclExplicitSafety({recordDecl, false}),
8801-
ExplicitSafety::Unspecified);
8802-
switch (safety) {
8803-
case ExplicitSafety::Unsafe:
8804-
return true;
8805-
8806-
case ExplicitSafety::Safe:
8807-
case ExplicitSafety::Unspecified:
8808-
return false;
8845+
// Escapability annotations imply that the declaration is safe
8846+
if (evaluateOrDefault(
8847+
evaluator,
8848+
ClangTypeEscapability({recordDecl->getTypeForDecl(), nullptr}),
8849+
CxxEscapability::Unknown) != CxxEscapability::Unknown)
8850+
continue;
8851+
8852+
// A template class is unsafe if any of its type arguments are unsafe.
8853+
// Note that this does not rely on the record being defined.
8854+
if (auto *specDecl =
8855+
dyn_cast<clang::ClassTemplateSpecializationDecl>(recordDecl)) {
8856+
for (auto arg : specDecl->getTemplateArgs().asArray()) {
8857+
switch (arg.getKind()) {
8858+
case clang::TemplateArgument::Type:
8859+
if (isUnsafe(arg.getAsType()))
8860+
return ExplicitSafety::Unsafe;
8861+
break;
8862+
case clang::TemplateArgument::Pack:
8863+
for (auto pkArg : arg.getPackAsArray()) {
8864+
if (pkArg.getKind() == clang::TemplateArgument::Type &&
8865+
isUnsafe(pkArg.getAsType()))
8866+
return ExplicitSafety::Unsafe;
8867+
}
8868+
break;
8869+
default:
8870+
continue;
8871+
}
8872+
}
88098873
}
8810-
}
8811-
8812-
// Everything else is safe.
8813-
return false;
8814-
}
88158874

8816-
ExplicitSafety
8817-
ClangDeclExplicitSafety::evaluate(Evaluator &evaluator,
8818-
CxxDeclExplicitSafetyDescriptor desc) const {
8819-
// FIXME: Also similar to hasPointerInSubobjects
8820-
// FIXME: should probably also subsume IsSafeUseOfCxxDecl
8821-
8822-
// Explicitly unsafe.
8823-
auto decl = desc.decl;
8824-
if (hasSwiftAttribute(decl, "unsafe"))
8825-
return ExplicitSafety::Unsafe;
8826-
8827-
// Explicitly safe.
8828-
if (hasSwiftAttribute(decl, "safe"))
8829-
return ExplicitSafety::Safe;
8830-
8831-
// Shared references are considered safe.
8832-
if (desc.isClass)
8833-
return ExplicitSafety::Safe;
8834-
8835-
// Enums are always safe.
8836-
if (isa<clang::EnumDecl>(decl))
8837-
return ExplicitSafety::Safe;
8838-
8839-
// If it's not a record, leave it unspecified.
8840-
auto recordDecl = dyn_cast<clang::RecordDecl>(decl);
8841-
if (!recordDecl)
8842-
return ExplicitSafety::Unspecified;
8843-
8844-
// Escapable and non-escapable annotations imply that the declaration is
8845-
// safe.
8846-
if (evaluateOrDefault(
8847-
evaluator,
8848-
ClangTypeEscapability({recordDecl->getTypeForDecl(), nullptr}),
8849-
CxxEscapability::Unknown) != CxxEscapability::Unknown)
8850-
return ExplicitSafety::Safe;
8851-
8852-
// If we don't have a definition, leave it unspecified.
8853-
recordDecl = recordDecl->getDefinition();
8854-
if (!recordDecl)
8855-
return ExplicitSafety::Unspecified;
8856-
8857-
// If this is a C++ class, check its bases.
8858-
if (auto cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(recordDecl)) {
8859-
for (auto base : cxxRecordDecl->bases()) {
8860-
if (hasUnsafeType(evaluator, base.getType()))
8875+
recordDecl = recordDecl->getDefinition();
8876+
if (!recordDecl) {
8877+
if (decl == desc.decl)
8878+
// If desc.decl doesn't have a definition, safety is unspecified.
8879+
return ExplicitSafety::Unspecified;
8880+
// If we encountered decl without definition during recursive traversal,
8881+
// we need to continue checking safety of other decls.
8882+
continue;
8883+
}
8884+
8885+
if (auto *cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(recordDecl)) {
8886+
for (auto base : cxxRecordDecl->bases()) {
8887+
if (isUnsafe(base.getType()))
8888+
return ExplicitSafety::Unsafe;
8889+
}
8890+
}
8891+
8892+
for (auto *field : recordDecl->fields()) {
8893+
if (isUnsafe(field->getType()))
88618894
return ExplicitSafety::Unsafe;
88628895
}
88638896
}
8864-
8865-
// Check the fields.
8866-
for (auto field : recordDecl->fields()) {
8867-
if (hasUnsafeType(evaluator, field->getType()))
8868-
return ExplicitSafety::Unsafe;
8869-
}
88708897

8871-
// Okay, call it safe.
8898+
// desc.decl isn't explicitly marked unsafe, and none of the types/decls
8899+
// reachable from desc.decl are considered unsafe either. Cases where we would
8900+
// consider desc.decl's safety unspecified should have returned early from the
8901+
// loop. Thus, we can conclude that desc.decl is safe.
88728902
return ExplicitSafety::Safe;
88738903
}
88748904

lib/ClangImporter/ImportDecl.cpp

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2300,34 +2300,6 @@ namespace {
23002300
}
23012301
}
23022302

2303-
// We have to do this after populating ImportedDecls to avoid importing
2304-
// the same decl multiple times. Also after we imported the bases.
2305-
if (const auto *ctsd =
2306-
dyn_cast<clang::ClassTemplateSpecializationDecl>(decl)) {
2307-
for (auto arg : ctsd->getTemplateArgs().asArray()) {
2308-
llvm::SmallVector<clang::TemplateArgument, 1> nonPackArgs;
2309-
if (arg.getKind() == clang::TemplateArgument::Pack) {
2310-
auto pack = arg.getPackAsArray();
2311-
nonPackArgs.assign(pack.begin(), pack.end());
2312-
} else {
2313-
nonPackArgs.push_back(arg);
2314-
}
2315-
for (auto realArg : nonPackArgs) {
2316-
if (realArg.getKind() != clang::TemplateArgument::Type)
2317-
continue;
2318-
auto SwiftType = Impl.importTypeIgnoreIUO(
2319-
realArg.getAsType(), ImportTypeKind::Abstract,
2320-
[](Diagnostic &&diag) {}, false, Bridgeability::None,
2321-
ImportTypeAttrs());
2322-
if (SwiftType && SwiftType->isUnsafe()) {
2323-
auto attr = new (Impl.SwiftContext) UnsafeAttr(/*implicit=*/true);
2324-
result->getAttrs().add(attr);
2325-
break;
2326-
}
2327-
}
2328-
}
2329-
}
2330-
23312303
bool isNonEscapable = false;
23322304
if (evaluateOrDefault(
23332305
Impl.SwiftContext.evaluator,

0 commit comments

Comments
 (0)