diff --git a/include/swift/ClangImporter/ClangImporterRequests.h b/include/swift/ClangImporter/ClangImporterRequests.h index ec626cb0e9164..fe83d0fae0405 100644 --- a/include/swift/ClangImporter/ClangImporterRequests.h +++ b/include/swift/ClangImporter/ClangImporterRequests.h @@ -624,53 +624,6 @@ class CxxValueSemantics void simple_display(llvm::raw_ostream &out, CxxValueSemanticsDescriptor desc); SourceLoc extractNearestSourceLoc(CxxValueSemanticsDescriptor desc); -struct ClangTypeExplicitSafetyDescriptor final { - clang::CanQualType type; - - ClangTypeExplicitSafetyDescriptor(clang::CanQualType type) : type(type) {} - ClangTypeExplicitSafetyDescriptor(clang::QualType type) - : type(type->getCanonicalTypeUnqualified()) {} - - friend llvm::hash_code - hash_value(const ClangTypeExplicitSafetyDescriptor &desc) { - return llvm::hash_combine(desc.type.getTypePtrOrNull()); - } - - friend bool operator==(const ClangTypeExplicitSafetyDescriptor &lhs, - const ClangTypeExplicitSafetyDescriptor &rhs) { - return lhs.type == rhs.type; - } - - friend bool operator!=(const ClangTypeExplicitSafetyDescriptor &lhs, - const ClangTypeExplicitSafetyDescriptor &rhs) { - return !(lhs == rhs); - } -}; - -void simple_display(llvm::raw_ostream &out, - ClangTypeExplicitSafetyDescriptor desc); -SourceLoc extractNearestSourceLoc(ClangTypeExplicitSafetyDescriptor desc); - -/// Determine the safety of the given Clang declaration. -class ClangTypeExplicitSafety - : public SimpleRequest { -public: - using SimpleRequest::SimpleRequest; - - // Source location - SourceLoc getNearestLoc() const { return SourceLoc(); }; - bool isCached() const { return true; } - -private: - friend SimpleRequest; - - // Evaluation. - ExplicitSafety evaluate(Evaluator &evaluator, - ClangTypeExplicitSafetyDescriptor desc) const; -}; - struct CxxDeclExplicitSafetyDescriptor final { const clang::Decl *decl; bool isClass; diff --git a/include/swift/ClangImporter/ClangImporterTypeIDZone.def b/include/swift/ClangImporter/ClangImporterTypeIDZone.def index 231c4efc0d2c1..0d562311a9c72 100644 --- a/include/swift/ClangImporter/ClangImporterTypeIDZone.def +++ b/include/swift/ClangImporter/ClangImporterTypeIDZone.def @@ -48,9 +48,6 @@ SWIFT_REQUEST(ClangImporter, ClangTypeEscapability, SWIFT_REQUEST(ClangImporter, CxxValueSemantics, CxxValueSemanticsKind(CxxValueSemanticsDescriptor), Cached, NoLocationInfo) -SWIFT_REQUEST(ClangImporter, ClangTypeExplicitSafety, - ExplicitSafety(ClangTypeExplicitSafetyDescriptor), Cached, - NoLocationInfo) SWIFT_REQUEST(ClangImporter, ClangDeclExplicitSafety, ExplicitSafety(CxxDeclExplicitSafetyDescriptor), Cached, NoLocationInfo) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 2a19f3735f554..758ca73a84259 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -8723,56 +8723,6 @@ SourceLoc swift::extractNearestSourceLoc(SafeUseOfCxxDeclDescriptor desc) { return SourceLoc(); } -void swift::simple_display(llvm::raw_ostream &out, - ClangTypeExplicitSafetyDescriptor desc) { - auto qt = static_cast(desc.type); - out << "Checking if type '" << qt.getAsString() << "' is explicitly safe.\n"; -} - -SourceLoc swift::extractNearestSourceLoc(ClangTypeExplicitSafetyDescriptor desc) { - return SourceLoc(); -} - -ExplicitSafety ClangTypeExplicitSafety::evaluate( - Evaluator &evaluator, ClangTypeExplicitSafetyDescriptor desc) const { - auto clangType = static_cast(desc.type); - - // Handle pointers. - auto pointeeType = clangType->getPointeeType(); - if (!pointeeType.isNull()) { - // Function pointers are okay. - if (pointeeType->isFunctionType()) - return ExplicitSafety::Safe; - - // Pointers to record types are okay if they come in as foreign reference - // types. - if (auto *recordDecl = pointeeType->getAsRecordDecl()) { - if (hasImportAsRefAttr(recordDecl)) - return ExplicitSafety::Safe; - } - - // All other pointers are considered unsafe. - return ExplicitSafety::Unsafe; - } - - // Handle records recursively. - 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. - 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. - return ExplicitSafety::Safe; -} - void swift::simple_display(llvm::raw_ostream &out, CxxDeclExplicitSafetyDescriptor desc) { out << "Checking if '"; @@ -8844,13 +8794,43 @@ CustomRefCountingOperationResult CustomRefCountingOperation::evaluate( /// Check whether the given Clang type involves an unsafe type. static bool hasUnsafeType(Evaluator &evaluator, clang::QualType clangType) { - auto req = ClangTypeExplicitSafety({clangType}); - if (evaluator.hasActiveRequest(req)) - // If there is a cycle in a type, assume ExplicitSafety is Unspecified, - // i.e., not unsafe: - return false; - return evaluateOrDefault(evaluator, req, ExplicitSafety::Unspecified) == - ExplicitSafety::Unsafe; + // Handle pointers. + auto pointeeType = clangType->getPointeeType(); + if (!pointeeType.isNull()) { + // Function pointers are okay. + if (pointeeType->isFunctionType()) + return false; + + // Pointers to record types are okay if they come in as foreign reference + // types. + if (auto recordDecl = pointeeType->getAsRecordDecl()) { + if (hasImportAsRefAttr(recordDecl)) + return false; + } + + // All other pointers are considered unsafe. + return true; + } + + // Handle records recursively. + 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. + auto safety = evaluateOrDefault( + evaluator, ClangDeclExplicitSafety({recordDecl, false}), + ExplicitSafety::Unspecified); + switch (safety) { + case ExplicitSafety::Unsafe: + return true; + + case ExplicitSafety::Safe: + case ExplicitSafety::Unspecified: + return false; + } + } + + // Everything else is safe. + return false; } ExplicitSafety @@ -8888,29 +8868,6 @@ 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(); @@ -8924,7 +8881,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 343a799bd27f5..17f8e32c767bd 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -69,7 +69,6 @@ #include "clang/AST/RecordLayout.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/StmtVisitor.h" -#include "clang/AST/TemplateBase.h" #include "clang/AST/Type.h" #include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetInfo.h" @@ -2302,6 +2301,34 @@ namespace { } } + // We have to do this after populating ImportedDecls to avoid importing + // the same decl multiple times. Also after we imported the bases. + if (const auto *ctsd = + dyn_cast(decl)) { + for (auto arg : ctsd->getTemplateArgs().asArray()) { + llvm::SmallVector nonPackArgs; + if (arg.getKind() == clang::TemplateArgument::Pack) { + auto pack = arg.getPackAsArray(); + nonPackArgs.assign(pack.begin(), pack.end()); + } else { + nonPackArgs.push_back(arg); + } + for (auto realArg : nonPackArgs) { + if (realArg.getKind() != clang::TemplateArgument::Type) + continue; + auto SwiftType = Impl.importTypeIgnoreIUO( + realArg.getAsType(), ImportTypeKind::Abstract, + [](Diagnostic &&diag) {}, false, Bridgeability::None, + ImportTypeAttrs()); + if (SwiftType && SwiftType->isUnsafe()) { + auto attr = new (Impl.SwiftContext) UnsafeAttr(/*implicit=*/true); + result->getAttrs().add(attr); + 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 f9eb61a94c449..2696b88b19c2e 100644 --- a/test/Interop/Cxx/class/safe-interop-mode.swift +++ b/test/Interop/Cxx/class/safe-interop-mode.swift @@ -83,27 +83,6 @@ struct OwnedData { void takeSharedObject(SharedObject *) const; }; -// A class template that throws away its type argument. -// -// If this template is instantiated with an unsafe type, it should be considered -// unsafe even if that type is never used. -template struct TTake {}; - -using TTakeInt = TTake; -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; @@ -205,26 +184,3 @@ func useSharedReference(frt: DerivedFromSharedObject, h: HoldsShared) { let _ = frt let _ = h.getObj() } - -func useTTakeInt(x: TTakeInt) { - _ = x -} - -func useTTakePtr(x: TTakePtr) { - // expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}} - _ = x // expected-note{{reference to parameter 'x' involves unsafe type}} -} - -func useTTakeSafeTuple(x: TTakeSafeTuple) { - _ = x -} - -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 } diff --git a/test/Interop/Cxx/templates/sfinae-template-type-arguments.swift b/test/Interop/Cxx/templates/sfinae-template-type-arguments.swift deleted file mode 100644 index e24b8bfe842ef..0000000000000 --- a/test/Interop/Cxx/templates/sfinae-template-type-arguments.swift +++ /dev/null @@ -1,53 +0,0 @@ -// RUN: split-file %s %t -// RUN: %target-clang -c -o /dev/null -Xclang -verify -I %t/Inputs %t/cxx.cpp -// RUN: %target-swift-frontend -typecheck -verify -cxx-interoperability-mode=default -I %t%{fs-sep}Inputs -verify-additional-file %t%{fs-sep}Inputs%{fs-sep}CxxHeader.h %t%{fs-sep}main.swift -// RUN: %target-swift-ide-test -print-module -module-to-print=CxxModule -I %t/Inputs -source-filename=x -cxx-interoperability-mode=default | %FileCheck %t/Inputs/CxxHeader.h - -//--- Inputs/module.modulemap -module CxxModule { - requires cplusplus - header "CxxHeader.h" -} - -//--- Inputs/CxxHeader.h -#pragma once - -struct Empty {}; -template struct MissingMember { typename T::Missing member; }; -using SUB = MissingMember; - -struct Incomplete; -template struct IncompleteField { T member; }; -using INC = IncompleteField; - -template struct DefaultedTemplateArgs {}; -using DefaultedTemplateArgsInst = DefaultedTemplateArgs<>; - -// CHECK: struct DefaultedTemplateArgs, IncompleteField> { -// CHECK: } -// CHECK: typealias DefaultedTemplateArgsInst = DefaultedTemplateArgs, IncompleteField> - -template struct ThrowsAwayTemplateArgs {}; -using ThrowsAwayTemplateArgsInst = ThrowsAwayTemplateArgs; - -// CHECK: struct ThrowsAwayTemplateArgs, IncompleteField> { -// CHECK: } -// CHECK: typealias ThrowsAwayTemplateArgsInst = ThrowsAwayTemplateArgs, IncompleteField> - -//--- cxx.cpp -// expected-no-diagnostics -#include -void make(void) { - DefaultedTemplateArgs<> dta{}; - DefaultedTemplateArgsInst dtai{}; - - ThrowsAwayTemplateArgs tata{}; - ThrowsAwayTemplateArgsInst tatai{}; -} - -//--- main.swift -import CxxModule -func make() { - let _: DefaultedTemplateArgsInst = .init() - let _: ThrowsAwayTemplateArgsInst = .init() -}