Skip to content

Commit b69eac9

Browse files
authored
Merge pull request #39535 from guitard0g/tparam-not-used-in-fsig
2 parents 915a521 + f433ac2 commit b69eac9

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)