Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1500,6 +1500,24 @@ namespace {
// bridging, i.e. if the imported typealias should name a bridged type
// 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()),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#ifndef FORWARD_DECLARED_SPECIALIZATION_H
#define FORWARD_DECLARED_SPECIALIZATION_H

// Basic template definition
template <typename T>
struct BasicTemplate {
T value;
};

// Case 1: Forward-declared specialization (should NOT import)
template <>
struct BasicTemplate<int>;
typedef BasicTemplate<int> ForwardDeclaredInt;

// Case 2: Complete specialization (should import successfully)
template <>
struct BasicTemplate<double> {
double value;
double getValue() const { return value; }
};
typedef BasicTemplate<double> CompleteDouble;

// Case 3: Specialization defined after typedef (should import)
template <>
struct BasicTemplate<float>;
typedef BasicTemplate<float> FloatTypedef;

template <>
struct BasicTemplate<float> {
float value;
};

// Case 4: For comparison - forward-declared non-templated struct (have same behavior)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: it’s not clear what the behavior is the same as. Better to make it explicit imo:

Suggested change
// Case 4: For comparison - forward-declared non-templated struct (have same behavior)
// Case 4: For comparison - forward-declared non-templated struct (also does not import)

This suggestion isn’t blocking, i.e., no need to change the comment and be forced to re-run CI.

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 <typename T, typename U>
struct PartialTemplate {
T first;
U second;
};

// Case 6: Forward-declared partial specialization (should NOT import - same as explicit)
template <typename T>
struct PartialTemplate<T*, int>; // Forward declaration only
typedef PartialTemplate<double*, int> ForwardDeclaredPartial;

// Case 7: Complete partial specialization (should import successfully)
template <typename T>
struct PartialTemplate<T*, double> {
T* ptr;
double value;
};
typedef PartialTemplate<int*, double> CompletePartial;

#endif
5 changes: 5 additions & 0 deletions test/Interop/Cxx/templates/Inputs/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,8 @@ module VariableTemplate {
header "variable-template.h"
requires cplusplus
}

module ForwardDeclaredSpecialization {
header "ForwardDeclaredSpecialization.h"
requires cplusplus
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// RUN: %target-typecheck-verify-swift -I %S/Inputs -cxx-interoperability-mode=default

import ForwardDeclaredSpecialization

func testForwardDeclaredSpecialization(_ param: ForwardDeclaredInt) {
// expected-error@-1 {{cannot find type 'ForwardDeclaredInt' in scope}}
}

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
}