-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[cxx-interop] Support function templates. #33053
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's try to shop the modeling here.
lib/IRGen/IRGenSIL.cpp
Outdated
@@ -2393,6 +2402,31 @@ void IRGenSILFunction::visitTryApplyInst(swift::TryApplyInst *i) { | |||
visitFullApplySite(i); | |||
} | |||
|
|||
static void setCXXFunctionTemplateAddr(IRGenSILFunction &IGF, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like we need some notion of a "foreign specialization" so this code doesn't have to completely special-case the apply path.
lib/IRGen/IRGenSIL.cpp
Outdated
Builder.CreateStore(result, resultArgPtr, IGM.getPointerAlignment()); | ||
} | ||
// Now we're done. | ||
emission.clear(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to imply that you have unclaimed parameter values along this open-coded path.
@@ -78,9 +78,10 @@ class ClangTypeConverter : | |||
/// declaration introduced by this converter, and if so, return the original | |||
/// Swift declaration. | |||
Decl *getSwiftDeclForExportedClangDecl(const clang::Decl *decl) const; | |||
|
|||
clang::QualType convert(Type type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm not mistaken, this is an implementation detail and should not be exposed. @varungandhi-apple?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm a bit wary of this. Could we instead have a method on ClangTypeConverter
which handles translating a templated C++ function type to a Swift function type, given a substitution map? I would've understood if this method needed to be used in a lot of places, but it only seems to have two external clients in this patch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to not making ClangTypeConverter public.
@varungandhi-apple, did you mean to have a method that translates a templated C++ function type to a Swift function type on ClangModuleLoader
? That sounds like a place that would keep ClangTypeConverter
encapsulated and allow us to do what we need in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ASTContext::Implementation
already has a ClangTypeConverter
member so, I think it makes sense to put this helper function there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I missed this comment somehow.
did you mean to have a method that translates a templated C++ function type to a Swift function type on ClangModuleLoader? That sounds like a place that would keep ClangTypeConverter encapsulated and allow us to do what we need in this PR.
No, the type conversion methods are all on ClangTypeConverter
. As Zoe said, the ASTContext carries around a ClangTypeConverter
so that can be used (it is already being used in a handful of places). ClangModuleLoader
shouldn't have methods for converting types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
lib/IRGen/IRGenSIL.cpp
Outdated
if (auto fn = site.getInitiallyReferencedFunction()) { | ||
auto clangDecl = fn->getClangDecl(); | ||
if (clangDecl && isa<clang::FunctionTemplateDecl>(clangDecl)) { | ||
setCXXFunctionTemplateAddr( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We ought to aim to do this much, much earlier in the process here. This should be done as part of the lowering of a dedicated SIL instruction.
On the overall approach: I don't think this is what we want to do in the medium-long term... I need to fix merge conflicts in #29691 and land that (I've been busy with other things, going to resume work on that tomorrow). Once we have Clang function types available in SIL, then we should be able to compute the type mapping when creating the corresponding Swift function type (right now it is eager, but we probably want to make it lazier later on). For templated functions, this would be unsubstituted types, since we are importing the declaration. During SILGen, I think we should be able to use the substitution map to translate the unsubstituted types into fully substituted types. So SILGen shouldn't need to do translation itself (all translations happen at the importing/AST layer), it only does substitutions. Does that make sense and/or sound reasonable? This is probably a bit fuzzy, I'm not sure about the exact details. |
IIUC the difference between what you're suggesting and this patch is the SILGen'ed |
That's a good summary of it, yeah. The broader point is that instead of having this type translation logic be defined and used in several places throughout the codebase, it would be nice to have as narrow an API as reasonably possible and only use in specific spots and have things "just work" (famous last words?), reducing coordination between different, distant compiler phases. |
I have a draft up here for incorporating Clang types in SIL: #33085. |
Hi Zoe, Thanks for working on this! I chatted with @gribozavr a bit this morning and I'd like to discuss high-level bits before we dig into the code review. I think instantiating function template signatures during SILGen is too late. We'll need to instantiate during the typechecking, at least templates that contain dependent types in the signature. Consider a function template: template<class T>
void f(T t, std:: add_lvalue_reference<T>::type t_ref) { ... } Before we substitute template arguments we cannot import this function into Swift and typecheck calls to it. As for instantiating function template bodies, we see 2 options:
What do you think? |
The IDE tooling part aspect sounds appealing; will this approach work with |
Instantiating the templates on the AST level is sufficient to get all errors. Clang does not produce errors from IRGen. |
SGTM. I'll work on re-factoring/writing this patch over the weekend so that it does the work during typechecking. I think, especially, for now, we should do all the work in the type checker. For a few reasons:
@gribozavr also brought up a good point (which I was meaning to try and forgot), what happens when we have a function like this? auto test() { return 0; } The whole function probably needs to be synthesized in that case. I think this is another argument for doing all the work in the type checker. |
852c823
to
e4c070a
Compare
I've updated this patch to have the specialization logic in type checking. This doesn't resolve the layering issue of having the |
// TODO: Generics supplied as template parameters. | ||
// X-CHECK-LABEL: define {{.*}}void @"$s4main22testPassThroughGeneric1xxx_tlF"(%swift.opaque* noalias nocapture sret %0, %swift.opaque* noalias nocapture %1, %swift.type* %T) | ||
// X-CHECK-LABEL: define linkonce_odr i8* @_Z11passThroughIP11objc_objectET_S2_(i8* %value) | ||
// public func testPassThroughGeneric<T>(x: T) -> T { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think generic types should be a follow-up patch. The "issue" is that we need to generate a FuncDecl
for the specialized clang function template with the specialized parameters. The specialized parameters are generic types so, they can't exist outside of the caller function. However, I think this can be solved pretty easily by just giving the new FuncDecl
a generic parameter for each generic it gets from the caller.
The nice thing about having everything in the type checker is we now don't have to have any special SILGen or IRGen logic 🙂 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great PR, thank you Zoe!
@@ -78,9 +78,10 @@ class ClangTypeConverter : | |||
/// declaration introduced by this converter, and if so, return the original | |||
/// Swift declaration. | |||
Decl *getSwiftDeclForExportedClangDecl(const clang::Decl *decl) const; | |||
|
|||
clang::QualType convert(Type type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to not making ClangTypeConverter public.
@varungandhi-apple, did you mean to have a method that translates a templated C++ function type to a Swift function type on ClangModuleLoader
? That sounds like a place that would keep ClangTypeConverter
encapsulated and allow us to do what we need in this PR.
lib/ClangImporter/ImportDecl.cpp
Outdated
DeclContext *dc, | ||
GenericParamList *genericParams, | ||
ParameterList *bodyParams, Type resultTy, | ||
bool throws, DeclContext *dc, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disclaimer, I don't know what's the policy on reformatting in the Swift project.
I try to only reformat pieces of code directly modified by a PR. So I'd not reformat the whole ImportDecl.cpp (and other files) in this PR. I find the PR review easier this way, there are fewer distractions, and smaller chance of missing a behavioural change.
I'd instead upload a separate PR without behavioural changes and reformat for example the whole lib/AST or lib/ClangImporter + corresponding include directories. What do you think?
Please ignore this suggestion if you know what the policy is and this PR follows it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I completely understand what you're saying. These parameters got reformatted because I added genericParams
. But, I'll remove the header-re-arranging (or put it in another PR).
lib/Sema/CSApply.cpp
Outdated
clang::SourceLocation()); | ||
sema.InstantiateFunctionDefinition(clang::SourceLocation(), spec); | ||
return spec; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about moving this whole function as a method in ClangModuleLoader?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
lib/Sema/CSApply.cpp
Outdated
@@ -578,7 +664,7 @@ namespace { | |||
if (!ref) | |||
ref = solution.resolveConcreteDeclRef(decl, loc); | |||
|
|||
assert(ref.getDecl() == decl); | |||
// assert(ref.getDecl() == decl); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remnants of a fun debugging session? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I think we need to remove this assertion. It seems to be checking that we in fact resolved a ConcreteDeclRef
for the given decl and not a random one but, I don't think that's a necessary thing to check. If we get a different decl (as we do in this case) I think that's OK. And this is something that will easily be caught via unit tests if there's a logical error.
} | ||
|
||
// Same here: | ||
template <class T> void cannot_infer_template() {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we get some tests for SFINAE? For example showing that the right function out of an overload set is selected
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. After writing some SFINAE tests I found an infinite loop. I'll have to fix that before this lands.
e4c070a
to
83fe483
Compare
Sorry for the delay in updating this. I've removed the commit that makes |
83fe483
to
5f1ee55
Compare
@swift-ci please test windows platform. |
@swift-ci please smoke test. |
Oops. I didn't think that would re-run the windows tests 🤦 |
@@ -219,6 +221,18 @@ class ClangModuleLoader : public ModuleLoader { | |||
/// based on subtleties like the target module interface format. | |||
virtual bool isSerializable(const clang::Type *type, | |||
bool checkCanonical) const = 0; | |||
|
|||
virtual clang::FunctionDecl * | |||
generateCXXFunctionTemplateSpecializations(ASTContext &ctx, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename to instantiateCXXFunctionTemplate
.
@gribozavr friendly ping. Do you think you'll be able to review this patch in the next week or two? If not, what would you think about doing post-commit review? |
Unless anyone has an objection, I'm going to land this patch on Tuesday. |
1b98f23
to
ea397e0
Compare
There are some small changes (and lots of tests) that need to be made to support templated constructors. I'm going to implement that in a follow-up patch. |
ea397e0
to
c919f69
Compare
@swift-ci please test and merge. |
llvm::find_if(genericParams, [name](GenericTypeParamDecl *generic) { | ||
return generic->getName().str() == name; | ||
}); | ||
// TODO: once we support generics in class types, replace this with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries, I'll get to that soon :)
@swift-ci please test and merge. |
This patch adds rudimentary support for C++ template functions in swift.
c919f69
to
b2b6b05
Compare
@swift-ci please test. |
@swift-ci please test windows platform. |
@swift-ci please test Windows platform. |
Big congrats Zoe, great work! |
template <class T> const T passThroughConst(const T value) { return value; } | ||
|
||
void takesString(const char *) { } | ||
template <class T> void expectsString(T str) { takesString(str); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expectsString => expectsConstCharPtr
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe expects CString?
@gribozavr Thank you for all those comments! Sorry they didn't make it into the original patch, but I've addressed them all in this PR: #35416. |
… review comments from swiftlang#33053. Addresses the post-commit review comments from swiftlang#33053. Just re-naming, commenting, and some other small cleanups, nothing functionally different.
…e-post-commit [NFC] Cleanup function templates implementation. Address post-commit review comments from #33053.
This patch adds rudimentary support for C++ template functions in swift. There's still a lot lacking but, that mostly has to do with the clang type converter which can be updated with incremental follow up patches.
Unfortunately, there's some ugliness and unconventional logic in this patch. First, we have to generate and define the specialization in the SILGen phase (instead of when we import the function) so we have access to the argument types and substitution map. Similarly, in IRGen, we can't get the function pointer while visiting the
function_ref
instruction because we also need the substitution map to find the correct specialization of the template (this also means there may be problems when the compiler can't find the correspondingfunction_ref
, i.e. if the function was passed as an argument).