Skip to content

Commit fff2fd4

Browse files
committed
[cxx-interop] Check template argument safety in ClangDeclExplicitSafety
Checking this upon import may overlook annotations that explicitly mark a type as safe (or unsafe). Instead, we consolidate this logic in the ClangDeclExplicitSafety request. rdar://163196609
1 parent a302d4e commit fff2fd4

File tree

2 files changed

+42
-15
lines changed

2 files changed

+42
-15
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8777,14 +8777,14 @@ static bool hasUnsafeType(Evaluator &evaluator, clang::QualType clangType) {
87778777
// Function pointers are okay.
87788778
if (pointeeType->isFunctionType())
87798779
return false;
8780-
8780+
87818781
// Pointers to record types are okay if they come in as foreign reference
87828782
// types.
8783-
if (auto recordDecl = pointeeType->getAsRecordDecl()) {
8783+
if (auto *recordDecl = pointeeType->getAsRecordDecl()) {
87848784
if (hasImportAsRefAttr(recordDecl))
87858785
return false;
87868786
}
8787-
8787+
87888788
// All other pointers are considered unsafe.
87898789
return true;
87908790
}
@@ -8793,19 +8793,17 @@ static bool hasUnsafeType(Evaluator &evaluator, clang::QualType clangType) {
87938793
if (auto recordDecl = clangType->getAsTagDecl()) {
87948794
// If we reached this point the types is not imported as a shared reference,
87958795
// so we don't need to check the bases whether they are shared references.
8796-
auto safety = evaluateOrDefault(
8797-
evaluator, ClangDeclExplicitSafety({recordDecl, false}),
8798-
ExplicitSafety::Unspecified);
8799-
switch (safety) {
8800-
case ExplicitSafety::Unsafe:
8801-
return true;
8802-
8803-
case ExplicitSafety::Safe:
8804-
case ExplicitSafety::Unspecified:
8805-
return false;
8806-
}
8796+
auto req = ClangDeclExplicitSafety({recordDecl, false});
8797+
if (evaluator.hasActiveRequest(req))
8798+
// Cycles are allowed in templates, e.g.:
8799+
// template <typename> class Foo { ... }; // throws away template arg
8800+
// template <typename T> class Bar : Foo<Bar<T>> { ... };
8801+
// We need to avoid them here.
8802+
return false;
8803+
return evaluateOrDefault(evaluator, req, ExplicitSafety::Unspecified) ==
8804+
ExplicitSafety::Unsafe;
88078805
}
8808-
8806+
88098807
// Everything else is safe.
88108808
return false;
88118809
}

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,17 @@ struct OwnedData {
8383
void takeSharedObject(SharedObject *) const;
8484
};
8585

86+
// A class template that throws away its type argument.
87+
//
88+
// If this template is instantiated with an unsafe type, it should be considered
89+
// unsafe even if that type is never used.
90+
template <typename> struct TTake {};
91+
92+
using TTakeInt = TTake<int>;
93+
using TTakePtr = TTake<int *>;
94+
using TTakeSafeTuple = TTake<SafeTuple>;
95+
using TTakeUnsafeTuple = TTake<UnsafeTuple>;
96+
8697
struct HoldsShared {
8798
SharedObject* obj;
8899

@@ -184,3 +195,21 @@ func useSharedReference(frt: DerivedFromSharedObject, h: HoldsShared) {
184195
let _ = frt
185196
let _ = h.getObj()
186197
}
198+
199+
func useTTakeInt(x: TTakeInt) {
200+
_ = x
201+
}
202+
203+
func useTTakePtr(x: TTakePtr) {
204+
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
205+
_ = x // expected-note{{reference to parameter 'x' involves unsafe type}}
206+
}
207+
208+
func useTTakeSafeTuple(x: TTakeSafeTuple) {
209+
_ = x
210+
}
211+
212+
func useTTakeUnsafeTuple(x: TTakeUnsafeTuple) {
213+
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
214+
_ = x // expected-note{{reference to parameter 'x' involves unsafe type}}
215+
}

0 commit comments

Comments
 (0)