Skip to content

Commit f433ac2

Browse files
Josh LearnJosh Learn
authored andcommitted
Allow importing templated functions when template args do not appear
in the function signature by adding explicit metatype parameters to the function signature.
1 parent 25f7145 commit f433ac2

9 files changed

+250
-30
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4019,7 +4019,7 @@ namespace {
40194019
}
40204020
return Impl.importFunctionParameterList(
40214021
dc, decl, nonSelfParams, decl->isVariadic(), allowNSUIntegerAsInt,
4022-
argNames, genericParams);
4022+
argNames, genericParams, /*resultType=*/nullptr);
40234023
}
40244024

40254025
Decl *importGlobalAsInitializer(const clang::FunctionDecl *decl,
@@ -4079,23 +4079,15 @@ namespace {
40794079
DeclName name = accessorInfo ? DeclName() : importedName.getDeclName();
40804080
auto selfIdx = importedName.getSelfIndex();
40814081

4082-
auto underlyingTypeIsSame = [](const clang::Type *a,
4083-
clang::TemplateTypeParmDecl *b) {
4084-
while (a->isPointerType() || a->isReferenceType())
4085-
a = a->getPointeeType().getTypePtr();
4086-
return a == b->getTypeForDecl();
4087-
};
4088-
40894082
auto templateParamTypeUsedInSignature =
4090-
[underlyingTypeIsSame,
4091-
decl](clang::TemplateTypeParmDecl *type) -> bool {
4083+
[decl](clang::TemplateTypeParmDecl *type) -> bool {
40924084
// TODO(SR-13809): we will want to update this to handle dependent
40934085
// types when those are supported.
4094-
if (underlyingTypeIsSame(decl->getReturnType().getTypePtr(), type))
4086+
if (hasSameUnderlyingType(decl->getReturnType().getTypePtr(), type))
40954087
return true;
40964088

40974089
for (unsigned i : range(0, decl->getNumParams())) {
4098-
if (underlyingTypeIsSame(
4090+
if (hasSameUnderlyingType(
40994091
decl->getParamDecl(i)->getType().getTypePtr(), type))
41004092
return true;
41014093
}
@@ -4241,6 +4233,11 @@ namespace {
42414233
if (!bodyParams)
42424234
return nullptr;
42434235

4236+
if (name && name.getArgumentNames().size() != bodyParams->size()) {
4237+
// We synthesized additional parameters so rebuild the DeclName.
4238+
name = DeclName(Impl.SwiftContext, name.getBaseName(), bodyParams);
4239+
}
4240+
42444241
auto loc = Impl.importSourceLoc(decl->getLocation());
42454242

42464243
ClangNode clangNode = decl;
@@ -6619,7 +6616,7 @@ Decl *SwiftDeclConverter::importGlobalAsInitializer(
66196616
} else {
66206617
parameterList = Impl.importFunctionParameterList(
66216618
dc, decl, {decl->param_begin(), decl->param_end()}, decl->isVariadic(),
6622-
allowNSUIntegerAsInt, argNames, /*genericParams=*/{});
6619+
allowNSUIntegerAsInt, argNames, /*genericParams=*/{}, /*resultType=*/nullptr);
66236620
}
66246621
if (!parameterList)
66256622
return nullptr;
@@ -8534,6 +8531,13 @@ bool importer::isImportedAsStatic(const clang::OverloadedOperatorKind op) {
85348531
}
85358532
}
85368533

8534+
bool importer::hasSameUnderlyingType(const clang::Type *a,
8535+
const clang::TemplateTypeParmDecl *b) {
8536+
while (a->isPointerType() || a->isReferenceType())
8537+
a = a->getPointeeType().getTypePtr();
8538+
return a == b->getTypeForDecl();
8539+
}
8540+
85378541
unsigned ClangImporter::Implementation::getClangSwiftAttrSourceBuffer(
85388542
StringRef attributeText) {
85398543
auto known = ClangSwiftAttrSourceBuffers.find(attributeText);

lib/ClangImporter/ImportType.cpp

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1844,14 +1844,14 @@ ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType(
18441844
return {Type(), false};
18451845
}
18461846

1847+
Type swiftResultTy = importedType.getType();
18471848
ArrayRef<Identifier> argNames = name.getArgumentNames();
18481849
parameterList = importFunctionParameterList(dc, clangDecl, params, isVariadic,
18491850
allowNSUIntegerAsInt, argNames,
1850-
genericParams);
1851+
genericParams, swiftResultTy);
18511852
if (!parameterList)
18521853
return {Type(), false};
18531854

1854-
Type swiftResultTy = importedType.getType();
18551855
if (clangDecl->isNoReturn())
18561856
swiftResultTy = SwiftContext.getNeverType();
18571857

@@ -1862,7 +1862,7 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList(
18621862
DeclContext *dc, const clang::FunctionDecl *clangDecl,
18631863
ArrayRef<const clang::ParmVarDecl *> params, bool isVariadic,
18641864
bool allowNSUIntegerAsInt, ArrayRef<Identifier> argNames,
1865-
ArrayRef<GenericTypeParamDecl *> genericParams) {
1865+
ArrayRef<GenericTypeParamDecl *> genericParams, Type resultType) {
18661866
// Import the parameters.
18671867
SmallVector<ParamDecl *, 4> parameters;
18681868
unsigned index = 0;
@@ -1980,6 +1980,50 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList(
19801980
++index;
19811981
}
19821982

1983+
auto genericParamTypeUsedInSignature =
1984+
[params, resultType](GenericTypeParamDecl *genericParam,
1985+
bool shouldCheckResultType) -> bool {
1986+
auto paramDecl = genericParam->getClangDecl();
1987+
auto templateTypeParam = cast<clang::TemplateTypeParmDecl>(paramDecl);
1988+
// TODO(SR-13809): This won't work when we support importing dependent types.
1989+
// We'll have to change this logic to traverse the type tree of the imported
1990+
// Swift type (basically whatever ends up in the parameters variable).
1991+
// Check if genericParam's corresponding clang template type is used by
1992+
// the clang function's parameters.
1993+
for (auto param : params) {
1994+
if (hasSameUnderlyingType(param->getType().getTypePtr(),
1995+
templateTypeParam)) {
1996+
return true;
1997+
}
1998+
}
1999+
2000+
// Check if genericParam is used as a type parameter in the result type.
2001+
return shouldCheckResultType &&
2002+
resultType.findIf([genericParam](Type typePart) -> bool {
2003+
return typePart->isEqual(genericParam->getDeclaredInterfaceType());
2004+
});
2005+
};
2006+
2007+
// Make sure all generic parameters are accounted for in the function signature.
2008+
for (auto genericParam : genericParams) {
2009+
bool shouldCheckResultType = resultType && resultType->hasTypeParameter();
2010+
if (genericParamTypeUsedInSignature(genericParam, shouldCheckResultType))
2011+
continue;
2012+
2013+
// If this generic parameter is not used in the function signature,
2014+
// add a new parameter that accepts a metatype corresponding to that
2015+
// generic parameter.
2016+
Identifier name = genericParam->getName();
2017+
auto param = new (SwiftContext)
2018+
ParamDecl(SourceLoc(), SourceLoc(), name, SourceLoc(),
2019+
name, dc);
2020+
auto metatype =
2021+
cast<MetatypeType>(genericParam->getInterfaceType().getPointer());
2022+
param->setInterfaceType(metatype);
2023+
param->setSpecifier(ParamSpecifier::Default);
2024+
parameters.push_back(param);
2025+
}
2026+
19832027
// Append an additional argument to represent varargs.
19842028
if (isVariadic) {
19852029
auto paramTy =

lib/ClangImporter/ImporterImpl.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1223,7 +1223,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
12231223
DeclContext *dc, const clang::FunctionDecl *clangDecl,
12241224
ArrayRef<const clang::ParmVarDecl *> params, bool isVariadic,
12251225
bool allowNSUIntegerAsInt, ArrayRef<Identifier> argNames,
1226-
ArrayRef<GenericTypeParamDecl *> genericParams);
1226+
ArrayRef<GenericTypeParamDecl *> genericParams, Type resultType);
12271227

12281228
ImportedType importPropertyType(const clang::ObjCPropertyDecl *clangDecl,
12291229
bool isFromSystemModule);
@@ -1560,6 +1560,12 @@ bool isSpecialUIKitStructZeroProperty(const clang::NamedDecl *decl);
15601560
/// even if imported as a non-static member function.
15611561
bool isImportedAsStatic(clang::OverloadedOperatorKind op);
15621562

1563+
/// \returns true if \p a has the same underlying type as \p b after removing
1564+
/// any pointer/reference specifiers. Note that this does not currently look through
1565+
/// nested types other than pointers or references.
1566+
bool hasSameUnderlyingType(const clang::Type *a,
1567+
const clang::TemplateTypeParmDecl *b);
1568+
15631569
/// Add command-line arguments for a normal import of Clang code.
15641570
void getNormalInvocationArguments(std::vector<std::string> &invocationArgStrs,
15651571
ASTContext &ctx);

lib/Sema/CSApply.cpp

Lines changed: 104 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -109,16 +109,18 @@ Solution::computeSubstitutions(GenericSignature sig,
109109
lookupConformanceFn);
110110
}
111111

112-
static ConcreteDeclRef generateDeclRefForSpecializedCXXFunctionTemplate(
113-
ASTContext &ctx, AbstractFunctionDecl *oldDecl, SubstitutionMap subst,
114-
clang::FunctionDecl *specialized) {
112+
// Derive a concrete function type for fdecl by substituting the generic args
113+
// and use that to derive the corresponding function type and parameter list.
114+
static std::pair<FunctionType *, ParameterList *>
115+
substituteFunctionTypeAndParamList(ASTContext &ctx, AbstractFunctionDecl *fdecl,
116+
SubstitutionMap subst) {
115117
FunctionType *newFnType = nullptr;
116118
// Create a new ParameterList with the substituted type.
117119
if (auto oldFnType = dyn_cast<GenericFunctionType>(
118-
oldDecl->getInterfaceType().getPointer())) {
120+
fdecl->getInterfaceType().getPointer())) {
119121
newFnType = oldFnType->substGenericArgs(subst);
120122
} else {
121-
newFnType = cast<FunctionType>(oldDecl->getInterfaceType().getPointer());
123+
newFnType = cast<FunctionType>(fdecl->getInterfaceType().getPointer());
122124
}
123125
// The constructor type is a function type as follows:
124126
// (CType.Type) -> (Generic) -> CType
@@ -127,22 +129,49 @@ static ConcreteDeclRef generateDeclRefForSpecializedCXXFunctionTemplate(
127129
// In either case, we only want the result of that function type because that
128130
// is the function type with the generic params that need to be substituted:
129131
// (Generic) -> CType
130-
if (isa<ConstructorDecl>(oldDecl) || oldDecl->isInstanceMember() ||
131-
oldDecl->isStatic())
132+
if (isa<ConstructorDecl>(fdecl) || fdecl->isInstanceMember() ||
133+
fdecl->isStatic())
132134
newFnType = cast<FunctionType>(newFnType->getResult().getPointer());
133135
SmallVector<ParamDecl *, 4> newParams;
134136
unsigned i = 0;
137+
135138
for (auto paramTy : newFnType->getParams()) {
136-
auto *oldParamDecl = oldDecl->getParameters()->get(i);
139+
auto *oldParamDecl = fdecl->getParameters()->get(i);
137140
auto *newParamDecl =
138-
ParamDecl::cloneWithoutType(oldDecl->getASTContext(), oldParamDecl);
141+
ParamDecl::cloneWithoutType(fdecl->getASTContext(), oldParamDecl);
139142
newParamDecl->setInterfaceType(paramTy.getParameterType());
140143
newParams.push_back(newParamDecl);
141144
(void)++i;
142145
}
143146
auto *newParamList =
144147
ParameterList::create(ctx, SourceLoc(), newParams, SourceLoc());
145148

149+
return {newFnType, newParamList};
150+
}
151+
152+
static ValueDecl *generateSpecializedCXXFunctionTemplate(
153+
ASTContext &ctx, AbstractFunctionDecl *oldDecl, SubstitutionMap subst,
154+
clang::FunctionDecl *specialized) {
155+
auto newFnTypeAndParams = substituteFunctionTypeAndParamList(ctx, oldDecl, subst);
156+
auto newFnType = newFnTypeAndParams.first;
157+
auto paramList = newFnTypeAndParams.second;
158+
159+
SmallVector<ParamDecl *, 4> newParamsWithoutMetatypes;
160+
for (auto param : *paramList ) {
161+
if (isa<FuncDecl>(oldDecl) &&
162+
isa<MetatypeType>(param->getType().getPointer())) {
163+
// Metatype parameters are added synthetically to account for template
164+
// params that don't make it to the function signature. These shouldn't
165+
// exist in the resulting specialized FuncDecl. Note that this doesn't
166+
// affect constructors because all template params for a constructor
167+
// must be in the function signature by design.
168+
continue;
169+
}
170+
newParamsWithoutMetatypes.push_back(param);
171+
}
172+
auto *newParamList =
173+
ParameterList::create(ctx, SourceLoc(), newParamsWithoutMetatypes, SourceLoc());
174+
146175
if (isa<ConstructorDecl>(oldDecl)) {
147176
DeclName ctorName(ctx, DeclBaseName::createConstructor(), newParamList);
148177
auto newCtorDecl = ConstructorDecl::createImported(
@@ -152,7 +181,7 @@ static ConcreteDeclRef generateDeclRefForSpecializedCXXFunctionTemplate(
152181
/*throws=*/false, /*throwsLoc=*/SourceLoc(),
153182
newParamList, /*genericParams=*/nullptr,
154183
oldDecl->getDeclContext());
155-
return ConcreteDeclRef(newCtorDecl);
184+
return newCtorDecl;
156185
}
157186

158187
// Generate a name for the specialized function.
@@ -176,7 +205,48 @@ static ConcreteDeclRef generateDeclRefForSpecializedCXXFunctionTemplate(
176205
newFnDecl->setImportAsStaticMember();
177206
}
178207
newFnDecl->setSelfAccessKind(cast<FuncDecl>(oldDecl)->getSelfAccessKind());
179-
return ConcreteDeclRef(newFnDecl);
208+
return newFnDecl;
209+
}
210+
211+
// Synthesizes the body of a thunk that takes extra metatype arguments and
212+
// skips over them to forward them along to the FuncDecl contained by context.
213+
// This is used when importing a C++ templated function where the template params
214+
// are not used in the function signature. We supply the type params as explicit
215+
// metatype arguments to aid in typechecking, but they shouldn't be forwarded to
216+
// the corresponding C++ function.
217+
static std::pair<BraceStmt *, bool>
218+
synthesizeForwardingThunkBody(AbstractFunctionDecl *afd, void *context) {
219+
ASTContext &ctx = afd->getASTContext();
220+
221+
auto thunkDecl = cast<FuncDecl>(afd);
222+
auto specializedFuncDecl = static_cast<FuncDecl *>(context);
223+
224+
SmallVector<Argument, 8> forwardingParams;
225+
for (auto param : *thunkDecl->getParameters()) {
226+
if (isa<MetatypeType>(param->getType().getPointer())) {
227+
continue;
228+
}
229+
auto paramRefExpr = new (ctx) DeclRefExpr(param, DeclNameLoc(),
230+
/*Implicit=*/true);
231+
paramRefExpr->setType(param->getType());
232+
forwardingParams.push_back(Argument(SourceLoc(), Identifier(), paramRefExpr));
233+
}
234+
235+
auto *specializedFuncDeclRef = new (ctx) DeclRefExpr(ConcreteDeclRef(specializedFuncDecl),
236+
DeclNameLoc(), true);
237+
specializedFuncDeclRef->setType(specializedFuncDecl->getInterfaceType());
238+
239+
auto argList = ArgumentList::createImplicit(ctx, forwardingParams);
240+
auto *specializedFuncCallExpr = CallExpr::createImplicit(ctx, specializedFuncDeclRef, argList);
241+
specializedFuncCallExpr->setType(thunkDecl->getResultInterfaceType());
242+
specializedFuncCallExpr->setThrows(false);
243+
244+
auto returnStmt = new (ctx) ReturnStmt(SourceLoc(), specializedFuncCallExpr,
245+
/*implicit=*/true);
246+
247+
auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(),
248+
/*implicit=*/true);
249+
return {body, /*isTypeChecked=*/true};
180250
}
181251

182252
ConcreteDeclRef
@@ -215,13 +285,35 @@ Solution::resolveConcreteDeclRef(ValueDecl *decl,
215285
const_cast<clang::FunctionTemplateDecl *>(
216286
cast<clang::FunctionTemplateDecl>(decl->getClangDecl())),
217287
subst);
218-
return generateDeclRefForSpecializedCXXFunctionTemplate(
288+
auto newDecl = generateSpecializedCXXFunctionTemplate(
219289
decl->getASTContext(), cast<AbstractFunctionDecl>(decl), subst, newFn);
290+
if (auto fn = dyn_cast<FuncDecl>(decl)) {
291+
if (newFn->getNumParams() != fn->getParameters()->size()) {
292+
// We added additional metatype parameters to aid template
293+
// specialization, which are no longer now that we've specialized
294+
// this function. Create a thunk that only forwards the original
295+
// parameters along to the clang function.
296+
auto thunkTypeAndParamList = substituteFunctionTypeAndParamList(decl->getASTContext(),
297+
fn, subst);
298+
auto thunk = FuncDecl::createImplicit(
299+
fn->getASTContext(), fn->getStaticSpelling(), fn->getName(),
300+
fn->getNameLoc(), fn->hasAsync(), fn->hasThrows(),
301+
/*genericParams=*/nullptr, thunkTypeAndParamList.second,
302+
thunkTypeAndParamList.first->getResult(), fn->getDeclContext());
303+
thunk->copyFormalAccessFrom(fn);
304+
thunk->setBodySynthesizer(synthesizeForwardingThunkBody, cast<FuncDecl>(newDecl));
305+
thunk->setSelfAccessKind(fn->getSelfAccessKind());
306+
307+
return ConcreteDeclRef(thunk);
308+
}
309+
}
310+
return ConcreteDeclRef(newDecl);
220311
}
221312

222313
return ConcreteDeclRef(decl, subst);
223314
}
224315

316+
225317
ConstraintLocator *Solution::getCalleeLocator(ConstraintLocator *locator,
226318
bool lookThroughApply) const {
227319
auto &cs = getConstraintSystem();

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,8 @@ module DefaultedTemplateTypeParameter {
122122
header "defaulted-template-type-parameter.h"
123123
requires cplusplus
124124
}
125+
126+
module TemplateTypeParameterNotInSignature {
127+
header "template-type-parameter-not-in-signature.h"
128+
requires cplusplus
129+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#ifndef TEST_INTEROP_CXX_TEMPLATES_INPUTS_TEMPLATE_TYPE_PARAMETER_NOT_IN_SIGNATURE_H
2+
#define TEST_INTEROP_CXX_TEMPLATES_INPUTS_TEMPLATE_TYPE_PARAMETER_NOT_IN_SIGNATURE_H
3+
4+
template <typename T>
5+
void templateTypeParamNotUsedInSignature() {}
6+
7+
template <typename T, typename U>
8+
void multiTemplateTypeParamNotUsedInSignature() {}
9+
10+
template <typename T, typename U>
11+
U multiTemplateTypeParamOneUsedInSignature(U u) { return u; }
12+
13+
template <typename T, typename U>
14+
void multiTemplateTypeParamNotUsedInSignatureWithUnrelatedParams(int x, long y) {}
15+
16+
template <typename T>
17+
T templateTypeParamUsedInReturnType(int x) { return x; }
18+
19+
template <typename T>
20+
T templateTypeParamUsedInReferenceParam(T &t) { return t; }
21+
22+
template <typename T, typename U>
23+
void templateTypeParamNotUsedInSignatureWithVarargs(...) {}
24+
25+
template <typename T, typename U, typename V>
26+
void templateTypeParamNotUsedInSignatureWithVarargsAndUnrelatedParam(int x, ...) {}
27+
28+
template <typename T, int N>
29+
void templateTypeParamNotUsedInSignatureWithNonTypeParam() {}
30+
31+
#endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_TEMPLATE_TYPE_PARAMETER_NOT_IN_SIGNATURE_H

test/Interop/Cxx/templates/function-template-module-interface.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// CHECK: func passThrough<T>(_ value: T) -> T
66
// CHECK: func passThroughConst<T>(_ value: T) -> T
77
// CHECK: func templateParameterReturnType<R, T, U>(_ a: T, _ b: U) -> R
8-
// CHECK: func cannotInferTemplate<T>()
8+
// CHECK: func cannotInferTemplate<T>(T: T.Type)
99

1010
// CHECK: struct HasVariadicMemeber {
1111
// CHECK: @available(*, unavailable, message: "Variadic function is unavailable")

0 commit comments

Comments
 (0)