[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.
This commit is contained in:
John Hui
2025-12-11 18:01:32 -08:00
parent ef9e3efaaf
commit bb5bb97958
3 changed files with 31 additions and 18 deletions

View File

@@ -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<clang::TemplateSpecializationType>(
importer::desugarIfElaborated(ClangType).getTypePtr())) {
if (auto *recordType =
templateSpec->desugar()->getAs<clang::RecordType>()) {
if (auto *spec = dyn_cast<clang::ClassTemplateSpecializationDecl>(
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<int>;
//
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();

View File

@@ -60,4 +60,11 @@ struct PartialTemplate<T*, double> {
};
typedef PartialTemplate<int*, double> CompletePartial;
// Some functions that use forward-declared specializations
void TakesIncompleteSpecialization(BasicTemplate<int>);
BasicTemplate<int> ReturnsIncompleteSpecialization();
void TakesPtrToIncompleteSpecialization(BasicTemplate<int> *);
BasicTemplate<int> *ReturnsPtrToIncompleteSpecialization();
#endif

View File

@@ -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)
}