-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Opaque result types: dynamic replacement #24201
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
Opaque result types: dynamic replacement #24201
Conversation
This is to support dynamic function replacement of functions with opaque result type. This approach requires that all state is thrown away (that could contain the old returned type for an opaque type) between replacements. rdar://48887938
Clones functions containing opaque type archetypes replacing the opaque type by a concrete type. rdar://46140751
Fixes rdar://problem/50001229.
@swift-ci Please test |
Build failed |
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 don’t know why some of my comments are not rendered properly.
@@ -576,6 +592,10 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const { | |||
case Kind::ProtocolRequirementsBaseDescriptor: | |||
case Kind::MethodLookupFunction: | |||
case Kind::OpaqueTypeDescriptor: | |||
case Kind::OpaqueTypeDescriptorAccessor: |
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.
Do these really need to be public?
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.
Yed, they are what is getting replaced.
@@ -199,6 +199,10 @@ class LinkEntity { | |||
/// The descriptor for an opaque type. | |||
/// The pointer is an OpaqueTypeDecl*. | |||
OpaqueTypeDescriptor, | |||
OpaqueTypeDescriptorAccessor, |
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.
These should have comments
@aschwaighofer It looks like when dynamic replacement is off, we directly reference the opaque type descriptor and call a runtime function to get the concrete metadata. Is this correct? Then why do we need to emit an accessor function when dynamic replacement is on? Can we not dynamically replace a global variable, perhaps storing a pointer inside an indirect global variable? Do the new symbols get emitted even when dynamic replacement is off? Here's an example showing you can have a 'some P' type that's in parameter position, or nested inside a structural return type:
Perhaps dynamic replacement can't support this yet, but it should degrade gracefully instead of crashing in a cast. |
SmallVector<OpaqueTypeArchetypeType *, 8> newFuncTypes; | ||
SmallVector<OpaqueTypeArchetypeType *, 8> origFuncTypes; | ||
for (auto *newFunc : DynamicReplacements) { | ||
if (!newFunc->getLoweredFunctionType()->hasOpaqueArchetype()) |
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 should be a structural walk over the SILFunctionType using newFunc->getLoweredFunctionType().each()
auto argType = substConv.getSILArgumentType(argIdx); | ||
if (argConv.isIndirectConvention() && | ||
argType.getASTType()->hasOpaqueArchetype() && | ||
!opd.get()->getType().getASTType()->hasOpaqueArchetype()) { |
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 just check that the types differ? What if both have opaque types but they're different, because of various inlining and specialization passes?
for (auto &inst : BB) { | ||
auto *allocStack = dyn_cast<AllocStackInst>(&inst); | ||
if (!allocStack || | ||
!allocStack->getElementType().is<OpaqueTypeArchetypeType>()) |
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 if the element type contains an opaque type but its not at the top level?
lib/Sema/TypeCheckAttr.cpp
Outdated
@@ -2093,6 +2093,94 @@ static Type getDynamicComparisonType(ValueDecl *value) { | |||
return interfaceType->removeArgumentLabels(numArgumentLabels); | |||
} | |||
|
|||
static OpaqueTypeArchetypeType *getOpaqueResulType(const AbstractFunctionDecl *func) { |
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.
typo, should be "ResultType"
lib/Sema/TypeCheckAttr.cpp
Outdated
} | ||
|
||
static bool | ||
matchFunctionWithOpaqueResultType(const AbstractFunctionDecl *replacement, |
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 instead this could be a flag for TypeBase::matches(), TypeMatchFlags::CompareOpaqueTypes or something? It really doesn't need to handle parameters and results differently, the only difference is when it finds an opaque type, instead of checking for equality it checks if they have the same constraints.
We'll need that flag when we allow non-final class methods to have opaque result types for instance...
I reuse the mechanism of dynamically replacing a function which is already present. I don't want to invent a new mechanism/runtime support for a feature that will be supplanted by something else.
No it should not. (see emitOpaqueTypeDescriptorAccessor) but there is a bug that I missed the same check (getAddressOfOpaqueTypeDescriptor) that I have to fix. |
@aschwaighofer Ok, if you're going to replace this soon anyway then the current approach is fine. Do you have a radar or JIRA for the followup change? I'm curious to learn more about it. Do you mind still tidying up the walks over the type to fish out any opaque types it contains, though, as well as the function type replacement matching in TypeCheckAttr.cpp? |
Yes I will look at fixing nested opaque types. |
@swift-ci Please test |
Build failed |
Build failed |
@swift-ci Please test |
Build failed |
Build failed |
auto argConv = apply.getArgumentConvention(opd); | ||
auto argIdx = apply.getCalleeArgIndex(opd); | ||
auto argType = substConv.getSILArgumentType(argIdx); | ||
if (argConv.isIndirectConvention() && |
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 an opaque archetype be loadable (hence the argument convention is direct) if its protocol is class-constrained? I think you want to handle that by emitting an unchecked_ref_cast
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.
Yes. Fixed. Test case added.
bool foundOpaqueArchetype = false; | ||
for (auto &BB : *getFunction()) { | ||
for (auto &inst : BB) { | ||
auto *allocStack = dyn_cast<AllocStackInst>(&inst); |
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.
Why not just look for any instruction containing an opaque 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.
Good point.
return SuperTy::remapASTType(ty) | ||
.subst(replacer, replacer, | ||
SubstFlags::SubstituteOpaqueArchetypes | | ||
SubstFlags::AllowLoweredTypes) |
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.
Indentation
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.
clang-format does that.
return SuperTy::remapConformance(type, conf) | ||
.subst(type, replacer, replacer, | ||
SubstFlags::SubstituteOpaqueArchetypes | | ||
SubstFlags::AllowLoweredTypes); |
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.
Indentation
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.
clang-format does that.
ReplaceOpaqueTypesWithUnderlyingTypes replacer; | ||
return SuperTy::remapSubstitutionMap(Subs).subst( | ||
replacer, replacer, | ||
SubstFlags::SubstituteOpaqueArchetypes | SubstFlags::AllowLoweredTypes); |
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 formatting is not consistent with the previous lines
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.
clang-format does that.
return t | ||
} | ||
|
||
// CHECK-LABEL: sil @$s1A10testFooBaryyxAA1PRzlF : $@convention(thin) <T where T : P> (@in_guaranteed 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.
Can you add a few more test cases?
- concrete type is address-only
- the protocol is class-constrained
- the opaque type is in another module (so it can't be specialized)
- assignment to a stored property whose type is opaque (use the typealias/associated type trick I showed you in the earlier comment). I believe this will crash right now because you have to insert a cast, just like you do for function arguments
|
||
if (foundOpaqueArchetype) { | ||
SubstitutionMap subsMap = getFunction()->getForwardingSubstitutionMap(); | ||
OpaqueSpecializerCloner s(subsMap, *getFunction()); |
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 the opaque type is from a different module (and hence its concrete type is not known to us) this is going to do a lot of wasted work. Can you check for feasibility before cloning the entire function?
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.
Resilience is currently not respected by type replacement: https://github.com/apple/swift/blob/master/lib/AST/Type.cpp#L2459
Why these two functionality are in one pull request? So compiler implementation of specialization is independent from dynamic replacement support for ORT. This comment is not criticism about development process. |
No good reason. |
Look at all instructions whether we have to cast. Return/YieldInst must not change the returned type.
This does not yet have the fix for stores ... |
I am going to remove the OpaqueArchetypeSpecializer from this PR for now. |
@swift-ci Please test |
Build failed |
Build failed |
I have moved the specializer to a separate PR here: #24224 |
@swift-ci Please test |
Thanks to response. |
Thanks for addressing the review feedback, LGTM! |
No description provided.