Skip to content

Commit cc8f060

Browse files
authored
[cxx-interop] Fix crash when importing C++ forward-declared template specializations in typedefs (#84186)
Swift crashes when importing C++ typedefs to forward-declared explicit template specializations. In assert build we see this assertion failure happening in clang: `Assertion failed: (!T->isDependentType() && "should not see dependent types here"), function getTypeInfoImpl, file TypeNodes.inc, line 79.` Example that crashes: ```cpp template <typename T> struct MyTemplate { T value; }; template <> struct MyTemplate<int>; // Forward-declared specialization typedef MyTemplate<int> MyIntTemplate; ``` In this patch, I propose detecting forward-declared explicit specializations in typedef imports. Instead of crashing, these specializations should be blocked from being imported with a diagnostic similar to the forward declared (but not defined) structs. rdar://147595723
1 parent fa25ba8 commit cc8f060

File tree

4 files changed

+118
-0
lines changed

4 files changed

+118
-0
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,24 @@ namespace {
14971497
// bridging, i.e. if the imported typealias should name a bridged type
14981498
// or the original C type.
14991499
clang::QualType ClangType = Decl->getUnderlyingType();
1500+
1501+
// Prevent import of typedefs to forward-declared explicit template
1502+
// specializations, which would trigger assertion in Clang.
1503+
if (auto *templateSpec = dyn_cast<clang::TemplateSpecializationType>(
1504+
importer::desugarIfElaborated(ClangType).getTypePtr())) {
1505+
if (auto *recordType =
1506+
templateSpec->desugar()->getAs<clang::RecordType>()) {
1507+
if (auto *spec = dyn_cast<clang::ClassTemplateSpecializationDecl>(
1508+
recordType->getDecl())) {
1509+
if (spec->getSpecializationKind() ==
1510+
clang::TSK_ExplicitSpecialization &&
1511+
!spec->isCompleteDefinition()) {
1512+
return nullptr;
1513+
}
1514+
}
1515+
}
1516+
}
1517+
15001518
SwiftType = Impl.importTypeIgnoreIUO(
15011519
ClangType, ImportTypeKind::Typedef,
15021520
ImportDiagnosticAdder(Impl, Decl, Decl->getLocation()),
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#ifndef FORWARD_DECLARED_SPECIALIZATION_H
2+
#define FORWARD_DECLARED_SPECIALIZATION_H
3+
4+
// Basic template definition
5+
template <typename T>
6+
struct BasicTemplate {
7+
T value;
8+
};
9+
10+
// Case 1: Forward-declared specialization (should NOT import)
11+
template <>
12+
struct BasicTemplate<int>;
13+
typedef BasicTemplate<int> ForwardDeclaredInt;
14+
15+
// Case 2: Complete specialization (should import successfully)
16+
template <>
17+
struct BasicTemplate<double> {
18+
double value;
19+
double getValue() const { return value; }
20+
};
21+
typedef BasicTemplate<double> CompleteDouble;
22+
23+
// Case 3: Specialization defined after typedef (should import)
24+
template <>
25+
struct BasicTemplate<float>;
26+
typedef BasicTemplate<float> FloatTypedef;
27+
28+
template <>
29+
struct BasicTemplate<float> {
30+
float value;
31+
};
32+
33+
// Case 4: For comparison - forward-declared non-templated struct (have same behavior)
34+
struct ForwardDeclaredStruct;
35+
typedef ForwardDeclaredStruct ForwardDeclaredStructType;
36+
37+
// Case 5: Complete non-templated struct (imports successfully)
38+
struct CompleteStruct {
39+
int value;
40+
};
41+
typedef CompleteStruct CompleteStructType;
42+
43+
// Template for partial specialization test
44+
template <typename T, typename U>
45+
struct PartialTemplate {
46+
T first;
47+
U second;
48+
};
49+
50+
// Case 6: Forward-declared partial specialization (should NOT import - same as explicit)
51+
template <typename T>
52+
struct PartialTemplate<T*, int>; // Forward declaration only
53+
typedef PartialTemplate<double*, int> ForwardDeclaredPartial;
54+
55+
// Case 7: Complete partial specialization (should import successfully)
56+
template <typename T>
57+
struct PartialTemplate<T*, double> {
58+
T* ptr;
59+
double value;
60+
};
61+
typedef PartialTemplate<int*, double> CompletePartial;
62+
63+
#endif

test/Interop/Cxx/templates/Inputs/module.modulemap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,8 @@ module VariableTemplate {
182182
header "variable-template.h"
183183
requires cplusplus
184184
}
185+
186+
module ForwardDeclaredSpecialization {
187+
header "ForwardDeclaredSpecialization.h"
188+
requires cplusplus
189+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %target-typecheck-verify-swift -I %S/Inputs -cxx-interoperability-mode=default
2+
3+
import ForwardDeclaredSpecialization
4+
5+
func testForwardDeclaredSpecialization(_ param: ForwardDeclaredInt) {
6+
// expected-error@-1 {{cannot find type 'ForwardDeclaredInt' in scope}}
7+
}
8+
9+
func testCompleteSpecialization(_ param: CompleteDouble) {
10+
let _ = param.getValue()
11+
}
12+
13+
func testSpecializationDefinedAfter(_ param: FloatTypedef) {
14+
let _ = param.value
15+
}
16+
17+
func testForwardDeclaredStruct(_ param: ForwardDeclaredStructType) {
18+
// expected-error@-1 {{cannot find type 'ForwardDeclaredStructType' in scope}}
19+
}
20+
21+
func testCompleteStruct(_ param: CompleteStructType) {
22+
let _ = param.value
23+
}
24+
25+
func testForwardDeclaredPartial(_ param: ForwardDeclaredPartial) {
26+
// expected-error@-1 {{cannot find type 'ForwardDeclaredPartial' in scope}}
27+
}
28+
29+
func testCompletePartial(_ param: CompletePartial) {
30+
let _ = param.ptr
31+
let _ = param.value
32+
}

0 commit comments

Comments
 (0)