From 23fc3c2b510a918d6a5ed9539638c4ba8b70cdea Mon Sep 17 00:00:00 2001 From: fahadnayyar Date: Tue, 9 Sep 2025 14:22:00 -0700 Subject: [PATCH 1/2] [cxx-interop] Fix crash when importing C++ forward-declared template specializations in typedefs rdar://147595723 --- lib/ClangImporter/ImportDecl.cpp | 26 +++++++++++++++++++ .../Inputs/ForwardDeclaredSpecialization.h | 19 ++++++++++++++ .../Cxx/templates/Inputs/module.modulemap | 5 ++++ .../forward-declared-specialization.swift | 9 +++++++ 4 files changed, 59 insertions(+) create mode 100644 test/Interop/Cxx/templates/Inputs/ForwardDeclaredSpecialization.h create mode 100644 test/Interop/Cxx/templates/forward-declared-specialization.swift diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index ccef3f30caaa3..31535056938e6 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -1500,6 +1500,32 @@ namespace { // bridging, i.e. if the imported typealias should name a bridged type // or the original C type. clang::QualType ClangType = Decl->getUnderlyingType(); + + clang::QualType checkType = ClangType; + if (auto elaborated = + dyn_cast(checkType.getTypePtr())) { + checkType = elaborated->getNamedType(); + } + + if (auto templateSpec = dyn_cast( + checkType.getTypePtr())) { + checkType = templateSpec->desugar(); + } + + if (auto recordType = checkType->getAs()) { + if (auto spec = dyn_cast( + recordType->getDecl())) { + if (spec->getSpecializationKind() == + clang::TSK_ExplicitSpecialization && + !spec->isCompleteDefinition()) { + Impl.addImportDiagnostic( + Decl, Diagnostic(diag::record_is_dependent, Decl->getName()), + Decl->getLocation()); + return nullptr; + } + } + } + SwiftType = Impl.importTypeIgnoreIUO( ClangType, ImportTypeKind::Typedef, ImportDiagnosticAdder(Impl, Decl, Decl->getLocation()), diff --git a/test/Interop/Cxx/templates/Inputs/ForwardDeclaredSpecialization.h b/test/Interop/Cxx/templates/Inputs/ForwardDeclaredSpecialization.h new file mode 100644 index 0000000000000..e86b5725081bf --- /dev/null +++ b/test/Interop/Cxx/templates/Inputs/ForwardDeclaredSpecialization.h @@ -0,0 +1,19 @@ +#ifndef FORWARD_DECLARED_SPECIALIZATION_H +#define FORWARD_DECLARED_SPECIALIZATION_H + +template +struct MyTemplate { + T value; +}; + +template <> +struct MyTemplate; +typedef MyTemplate MyIntTemplate; + +template <> +struct MyTemplate { + double value; +}; +typedef MyTemplate MyCompleteIntTemplate; + +#endif // FORWARD_DECLARED_SPECIALIZATION_H diff --git a/test/Interop/Cxx/templates/Inputs/module.modulemap b/test/Interop/Cxx/templates/Inputs/module.modulemap index 88d4bdd22085f..720ea7fc9da7f 100644 --- a/test/Interop/Cxx/templates/Inputs/module.modulemap +++ b/test/Interop/Cxx/templates/Inputs/module.modulemap @@ -182,3 +182,8 @@ module VariableTemplate { header "variable-template.h" requires cplusplus } + +module ForwardDeclaredSpecialization { + header "ForwardDeclaredSpecialization.h" + requires cplusplus +} diff --git a/test/Interop/Cxx/templates/forward-declared-specialization.swift b/test/Interop/Cxx/templates/forward-declared-specialization.swift new file mode 100644 index 0000000000000..30a60a6cfc6c4 --- /dev/null +++ b/test/Interop/Cxx/templates/forward-declared-specialization.swift @@ -0,0 +1,9 @@ +// RUN: %target-typecheck-verify-swift -I %S/Inputs -cxx-interoperability-mode=default + +import ForwardDeclaredSpecialization + +func testForwardDeclaredSpecialization(_ param: MyIntTemplate) { // expected-error {{cannot find type 'MyIntTemplate' in scope}} +} + +func testCompleteSpecialization(_ param: MyCompleteIntTemplate) { +} From b5a0a4e928b6262582a506e44d1ed71a339b953e Mon Sep 17 00:00:00 2001 From: Fahad Nayyar Date: Thu, 11 Sep 2025 13:59:47 -0700 Subject: [PATCH 2/2] added more tests --- lib/ClangImporter/ImportDecl.cpp | 34 ++++------ .../Inputs/ForwardDeclaredSpecialization.h | 64 ++++++++++++++++--- .../forward-declared-specialization.swift | 27 +++++++- 3 files changed, 92 insertions(+), 33 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 31535056938e6..cc6e169a25f0a 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -1501,27 +1501,19 @@ namespace { // or the original C type. clang::QualType ClangType = Decl->getUnderlyingType(); - clang::QualType checkType = ClangType; - if (auto elaborated = - dyn_cast(checkType.getTypePtr())) { - checkType = elaborated->getNamedType(); - } - - if (auto templateSpec = dyn_cast( - checkType.getTypePtr())) { - checkType = templateSpec->desugar(); - } - - if (auto recordType = checkType->getAs()) { - if (auto spec = dyn_cast( - recordType->getDecl())) { - if (spec->getSpecializationKind() == - clang::TSK_ExplicitSpecialization && - !spec->isCompleteDefinition()) { - Impl.addImportDiagnostic( - Decl, Diagnostic(diag::record_is_dependent, Decl->getName()), - Decl->getLocation()); - return nullptr; + // 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; + } } } } diff --git a/test/Interop/Cxx/templates/Inputs/ForwardDeclaredSpecialization.h b/test/Interop/Cxx/templates/Inputs/ForwardDeclaredSpecialization.h index e86b5725081bf..9ac3ee1dcac9a 100644 --- a/test/Interop/Cxx/templates/Inputs/ForwardDeclaredSpecialization.h +++ b/test/Interop/Cxx/templates/Inputs/ForwardDeclaredSpecialization.h @@ -1,19 +1,63 @@ #ifndef FORWARD_DECLARED_SPECIALIZATION_H #define FORWARD_DECLARED_SPECIALIZATION_H -template -struct MyTemplate { - T value; +// Basic template definition +template +struct BasicTemplate { + T value; }; -template <> -struct MyTemplate; -typedef MyTemplate MyIntTemplate; +// Case 1: Forward-declared specialization (should NOT import) +template <> +struct BasicTemplate; +typedef BasicTemplate ForwardDeclaredInt; -template <> -struct MyTemplate { +// Case 2: Complete specialization (should import successfully) +template <> +struct BasicTemplate { double value; + double getValue() const { return value; } }; -typedef MyTemplate MyCompleteIntTemplate; +typedef BasicTemplate CompleteDouble; -#endif // FORWARD_DECLARED_SPECIALIZATION_H +// Case 3: Specialization defined after typedef (should import) +template <> +struct BasicTemplate; +typedef BasicTemplate FloatTypedef; + +template <> +struct BasicTemplate { + float value; +}; + +// Case 4: For comparison - forward-declared non-templated struct (have same behavior) +struct ForwardDeclaredStruct; +typedef ForwardDeclaredStruct ForwardDeclaredStructType; + +// Case 5: Complete non-templated struct (imports successfully) +struct CompleteStruct { + int value; +}; +typedef CompleteStruct CompleteStructType; + +// Template for partial specialization test +template +struct PartialTemplate { + T first; + U second; +}; + +// Case 6: Forward-declared partial specialization (should NOT import - same as explicit) +template +struct PartialTemplate; // Forward declaration only +typedef PartialTemplate ForwardDeclaredPartial; + +// Case 7: Complete partial specialization (should import successfully) +template +struct PartialTemplate { + T* ptr; + double value; +}; +typedef PartialTemplate CompletePartial; + +#endif diff --git a/test/Interop/Cxx/templates/forward-declared-specialization.swift b/test/Interop/Cxx/templates/forward-declared-specialization.swift index 30a60a6cfc6c4..3f3080e048feb 100644 --- a/test/Interop/Cxx/templates/forward-declared-specialization.swift +++ b/test/Interop/Cxx/templates/forward-declared-specialization.swift @@ -2,8 +2,31 @@ import ForwardDeclaredSpecialization -func testForwardDeclaredSpecialization(_ param: MyIntTemplate) { // expected-error {{cannot find type 'MyIntTemplate' in scope}} +func testForwardDeclaredSpecialization(_ param: ForwardDeclaredInt) { + // expected-error@-1 {{cannot find type 'ForwardDeclaredInt' in scope}} } -func testCompleteSpecialization(_ param: MyCompleteIntTemplate) { +func testCompleteSpecialization(_ param: CompleteDouble) { + let _ = param.getValue() +} + +func testSpecializationDefinedAfter(_ param: FloatTypedef) { + let _ = param.value +} + +func testForwardDeclaredStruct(_ param: ForwardDeclaredStructType) { + // expected-error@-1 {{cannot find type 'ForwardDeclaredStructType' in scope}} +} + +func testCompleteStruct(_ param: CompleteStructType) { + let _ = param.value +} + +func testForwardDeclaredPartial(_ param: ForwardDeclaredPartial) { + // expected-error@-1 {{cannot find type 'ForwardDeclaredPartial' in scope}} +} + +func testCompletePartial(_ param: CompletePartial) { + let _ = param.ptr + let _ = param.value }