Skip to content

Commit 72221c9

Browse files
authored
Merge pull request swiftlang#37995 from zoecarver/fix-defaulted-template
[cxx-interop] Allow function templates with defaulted template type parameters to be called.
2 parents 4bea441 + d14281f commit 72221c9

File tree

7 files changed

+186
-6
lines changed

7 files changed

+186
-6
lines changed

lib/AST/ClangTypeConverter.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -865,7 +865,6 @@ ClangTypeConverter::getClangTemplateArguments(
865865
ArrayRef<Type> genericArgs,
866866
SmallVectorImpl<clang::TemplateArgument> &templateArgs) {
867867
assert(templateArgs.size() == 0);
868-
assert(genericArgs.size() == templateParams->size());
869868

870869
// Keep track of the types we failed to convert so we can return a useful
871870
// error.
@@ -874,6 +873,13 @@ ClangTypeConverter::getClangTemplateArguments(
874873
// Note: all template parameters must be template type parameters. This is
875874
// verified when we import the Clang decl.
876875
auto templateParam = cast<clang::TemplateTypeParmDecl>(param);
876+
// We must have found a defaulted parameter at the end of the list.
877+
if (templateParam->getIndex() >= genericArgs.size()) {
878+
templateArgs.push_back(
879+
clang::TemplateArgument(templateParam->getDefaultArgument()));
880+
continue;
881+
}
882+
877883
auto replacement = genericArgs[templateParam->getIndex()];
878884
auto qualType = convert(replacement);
879885
if (qualType.isNull()) {

lib/ClangImporter/ImportDecl.cpp

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4087,6 +4087,30 @@ namespace {
40874087
DeclName name = accessorInfo ? DeclName() : importedName.getDeclName();
40884088
auto selfIdx = importedName.getSelfIndex();
40894089

4090+
auto underlyingTypeIsSame = [](const clang::Type *a,
4091+
clang::TemplateTypeParmDecl *b) {
4092+
while (a->isPointerType() || a->isReferenceType())
4093+
a = a->getPointeeType().getTypePtr();
4094+
return a == b->getTypeForDecl();
4095+
};
4096+
4097+
auto templateParamTypeUsedInSignature =
4098+
[underlyingTypeIsSame,
4099+
decl](clang::TemplateTypeParmDecl *type) -> bool {
4100+
// TODO(SR-13809): we will want to update this to handle dependent
4101+
// types when those are supported.
4102+
if (underlyingTypeIsSame(decl->getReturnType().getTypePtr(), type))
4103+
return true;
4104+
4105+
for (unsigned i : range(0, decl->getNumParams())) {
4106+
if (underlyingTypeIsSame(
4107+
decl->getParamDecl(i)->getType().getTypePtr(), type))
4108+
return true;
4109+
}
4110+
4111+
return false;
4112+
};
4113+
40904114
ImportedType importedType;
40914115
bool selfIsInOut = false;
40924116
ParameterList *bodyParams = nullptr;
@@ -4095,15 +4119,32 @@ namespace {
40954119
if (funcTemplate) {
40964120
unsigned i = 0;
40974121
for (auto param : *funcTemplate->getTemplateParameters()) {
4122+
auto templateTypeParam = cast<clang::TemplateTypeParmDecl>(param);
4123+
// If the template type parameter isn't used in the signature then we
4124+
// won't be able to deduce what it is when the function template is
4125+
// called in Swift code. This is OK if there's a defaulted type we can
4126+
// use (in which case we just don't add a correspond generic). This
4127+
// also means sometimes we will import a function template as a
4128+
// "normal" (non-generic) Swift function.
4129+
//
4130+
// If the defaulted template type parameter is used in the signature,
4131+
// then still add a generic so that it can be overrieded.
4132+
// TODO(SR-14837): in the future we might want to import two overloads
4133+
// in this case so that the default type could still be used.
4134+
if (templateTypeParam->hasDefaultArgument() &&
4135+
!templateParamTypeUsedInSignature(templateTypeParam))
4136+
continue;
4137+
40984138
auto *typeParam = Impl.createDeclWithClangNode<GenericTypeParamDecl>(
40994139
param, AccessLevel::Public, dc,
41004140
Impl.SwiftContext.getIdentifier(param->getName()), SourceLoc(), 0,
41014141
i);
41024142
templateParams.push_back(typeParam);
41034143
(void)++i;
41044144
}
4105-
genericParams = GenericParamList::create(Impl.SwiftContext, SourceLoc(),
4106-
templateParams, SourceLoc());
4145+
if (!templateParams.empty())
4146+
genericParams = GenericParamList::create(
4147+
Impl.SwiftContext, SourceLoc(), templateParams, SourceLoc());
41074148
}
41084149

41094150
if (!dc->isModuleScopeContext() && !isa<clang::CXXMethodDecl>(decl)) {

lib/Sema/CSApply.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,14 @@ Solution::computeSubstitutions(GenericSignature sig,
112112
static ConcreteDeclRef generateDeclRefForSpecializedCXXFunctionTemplate(
113113
ASTContext &ctx, AbstractFunctionDecl *oldDecl, SubstitutionMap subst,
114114
clang::FunctionDecl *specialized) {
115+
FunctionType *newFnType = nullptr;
115116
// Create a new ParameterList with the substituted type.
116-
auto oldFnType =
117-
cast<GenericFunctionType>(oldDecl->getInterfaceType().getPointer());
118-
auto newFnType = oldFnType->substGenericArgs(subst);
117+
if (auto oldFnType = dyn_cast<GenericFunctionType>(
118+
oldDecl->getInterfaceType().getPointer())) {
119+
newFnType = oldFnType->substGenericArgs(subst);
120+
} else {
121+
newFnType = cast<FunctionType>(oldDecl->getInterfaceType().getPointer());
122+
}
119123
// The constructor type is a function type as follows:
120124
// (CType.Type) -> (Generic) -> CType
121125
// And a method's function type is as follows:
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#ifndef TEST_INTEROP_CXX_TEMPLATES_INPUTS_DEFAULTED_TEMPLATE_TYPE_PARAMETER_H
2+
#define TEST_INTEROP_CXX_TEMPLATES_INPUTS_DEFAULTED_TEMPLATE_TYPE_PARAMETER_H
3+
4+
template <class>
5+
struct ClassTemplate {};
6+
7+
struct X {
8+
enum class CtorPicked { dependent, arg, empty } picked;
9+
10+
// Make sure we don't crash for dependent types.
11+
template <class T = void>
12+
X(ClassTemplate<T>) : picked(CtorPicked::dependent) {}
13+
14+
template <class T = void>
15+
X(T) : picked(CtorPicked::arg) {}
16+
17+
template <class = void>
18+
X() : picked(CtorPicked::empty) {}
19+
};
20+
21+
template <class = void>
22+
void defaultedTemplateTypeParam() {}
23+
24+
template <class T = void>
25+
void defaultedTemplateTypeParamUsedInArgs(T) {}
26+
27+
template <class T = void>
28+
T defaultedTemplateTypeParamUsedInReturn() {
29+
return 0;
30+
}
31+
32+
template <class T = void>
33+
void defaultedTemplateTypeParamAndDefaultedParam(T = 0) {}
34+
35+
template <class T>
36+
void functionTemplateWithDefaultedParam(T = 0) {}
37+
38+
template <class T = void>
39+
void defaultedTemplateTypeParamUsedInSignatureAndUnrealtedParam(int, T) {}
40+
41+
template <class = void>
42+
void defaultedTemplateTypeParamAndUnrealtedParam(int) {}
43+
44+
template <class T = int>
45+
void overloadedDefaultedTemplate(T) {}
46+
47+
void overloadedDefaultedTemplate(int) {}
48+
49+
template <typename T = int>
50+
void defaultedTemplateReferenceTypeParam(T &t) {}
51+
52+
template <typename T = int>
53+
void defaultedTemplatePointerTypeParam(T *t) {}
54+
55+
template <typename T = int>
56+
void defaultedTemplatePointerReferenceTypeParam(T *&t) {}
57+
58+
template <typename T = int>
59+
void defaultedTemplatePointerPointerTypeParam(T **t) {}
60+
61+
#endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_DEFAULTED_TEMPLATE_TYPE_PARAMETER_H

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,8 @@ module LargeClassTemplates {
117117
header "large-class-templates.h"
118118
requires cplusplus
119119
}
120+
121+
module DefaultedTemplateTypeParameter {
122+
header "defaulted-template-type-parameter.h"
123+
requires cplusplus
124+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %target-swift-ide-test -print-module -module-to-print=DefaultedTemplateTypeParameter -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s
2+
3+
// CHECK: struct X {
4+
// CHECK: init<T>(_: T)
5+
// CHECK: init()
6+
// CHECK: }
7+
8+
// CHECK: func defaultedTemplateTypeParam()
9+
// CHECK: func defaultedTemplateTypeParamUsedInArgs<T>(_: T)
10+
// CHECK: func defaultedTemplateTypeParamUsedInReturn<T>() -> T
11+
// CHECK: func defaultedTemplateTypeParamAndDefaultedParam<T>(_: T)
12+
// CHECK: func functionTemplateWithDefaultedParam<T>(_: T)
13+
// CHECK: func defaultedTemplateTypeParamUsedInSignatureAndUnrealtedParam<T>(_: Int32, _: T)
14+
// CHECK: func defaultedTemplateTypeParamAndUnrealtedParam(_: Int32)
15+
// CHECK: func overloadedDefaultedTemplate<T>(_: T)
16+
// CHECK: func overloadedDefaultedTemplate(_: Int32)
17+
// CHECK: func defaultedTemplateReferenceTypeParam<T>(_ t: UnsafeMutablePointer<T>)
18+
// The following types aren't imported correctly, but that does not have to do
19+
// with the fact that the template type paramaters are defaulted.
20+
// CHECK: func defaultedTemplatePointerTypeParam<T>(_ t: OpaquePointer!)
21+
// CHECK: func defaultedTemplatePointerReferenceTypeParam<T>(_ t: UnsafeMutablePointer<OpaquePointer?>)
22+
// CHECK: func defaultedTemplatePointerPointerTypeParam<T>(_ t: UnsafeMutablePointer<OpaquePointer?>!)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-cxx-interop)
2+
//
3+
// REQUIRES: executable_test
4+
5+
import DefaultedTemplateTypeParameter
6+
import StdlibUnittest
7+
8+
// The purpose of this test is to make sure that we correctly IRGen these
9+
// templates and link them. The behavior is not important here (we test that
10+
// elsewhere).
11+
var DefaultedTemplateTestSuite = TestSuite("Defaulted Template Type Parameters")
12+
13+
DefaultedTemplateTestSuite.test("Correct ctor picked") {
14+
let x1 = X(0)
15+
expectEqual(x1.picked, .arg)
16+
17+
let x2 = X()
18+
expectEqual(x2.picked, .empty)
19+
}
20+
21+
DefaultedTemplateTestSuite.test("Function with defaulted template type parameters") {
22+
defaultedTemplateTypeParam()
23+
defaultedTemplateTypeParamUsedInArgs(0)
24+
let _: Int = defaultedTemplateTypeParamUsedInReturn()
25+
defaultedTemplateTypeParamAndDefaultedParam(0)
26+
functionTemplateWithDefaultedParam(0)
27+
defaultedTemplateTypeParamUsedInSignatureAndUnrealtedParam(0, 0)
28+
defaultedTemplateTypeParamAndUnrealtedParam(0)
29+
}
30+
31+
DefaultedTemplateTestSuite.test("Overloaded function template is not ambiguous") {
32+
overloadedDefaultedTemplate(X())
33+
overloadedDefaultedTemplate(0)
34+
}
35+
36+
DefaultedTemplateTestSuite.test("Pointer types") {
37+
var x = 0
38+
defaultedTemplateReferenceTypeParam(&x)
39+
}
40+
41+
runAllTests()

0 commit comments

Comments
 (0)