From bb5bb9795867e07cc4513ad89b87e127ea5081da Mon Sep 17 00:00:00 2001 From: John Hui Date: Thu, 11 Dec 2025 18:01:32 -0800 Subject: [PATCH] [cxx-interop] Move incomplete template specialization check We were only previously doing this check when we had a typedef, because that is the scenario where we encountered this issue. This patch moves the check closer to where we would actually instantiate the template, so that these cases can be stopped in more situations. --- lib/ClangImporter/ImportDecl.cpp | 26 +++++++------------ .../Inputs/ForwardDeclaredSpecialization.h | 7 +++++ .../forward-declared-specialization.swift | 16 +++++++++++- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 5401f80def4c8..19349241cc00b 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -1501,23 +1501,6 @@ namespace { // or the original C type. clang::QualType ClangType = Decl->getUnderlyingType(); - // Prevent import of typedefs to forward-declared explicit template - // specializations, which would trigger assertion in Clang. - if (auto *templateSpec = dyn_cast( - importer::desugarIfElaborated(ClangType).getTypePtr())) { - if (auto *recordType = - templateSpec->desugar()->getAs()) { - if (auto *spec = dyn_cast( - recordType->getDecl())) { - if (spec->getSpecializationKind() == - clang::TSK_ExplicitSpecialization && - !spec->isCompleteDefinition()) { - return nullptr; - } - } - } - } - SwiftType = Impl.importTypeIgnoreIUO( ClangType, ImportTypeKind::Typedef, ImportDiagnosticAdder(Impl, Decl, Decl->getLocation()), @@ -3346,6 +3329,15 @@ namespace { decl->getName() == "_Expr" || decl->getName() == "__val_expr")) return nullptr; + // Don't even try to specialize/import this template if it's + // a forward-declared specialization like this: + // + // template <> struct MyTemplate; + // + if (decl->getSpecializationKind() == clang::TSK_ExplicitSpecialization && + !decl->isCompleteDefinition()) + return nullptr; + // `decl->getDefinition()` can return nullptr before the call to sema and // return its definition afterwards. clang::Sema &clangSema = Impl.getClangSema(); diff --git a/test/Interop/Cxx/templates/Inputs/ForwardDeclaredSpecialization.h b/test/Interop/Cxx/templates/Inputs/ForwardDeclaredSpecialization.h index 9ac3ee1dcac9a..06381ded6e4ab 100644 --- a/test/Interop/Cxx/templates/Inputs/ForwardDeclaredSpecialization.h +++ b/test/Interop/Cxx/templates/Inputs/ForwardDeclaredSpecialization.h @@ -60,4 +60,11 @@ struct PartialTemplate { }; typedef PartialTemplate CompletePartial; +// Some functions that use forward-declared specializations +void TakesIncompleteSpecialization(BasicTemplate); +BasicTemplate ReturnsIncompleteSpecialization(); + +void TakesPtrToIncompleteSpecialization(BasicTemplate *); +BasicTemplate *ReturnsPtrToIncompleteSpecialization(); + #endif diff --git a/test/Interop/Cxx/templates/forward-declared-specialization.swift b/test/Interop/Cxx/templates/forward-declared-specialization.swift index 3f3080e048feb..d93caeb754820 100644 --- a/test/Interop/Cxx/templates/forward-declared-specialization.swift +++ b/test/Interop/Cxx/templates/forward-declared-specialization.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -I %S/Inputs -cxx-interoperability-mode=default +// RUN: %target-typecheck-verify-swift -I %S/Inputs -cxx-interoperability-mode=default -suppress-remarks -suppress-notes import ForwardDeclaredSpecialization @@ -30,3 +30,17 @@ func testCompletePartial(_ param: CompletePartial) { let _ = param.ptr let _ = param.value } + +func testFunctionsUsingIncompleteSpec() { + let inc = ReturnsIncompleteSpecialization() + // expected-error@-1 {{return type is unavailable in Swift}} + // expected-warning@-2 {{constant 'inc' inferred to have type 'Never', which is an enum with no cases}} + TakesIncompleteSpecialization(inc) + // expected-error@-1 {{cannot find 'TakesIncompleteSpecialization' in scope}} +} + +func testFunctionsUsingPtrToIncompleteSpec(_ ptr: OpaquePointer) { + let incPtr = ReturnsPtrToIncompleteSpecialization() + TakesPtrToIncompleteSpecialization(incPtr) + TakesPtrToIncompleteSpecialization(ptr) +}