Skip to content

Commit afaa499

Browse files
committed
[cxx-interop] Mark class templates with unsafe type arguments as unsafe
Doing so preserves ClangImporter's behavior first introduced in f12b48a, but do so without relying on importing the type argument (since that can lead to template over-instantiation). This patch also promotes the logic of hasUnsafeType() to a standalone SimpleRequest, to provide a QualType-typed entry point for evaluating the safety of a Clang entity.
1 parent d5473fe commit afaa499

File tree

5 files changed

+161
-36
lines changed

5 files changed

+161
-36
lines changed

include/swift/ClangImporter/ClangImporterRequests.h

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

627+
struct ClangTypeExplicitSafetyDescriptor final {
628+
clang::QualType type;
629+
630+
ClangTypeExplicitSafetyDescriptor(clang::QualType type) : type(type) {}
631+
632+
friend llvm::hash_code
633+
hash_value(const ClangTypeExplicitSafetyDescriptor &desc) {
634+
return llvm::hash_combine(desc.type.getTypePtrOrNull());
635+
}
636+
637+
friend bool operator==(const ClangTypeExplicitSafetyDescriptor &lhs,
638+
const ClangTypeExplicitSafetyDescriptor &rhs) {
639+
return lhs.type == rhs.type;
640+
}
641+
642+
friend bool operator!=(const ClangTypeExplicitSafetyDescriptor &lhs,
643+
const ClangTypeExplicitSafetyDescriptor &rhs) {
644+
return !(lhs == rhs);
645+
}
646+
};
647+
648+
void simple_display(llvm::raw_ostream &out,
649+
ClangTypeExplicitSafetyDescriptor desc);
650+
SourceLoc extractNearestSourceLoc(ClangTypeExplicitSafetyDescriptor desc);
651+
652+
/// Determine the safety of the given Clang declaration.
653+
class ClangTypeExplicitSafety
654+
: public SimpleRequest<ClangTypeExplicitSafety,
655+
ExplicitSafety(ClangTypeExplicitSafetyDescriptor),
656+
RequestFlags::Cached> {
657+
public:
658+
using SimpleRequest::SimpleRequest;
659+
660+
// Source location
661+
SourceLoc getNearestLoc() const { return SourceLoc(); };
662+
bool isCached() const { return true; }
663+
664+
private:
665+
friend SimpleRequest;
666+
667+
// Evaluation.
668+
ExplicitSafety evaluate(Evaluator &evaluator,
669+
ClangTypeExplicitSafetyDescriptor desc) const;
670+
};
671+
627672
struct CxxDeclExplicitSafetyDescriptor final {
628673
const clang::Decl *decl;
629674
bool isClass;

include/swift/ClangImporter/ClangImporterTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ SWIFT_REQUEST(ClangImporter, ClangTypeEscapability,
4848
SWIFT_REQUEST(ClangImporter, CxxValueSemantics,
4949
CxxValueSemanticsKind(CxxValueSemanticsDescriptor), Cached,
5050
NoLocationInfo)
51+
SWIFT_REQUEST(ClangImporter, ClangTypeExplicitSafety,
52+
ExplicitSafety(ClangTypeExplicitSafetyDescriptor), Cached,
53+
NoLocationInfo)
5154
SWIFT_REQUEST(ClangImporter, ClangDeclExplicitSafety,
5255
ExplicitSafety(CxxDeclExplicitSafetyDescriptor), Cached,
5356
NoLocationInfo)

lib/ClangImporter/ClangImporter.cpp

Lines changed: 49 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8640,6 +8640,51 @@ SourceLoc swift::extractNearestSourceLoc(SafeUseOfCxxDeclDescriptor desc) {
86408640
return SourceLoc();
86418641
}
86428642

8643+
void swift::simple_display(llvm::raw_ostream &out,
8644+
ClangTypeExplicitSafetyDescriptor desc) {
8645+
out << "Checking if type '" << desc.type.getAsString()
8646+
<< "' is explicitly safe.\n";
8647+
}
8648+
8649+
SourceLoc swift::extractNearestSourceLoc(ClangTypeExplicitSafetyDescriptor desc) {
8650+
return SourceLoc();
8651+
}
8652+
8653+
ExplicitSafety ClangTypeExplicitSafety::evaluate(
8654+
Evaluator &evaluator, ClangTypeExplicitSafetyDescriptor desc) const {
8655+
auto clangType = desc.type;
8656+
8657+
// Handle pointers.
8658+
auto pointeeType = clangType->getPointeeType();
8659+
if (!pointeeType.isNull()) {
8660+
// Function pointers are okay.
8661+
if (pointeeType->isFunctionType())
8662+
return ExplicitSafety::Safe;
8663+
8664+
// Pointers to record types are okay if they come in as foreign reference
8665+
// types.
8666+
if (auto *recordDecl = pointeeType->getAsRecordDecl()) {
8667+
if (hasImportAsRefAttr(recordDecl))
8668+
return ExplicitSafety::Safe;
8669+
}
8670+
8671+
// All other pointers are considered unsafe.
8672+
return ExplicitSafety::Unsafe;
8673+
}
8674+
8675+
// Handle records recursively.
8676+
if (auto recordDecl = clangType->getAsTagDecl()) {
8677+
// If we reached this point the types is not imported as a shared reference,
8678+
// so we don't need to check the bases whether they are shared references.
8679+
return evaluateOrDefault(evaluator,
8680+
ClangDeclExplicitSafety({recordDecl, false}),
8681+
ExplicitSafety::Unspecified);
8682+
}
8683+
8684+
// Everything else is safe.
8685+
return ExplicitSafety::Safe;
8686+
}
8687+
86438688
void swift::simple_display(llvm::raw_ostream &out,
86448689
CxxDeclExplicitSafetyDescriptor desc) {
86458690
out << "Checking if '";
@@ -8711,43 +8756,11 @@ CustomRefCountingOperationResult CustomRefCountingOperation::evaluate(
87118756

87128757
/// Check whether the given Clang type involves an unsafe type.
87138758
static bool hasUnsafeType(Evaluator &evaluator, clang::QualType clangType) {
8714-
// Handle pointers.
8715-
auto pointeeType = clangType->getPointeeType();
8716-
if (!pointeeType.isNull()) {
8717-
// Function pointers are okay.
8718-
if (pointeeType->isFunctionType())
8719-
return false;
8720-
8721-
// Pointers to record types are okay if they come in as foreign reference
8722-
// types.
8723-
if (auto recordDecl = pointeeType->getAsRecordDecl()) {
8724-
if (hasImportAsRefAttr(recordDecl))
8725-
return false;
8726-
}
8727-
8728-
// All other pointers are considered unsafe.
8729-
return true;
8730-
}
87318759

8732-
// Handle records recursively.
8733-
if (auto recordDecl = clangType->getAsTagDecl()) {
8734-
// If we reached this point the types is not imported as a shared reference,
8735-
// so we don't need to check the bases whether they are shared references.
8736-
auto safety = evaluateOrDefault(
8737-
evaluator, ClangDeclExplicitSafety({recordDecl, false}),
8738-
ExplicitSafety::Unspecified);
8739-
switch (safety) {
8740-
case ExplicitSafety::Unsafe:
8741-
return true;
8742-
8743-
case ExplicitSafety::Safe:
8744-
case ExplicitSafety::Unspecified:
8745-
return false;
8746-
}
8747-
}
8748-
8749-
// Everything else is safe.
8750-
return false;
8760+
auto safety =
8761+
evaluateOrDefault(evaluator, ClangTypeExplicitSafety({clangType}),
8762+
ExplicitSafety::Unspecified);
8763+
return safety == ExplicitSafety::Unsafe;
87518764
}
87528765

87538766
ExplicitSafety

lib/ClangImporter/ImportDecl.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
#include "clang/AST/RecordLayout.h"
6969
#include "clang/AST/RecursiveASTVisitor.h"
7070
#include "clang/AST/StmtVisitor.h"
71+
#include "clang/AST/TemplateBase.h"
7172
#include "clang/AST/Type.h"
7273
#include "clang/Basic/Specifiers.h"
7374
#include "clang/Basic/TargetInfo.h"
@@ -2298,6 +2299,40 @@ namespace {
22982299
}
22992300
}
23002301

2302+
if (const auto *ctsd =
2303+
dyn_cast<clang::ClassTemplateSpecializationDecl>(decl)) {
2304+
for (auto arg : ctsd->getTemplateArgs().asArray()) {
2305+
auto done = false;
2306+
auto checkUnsafe = [&](clang::TemplateArgument tyArg) {
2307+
if (tyArg.getKind() != clang::TemplateArgument::Type)
2308+
return;
2309+
2310+
auto safety =
2311+
evaluateOrDefault(Impl.SwiftContext.evaluator,
2312+
ClangTypeExplicitSafety({tyArg.getAsType()}),
2313+
ExplicitSafety::Unspecified);
2314+
2315+
if (safety == ExplicitSafety::Unsafe) {
2316+
result->getAttrs().add(new (Impl.SwiftContext)
2317+
UnsafeAttr(/*implicit=*/true));
2318+
done = true;
2319+
}
2320+
};
2321+
2322+
if (arg.getKind() == clang::TemplateArgument::Pack) {
2323+
for (auto pkArg : arg.getPackAsArray()) {
2324+
checkUnsafe(pkArg);
2325+
if (done)
2326+
break;
2327+
}
2328+
} else {
2329+
checkUnsafe(arg);
2330+
}
2331+
if (done)
2332+
break;
2333+
}
2334+
}
2335+
23012336
bool isNonEscapable = false;
23022337
if (evaluateOrDefault(
23032338
Impl.SwiftContext.evaluator,

test/Interop/Cxx/class/safe-interop-mode.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,17 @@ struct OwnedData {
7878
void takeSharedObject(SharedObject *) const;
7979
};
8080

81+
// A class template that throws away its type argument.
82+
//
83+
// If this template is instantiated with an unsafe type, it should be considered
84+
// unsafe even if that type is never used.
85+
template <typename> struct TTake {};
86+
87+
using TTakeInt = TTake<int>;
88+
using TTakePtr = TTake<int *>;
89+
using TTakeSafeTuple = TTake<SafeTuple>;
90+
using TTakeUnsafeTuple = TTake<UnsafeTuple>;
91+
8192
//--- test.swift
8293

8394
import Test
@@ -167,3 +178,21 @@ func useSharedReference(frt: SharedObject, x: OwnedData) {
167178
func useSharedReference(frt: DerivedFromSharedObject) {
168179
let _ = frt
169180
}
181+
182+
func useTTakeInt(x: TTakeInt) {
183+
_ = x
184+
}
185+
186+
func useTTakePtr(x: TTakePtr) {
187+
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
188+
_ = x // expected-note{{reference to parameter 'x' involves unsafe type}}
189+
}
190+
191+
func useTTakeSafeTuple(x: TTakeSafeTuple) {
192+
_ = x
193+
}
194+
195+
func useTTakeUnsafeTuple(x: TTakeUnsafeTuple) {
196+
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
197+
_ = x // expected-note{{reference to parameter 'x' involves unsafe type}}
198+
}

0 commit comments

Comments
 (0)