-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[cxx-interop] Allow importing templated functions when template args do not appear in function signature #39535
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -109,16 +109,18 @@ Solution::computeSubstitutions(GenericSignature sig, | |
lookupConformanceFn); | ||
} | ||
|
||
static ConcreteDeclRef generateDeclRefForSpecializedCXXFunctionTemplate( | ||
ASTContext &ctx, AbstractFunctionDecl *oldDecl, SubstitutionMap subst, | ||
clang::FunctionDecl *specialized) { | ||
// Derive a concrete function type for fdecl by substituting the generic args | ||
// and use that to derive the corresponding function type and parameter list. | ||
static std::pair<FunctionType *, ParameterList *> | ||
substituteFunctionTypeAndParamList(ASTContext &ctx, AbstractFunctionDecl *fdecl, | ||
SubstitutionMap subst) { | ||
FunctionType *newFnType = nullptr; | ||
// Create a new ParameterList with the substituted type. | ||
if (auto oldFnType = dyn_cast<GenericFunctionType>( | ||
oldDecl->getInterfaceType().getPointer())) { | ||
fdecl->getInterfaceType().getPointer())) { | ||
newFnType = oldFnType->substGenericArgs(subst); | ||
} else { | ||
newFnType = cast<FunctionType>(oldDecl->getInterfaceType().getPointer()); | ||
newFnType = cast<FunctionType>(fdecl->getInterfaceType().getPointer()); | ||
} | ||
// The constructor type is a function type as follows: | ||
// (CType.Type) -> (Generic) -> CType | ||
|
@@ -127,22 +129,49 @@ static ConcreteDeclRef generateDeclRefForSpecializedCXXFunctionTemplate( | |
// In either case, we only want the result of that function type because that | ||
// is the function type with the generic params that need to be substituted: | ||
// (Generic) -> CType | ||
if (isa<ConstructorDecl>(oldDecl) || oldDecl->isInstanceMember() || | ||
oldDecl->isStatic()) | ||
if (isa<ConstructorDecl>(fdecl) || fdecl->isInstanceMember() || | ||
fdecl->isStatic()) | ||
newFnType = cast<FunctionType>(newFnType->getResult().getPointer()); | ||
SmallVector<ParamDecl *, 4> newParams; | ||
unsigned i = 0; | ||
|
||
for (auto paramTy : newFnType->getParams()) { | ||
auto *oldParamDecl = oldDecl->getParameters()->get(i); | ||
auto *oldParamDecl = fdecl->getParameters()->get(i); | ||
auto *newParamDecl = | ||
ParamDecl::cloneWithoutType(oldDecl->getASTContext(), oldParamDecl); | ||
ParamDecl::cloneWithoutType(fdecl->getASTContext(), oldParamDecl); | ||
newParamDecl->setInterfaceType(paramTy.getParameterType()); | ||
newParams.push_back(newParamDecl); | ||
(void)++i; | ||
} | ||
auto *newParamList = | ||
ParameterList::create(ctx, SourceLoc(), newParams, SourceLoc()); | ||
|
||
return {newFnType, newParamList}; | ||
} | ||
|
||
static ValueDecl *generateSpecializedCXXFunctionTemplate( | ||
ASTContext &ctx, AbstractFunctionDecl *oldDecl, SubstitutionMap subst, | ||
clang::FunctionDecl *specialized) { | ||
auto newFnTypeAndParams = substituteFunctionTypeAndParamList(ctx, oldDecl, subst); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wish we could use |
||
auto newFnType = newFnTypeAndParams.first; | ||
auto paramList = newFnTypeAndParams.second; | ||
|
||
SmallVector<ParamDecl *, 4> newParamsWithoutMetatypes; | ||
for (auto param : *paramList ) { | ||
if (isa<FuncDecl>(oldDecl) && | ||
isa<MetatypeType>(param->getType().getPointer())) { | ||
// Metatype parameters are added synthetically to account for template | ||
// params that don't make it to the function signature. These shouldn't | ||
// exist in the resulting specialized FuncDecl. Note that this doesn't | ||
// affect constructors because all template params for a constructor | ||
// must be in the function signature by design. | ||
continue; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think various methods/static methods might take a meta type. We need to verify if this will break that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The tests are passing, so I guess this isn't an issue. |
||
} | ||
newParamsWithoutMetatypes.push_back(param); | ||
} | ||
auto *newParamList = | ||
ParameterList::create(ctx, SourceLoc(), newParamsWithoutMetatypes, SourceLoc()); | ||
|
||
if (isa<ConstructorDecl>(oldDecl)) { | ||
DeclName ctorName(ctx, DeclBaseName::createConstructor(), newParamList); | ||
auto newCtorDecl = ConstructorDecl::createImported( | ||
|
@@ -152,7 +181,7 @@ static ConcreteDeclRef generateDeclRefForSpecializedCXXFunctionTemplate( | |
/*throws=*/false, /*throwsLoc=*/SourceLoc(), | ||
newParamList, /*genericParams=*/nullptr, | ||
oldDecl->getDeclContext()); | ||
return ConcreteDeclRef(newCtorDecl); | ||
return newCtorDecl; | ||
} | ||
|
||
// Generate a name for the specialized function. | ||
|
@@ -176,7 +205,48 @@ static ConcreteDeclRef generateDeclRefForSpecializedCXXFunctionTemplate( | |
newFnDecl->setImportAsStaticMember(); | ||
} | ||
newFnDecl->setSelfAccessKind(cast<FuncDecl>(oldDecl)->getSelfAccessKind()); | ||
return ConcreteDeclRef(newFnDecl); | ||
return newFnDecl; | ||
} | ||
|
||
guitard0g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Synthesizes the body of a thunk that takes extra metatype arguments and | ||
// skips over them to forward them along to the FuncDecl contained by context. | ||
// This is used when importing a C++ templated function where the template params | ||
// are not used in the function signature. We supply the type params as explicit | ||
// metatype arguments to aid in typechecking, but they shouldn't be forwarded to | ||
// the corresponding C++ function. | ||
static std::pair<BraceStmt *, bool> | ||
synthesizeForwardingThunkBody(AbstractFunctionDecl *afd, void *context) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not in this patch, but at some point we should move all synthesizers to their own file. They just take up space right now and clutter the files they're scattered across. |
||
ASTContext &ctx = afd->getASTContext(); | ||
|
||
auto thunkDecl = cast<FuncDecl>(afd); | ||
auto specializedFuncDecl = static_cast<FuncDecl *>(context); | ||
|
||
SmallVector<Argument, 8> forwardingParams; | ||
for (auto param : *thunkDecl->getParameters()) { | ||
if (isa<MetatypeType>(param->getType().getPointer())) { | ||
continue; | ||
} | ||
auto paramRefExpr = new (ctx) DeclRefExpr(param, DeclNameLoc(), | ||
/*Implicit=*/true); | ||
paramRefExpr->setType(param->getType()); | ||
forwardingParams.push_back(Argument(SourceLoc(), Identifier(), paramRefExpr)); | ||
} | ||
|
||
auto *specializedFuncDeclRef = new (ctx) DeclRefExpr(ConcreteDeclRef(specializedFuncDecl), | ||
DeclNameLoc(), true); | ||
specializedFuncDeclRef->setType(specializedFuncDecl->getInterfaceType()); | ||
|
||
auto argList = ArgumentList::createImplicit(ctx, forwardingParams); | ||
auto *specializedFuncCallExpr = CallExpr::createImplicit(ctx, specializedFuncDeclRef, argList); | ||
specializedFuncCallExpr->setType(thunkDecl->getResultInterfaceType()); | ||
specializedFuncCallExpr->setThrows(false); | ||
|
||
auto returnStmt = new (ctx) ReturnStmt(SourceLoc(), specializedFuncCallExpr, | ||
/*implicit=*/true); | ||
|
||
auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(), | ||
/*implicit=*/true); | ||
return {body, /*isTypeChecked=*/true}; | ||
} | ||
|
||
ConcreteDeclRef | ||
|
@@ -215,13 +285,35 @@ Solution::resolveConcreteDeclRef(ValueDecl *decl, | |
const_cast<clang::FunctionTemplateDecl *>( | ||
cast<clang::FunctionTemplateDecl>(decl->getClangDecl())), | ||
subst); | ||
return generateDeclRefForSpecializedCXXFunctionTemplate( | ||
auto newDecl = generateSpecializedCXXFunctionTemplate( | ||
decl->getASTContext(), cast<AbstractFunctionDecl>(decl), subst, newFn); | ||
if (auto fn = dyn_cast<FuncDecl>(decl)) { | ||
if (newFn->getNumParams() != fn->getParameters()->size()) { | ||
// We added additional metatype parameters to aid template | ||
// specialization, which are no longer now that we've specialized | ||
// this function. Create a thunk that only forwards the original | ||
// parameters along to the clang function. | ||
auto thunkTypeAndParamList = substituteFunctionTypeAndParamList(decl->getASTContext(), | ||
fn, subst); | ||
auto thunk = FuncDecl::createImplicit( | ||
fn->getASTContext(), fn->getStaticSpelling(), fn->getName(), | ||
fn->getNameLoc(), fn->hasAsync(), fn->hasThrows(), | ||
/*genericParams=*/nullptr, thunkTypeAndParamList.second, | ||
thunkTypeAndParamList.first->getResult(), fn->getDeclContext()); | ||
thunk->copyFormalAccessFrom(fn); | ||
thunk->setBodySynthesizer(synthesizeForwardingThunkBody, cast<FuncDecl>(newDecl)); | ||
thunk->setSelfAccessKind(fn->getSelfAccessKind()); | ||
|
||
return ConcreteDeclRef(thunk); | ||
} | ||
} | ||
return ConcreteDeclRef(newDecl); | ||
} | ||
|
||
return ConcreteDeclRef(decl, subst); | ||
} | ||
|
||
|
||
ConstraintLocator *Solution::getCalleeLocator(ConstraintLocator *locator, | ||
bool lookThroughApply) const { | ||
auto &cs = getConstraintSystem(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#ifndef TEST_INTEROP_CXX_TEMPLATES_INPUTS_TEMPLATE_TYPE_PARAMETER_NOT_IN_SIGNATURE_H | ||
#define TEST_INTEROP_CXX_TEMPLATES_INPUTS_TEMPLATE_TYPE_PARAMETER_NOT_IN_SIGNATURE_H | ||
|
||
guitard0g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
template <typename T> | ||
void templateTypeParamNotUsedInSignature() {} | ||
|
||
template <typename T, typename U> | ||
void multiTemplateTypeParamNotUsedInSignature() {} | ||
|
||
template <typename T, typename U> | ||
U multiTemplateTypeParamOneUsedInSignature(U u) { return u; } | ||
|
||
template <typename T, typename U> | ||
void multiTemplateTypeParamNotUsedInSignatureWithUnrelatedParams(int x, long y) {} | ||
|
||
template <typename T> | ||
T templateTypeParamUsedInReturnType(int x) { return x; } | ||
|
||
template <typename T> | ||
T templateTypeParamUsedInReferenceParam(T &t) { return t; } | ||
|
||
template <typename T, typename U> | ||
void templateTypeParamNotUsedInSignatureWithVarargs(...) {} | ||
|
||
template <typename T, typename U, typename V> | ||
void templateTypeParamNotUsedInSignatureWithVarargsAndUnrelatedParam(int x, ...) {} | ||
|
||
template <typename T, int N> | ||
void templateTypeParamNotUsedInSignatureWithNonTypeParam() {} | ||
|
||
#endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_TEMPLATE_TYPE_PARAMETER_NOT_IN_SIGNATURE_H |
Uh oh!
There was an error while loading. Please reload this page.