-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[cxx-interop] Forbid C++ function template instantiation with Swift types #77430
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
@swift-ci please test |
3c36efb
to
59be37b
Compare
@swift-ci please test |
@swift-ci Please test |
This patch does not attempt to fix an existing issue, where the Swift compiler crashes when trying to instantiate a C++ function template with an optional pointer type. Notably, doing this still causes the compiler to crash: // C++
template <typename T> void T takes(T t) { } // Some C++ template function
int *ptr; // Some C++ ptr // Swift
takes(ptr) // crashes, because the type of ptr is UnsafeMutablePointer<Int32>? A workaround is to remove the optionality from the pointer: let sptr = ptr!
takes(sptr) // OK |
C function pointers are also not supported as a result of this patch. I cannot seem reliably obtain the original Clang type for imported function types. |
Could you open some tickets for these issues that are not covered by this PR? |
I wonder if it would be possible to split this up to 2 PRs. One that is purely a refactoring, and one that actually introduces the functional changes. It would make the review much easier. But in case that is too much work it is fine as is. |
@swift-ci please test |
1 similar comment
@swift-ci please test |
To track the issues with instantiating C++ function templates with pointer types, I've opened rdar://142850017 |
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.
Looks good to me, some minor nits/questions inline.
|
||
auto result = lookup(); | ||
if (!result.isNull()) | ||
Cache.insert({type, result}); |
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 it is possible to replace the double lookup in Cache
case of a cache miss using try_emplace
. Something like:
auto [it, inserted] = Cache.try_emplace(type, clang::QualType{});
if (!inserted)
it->second = lookup();
return it->second;
Also, if lookup()
returns null, do we want to repeat it the next call? Is there any reason to expect a different result next time?
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 see there are other parts of this file that are doing double lookups in case of cache misses, up to you if you want to fix those as well.
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.
Also, if lookup() returns null, do we want to repeat it the next call? Is there any reason to expect a different result next time?
Probably not, but I'm not fully confident of that. Do you reckon it's worth inserting an I think I'll do that in a subsequent patch, if at all. But it could be a good invariant to check for during debug builds.#ifndef NDEBUG
to catch such circumstances?
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 see there are other parts of this file that are doing double lookups in case of cache misses, up to you if you want to fix those as well.
I think it might be better to fix those in a follow-up patch, since this one is starting to get large. I'll change my use of Cache.find()
though.
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.
Er, sanity check: you meant if (inserted)
, right?
auto [it, inserted] = Cache.try_emplace(type, clang::QualType{});
if (inserted)
it->second = lookup();
return it->second;
inserted == true
would imply there was previously no entry, which is when we would need to perform a lookup()
. If inserted == false
then there was already a cached entry.
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, my bad! You are right!
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.
As it turns out, DenseMap::try_emplace()
does not work because the returned iterator may get invalidated by insertions during nested conversions, i.e.:
auto [it, inserted] = Cache.try_emplace(type, clang::QualType{});
if (inserted)
it->second = lookup(); // lookup() may cause insertions that invalidate it
return it->second;
So I'm going to revert that change.
(Also I realize that lookup()
is not the best choice of variable name; I've renamed it to conversion()
.)
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.
Also, following up on this:
Also, if lookup() returns null, do we want to repeat it the next call? Is there any reason to expect a different result next time?
It turns out that it is necessary, even though the original convert()
function did not appear to do this. I don't remember why I even added that check, or how the old convert()
function was working before... I'll leave it as is for now but investigate that in a follow-up patch.
|
||
// This type was imported from Clang, so we can convert it back by retrieving | ||
// ClangDecl stored in the imported type decl (without making a cache entry.) | ||
if (auto nominal = type->getAs<NominalType>()) |
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 am a bit on the edge whether we want to use a TypeVisitor
here, but maybe it is OK as is for now.
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 considered doing that but I figured we don't visit more types than we visit, and setting up the TypeVisitor
pattern also involves a lot of additional boilerplate. My thinking was that we can refactor this in the future, according to our needs then.
In case there is a concise way to describe what needs to be true for a Swift type to be supported as a C++ template argument, that would be a useful comment to add. In case it would just repeat the implementation details, feel free to ignore this. |
I had written something describing what you are asking in a comment, which I moved to the declaration in the header file in afe1e61. |
@swift-ci Please test |
@swift-ci Please test |
Hm. I'm not sure why I'm now encountering errors while building this in CI.... I'm going to try this one more time. |
@swift-ci please test |
At the moment, instantiating C++ function templates with Swift types is not supported. However, some Swift types---notably, Swift classes---have been falling through the conversion logic and causing swiftc crashes. This patch aims to turn those crashes into compiler errors. These issues are due to relying on ClangTypeConverter::convert() to look for and reject types that a function template should not be instantiated with. However, convert() is also used in other contexts with different conversion requirements, where those types should be allowed. Rather than complicate the existing conversion logic, this patch factors out some reusable parts of convert(), and uses them in a new ClangTypeConverter method specifically intended for converting C++ function template arguments during instantiation. In the future, this method can be elaborated to add support for instantiating C++ templates with Swift types is added. rdar://138947983 rdar://138946240 rdar://138944852 rdar://138946844 rdar://138946593 rdar://138945719
3a01220
to
427c824
Compare
@swift-ci Please test |
…ift closures Swift started to explicitly forbid the instantiation of C++ function templates with arbitrary types in swiftlang#77430, because many types cause the Swift compiler to crash. However, those checks prevented them from being instantiated with Swift closures (which were previously fine), causing a regression. This patch relaxes the convertTemplateArgument() function to also allow converting Swift function types, and adds some tests to make sure doing so is fine. This patch also does some cleanup of an existing test checking the instantiation of various types. rdar://148124104
…ift closures Swift started to explicitly forbid the instantiation of C++ function templates with arbitrary types in swiftlang#77430, because many types cause the Swift compiler to crash. However, those checks prevented them from being instantiated with Swift closures (which were previously fine), causing a regression. This patch relaxes the convertTemplateArgument() function to also allow converting Swift function types, and adds some tests to make sure doing so is fine. This patch also does some cleanup of existing tests checking the instantiation of various types, and adds testing for C function pointers and Obj-C blocks. rdar://148124104
…ift closures Swift started to explicitly forbid the instantiation of C++ function templates with arbitrary types in swiftlang#77430, because many types cause the Swift compiler to crash. However, those checks prevented them from being instantiated with Swift closures (which were previously fine), causing a regression. This patch relaxes the convertTemplateArgument() function to also allow converting Swift function types, and adds some tests to make sure doing so is fine. This patch also does some cleanup of existing tests checking the instantiation of various types, and adds testing for C function pointers and Obj-C blocks. rdar://148124104
…ift closures Swift started to explicitly forbid the instantiation of C++ function templates with arbitrary types in swiftlang#77430, because many types cause the Swift compiler to crash. However, those checks prevented them from being instantiated with Swift closures (which were previously fine), causing a regression. This patch relaxes the convertTemplateArgument() function to also allow converting Swift function types, and adds some tests to make sure doing so is fine. This patch also does some cleanup of existing tests checking the instantiation of various types, and adds testing for C function pointers and Obj-C blocks. rdar://148124104
…ift closures Swift started to explicitly forbid the instantiation of C++ function templates with arbitrary types in swiftlang#77430, because many types cause the Swift compiler to crash. However, those checks prevented them from being instantiated with Swift closures (which were previously fine), causing a regression. This patch relaxes the convertTemplateArgument() function to also allow converting Swift function types, and adds some tests to make sure doing so is fine. This patch also does some cleanup of existing tests checking the instantiation of various types, and adds testing for C function pointers and Obj-C blocks. rdar://148124104 (cherry picked from commit 284de98)
Instantiating C++ function templates with Swift types is not currently supported, but the Swift compiler attempts to do so. Sometimes this leads to a compiler crash; other times it leads to a runtime crash. This patch turns those crashes into compiler errors.
At the call site of a C++ function template, the Swift compiler must convert Swift types deduced by the Swift type checker back into Clang types, which are then used to instantiate the C++ function template. Prior to this patch, this conversion was performed by
ClangTypeConverter::convert()
, which converts unsupported Swift types. This patch factors out some reusable parts ofconvert()
and uses them in a newClangTypeConverter::convertTemplateArgument()
method that only converts supported template argument types. In the future, this method can be elaborated to support instantiating C++ templates with more types.rdar://112692940