diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 1ef8097e96bb2..05119d7af3dee 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -8709,9 +8709,14 @@ ExplicitSafety ClangTypeExplicitSafety::evaluate( if (auto recordDecl = clangType->getAsTagDecl()) { // If we reached this point the types is not imported as a shared reference, // so we don't need to check the bases whether they are shared references. - return evaluateOrDefault(evaluator, - ClangDeclExplicitSafety({recordDecl, false}), - ExplicitSafety::Unspecified); + auto req = ClangDeclExplicitSafety({recordDecl, false}); + if (evaluator.hasActiveRequest(req)) + // Cycles are allowed in templates, e.g.: + // template class Foo { ... }; // throws away template arg + // template class Bar : Foo> { ... }; + // We need to avoid them here. + return ExplicitSafety::Unspecified; + return evaluateOrDefault(evaluator, req, ExplicitSafety::Unspecified); } // Everything else is safe. @@ -8831,6 +8836,29 @@ ClangDeclExplicitSafety::evaluate(Evaluator &evaluator, ClangTypeEscapability({recordDecl->getTypeForDecl(), nullptr}), CxxEscapability::Unknown) != CxxEscapability::Unknown) return ExplicitSafety::Safe; + + // A template class is unsafe if any of its type arguments are unsafe. + // Note that this does not rely on the record being defined. + if (const auto *ctsd = + dyn_cast(recordDecl)) { + for (auto arg : ctsd->getTemplateArgs().asArray()) { + switch (arg.getKind()) { + case clang::TemplateArgument::Type: + if (hasUnsafeType(evaluator, arg.getAsType())) + return ExplicitSafety::Unsafe; + break; + case clang::TemplateArgument::Pack: + for (auto pkArg : arg.getPackAsArray()) { + if (pkArg.getKind() == clang::TemplateArgument::Type && + hasUnsafeType(evaluator, pkArg.getAsType())) + return ExplicitSafety::Unsafe; + } + break; + default: + continue; + } + } + } // If we don't have a definition, leave it unspecified. recordDecl = recordDecl->getDefinition(); @@ -8844,7 +8872,7 @@ ClangDeclExplicitSafety::evaluate(Evaluator &evaluator, return ExplicitSafety::Unsafe; } } - + // Check the fields. for (auto field : recordDecl->fields()) { if (hasUnsafeType(evaluator, field->getType())) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 044ab04cc65a2..def382a3a180d 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -2300,40 +2300,6 @@ namespace { } } - if (const auto *ctsd = - dyn_cast(decl)) { - for (auto arg : ctsd->getTemplateArgs().asArray()) { - auto done = false; - auto checkUnsafe = [&](clang::TemplateArgument tyArg) { - if (tyArg.getKind() != clang::TemplateArgument::Type) - return; - - auto safety = - evaluateOrDefault(Impl.SwiftContext.evaluator, - ClangTypeExplicitSafety({tyArg.getAsType()}), - ExplicitSafety::Unspecified); - - if (safety == ExplicitSafety::Unsafe) { - result->addAttribute(new (Impl.SwiftContext) - UnsafeAttr(/*implicit=*/true)); - done = true; - } - }; - - if (arg.getKind() == clang::TemplateArgument::Pack) { - for (auto pkArg : arg.getPackAsArray()) { - checkUnsafe(pkArg); - if (done) - break; - } - } else { - checkUnsafe(arg); - } - if (done) - break; - } - } - bool isNonEscapable = false; if (evaluateOrDefault( Impl.SwiftContext.evaluator, diff --git a/test/Interop/Cxx/class/safe-interop-mode.swift b/test/Interop/Cxx/class/safe-interop-mode.swift index c1ce9c0033a21..86faa3e40a0e7 100644 --- a/test/Interop/Cxx/class/safe-interop-mode.swift +++ b/test/Interop/Cxx/class/safe-interop-mode.swift @@ -94,6 +94,16 @@ using TTakePtr = TTake; using TTakeSafeTuple = TTake; using TTakeUnsafeTuple = TTake; +// An escapability or explicit safety annotation means a type is considered safe +// even if it would otherwise be considered unsafe. +template struct SWIFT_ESCAPABLE TEscape {}; +template struct __attribute__((swift_attr("safe"))) TSafe { void *ptr; }; + +using TEscapePtr = TEscape; +using TEscapeUnsafeTuple = TEscape; +using TSafePtr = TSafe; +using TSafeTuple = TSafe; + struct HoldsShared { SharedObject* obj; @@ -212,3 +222,8 @@ func useTTakeUnsafeTuple(x: TTakeUnsafeTuple) { // expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}} _ = x // expected-note{{reference to parameter 'x' involves unsafe type}} } + +func useTEscapePtr(x: TEscapePtr) { _ = x } +func useTEscapeUnsafeTuple(x: TEscapeUnsafeTuple) { _ = x } +func useTSafePtr(x: TSafePtr) { _ = x } +func useTSafeTuple(x: TSafeTuple) { _ = x }