-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Deterministically order class members for emission #32581
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
Deterministically order class members for emission #32581
Conversation
@@ -2451,16 +2503,40 @@ EmittedMembersRequest::evaluate(Evaluator &evaluator, | |||
forceConformance(Context.getProtocol(KnownProtocolKind::Decodable)); | |||
forceConformance(Context.getProtocol(KnownProtocolKind::Encodable)); | |||
forceConformance(Context.getProtocol(KnownProtocolKind::Hashable)); | |||
forceConformance(Context.getProtocol(KnownProtocolKind::Differentiable)); |
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.
@dan-zheng @rxwei I think this should be uncontroversial. We need to force the derived members, if any, to be synthesized here, because the result of this request is now cached.
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.
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.
LGTM! You'll see I have teeny nit, but it's probably not worth the time to address it.
@@ -4318,6 +4318,10 @@ GetDestructorRequest::evaluate(Evaluator &evaluator, ClassDecl *CD) const { | |||
if (ctx.LangOpts.EnableObjCInterop) | |||
CD->recordObjCMethod(DD, DD->getObjCSelector()); | |||
|
|||
// Mark it as synthesized to make its location in getEmittedMembers() | |||
// deterministic. |
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.
Nice "why" comment!
if (auto *cd = dyn_cast<ConstructorDecl>(afd)) | ||
mangledName = mangler.mangleConstructorEntity(cd, /*allocator=*/false); | ||
else if (auto *dd = dyn_cast<DestructorDecl>(afd)) | ||
mangledName = mangler.mangleDestructorEntity(dd, /*deallocating=*/false); | ||
else | ||
mangledName = mangler.mangleEntity(afd); |
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.
Too bad we don't just have a member function on AbstractFuncDecl to compute this "mangledNameForSorting". I wonder if this functionality is duplicated elsewhere?
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.
mangleEntity() will assert if you pass in a destructor or constructor. The reason we have separate entry points for those is that usually they have multiple manglings, for the (de)allocating and (de)initializing entry points, respectively. I don't think it's worth factoring this out.
c023ce5
to
141dfd0
Compare
@swift-ci Please test |
@swift-ci Please test source compatibility |
Build failed |
141dfd0
to
84ec411
Compare
@swift-ci Please test |
@swift-ci Please test source compatibility |
Build failed |
Build failed |
Build failed |
We sometimes need to mangle invalid identifiers, for example when sorting declarations in EmittedMembersRequest. Make sure this doesn't assert.
Previously we did this in the SILVTableVisitor, since maintaining a consistent order between translation units is important for correctness. However, we also want the order in which the members themselves are emitted to remain consistent regardless of the order in which members were synthesized. This is in service of reproducible builds.
… function order We can synthesize the default init() and the implicit deinit in a class in any order, depending on the order of primary files and how that class was used in other primary files. Make sure that EmittedMembersRequest puts the destructor at the end with all other synthesized members, so that we produce the same object file in any case.
84ec411
to
89b2f27
Compare
@swift-ci Please smoke test |
Mimic changes in upstream commit: faa06bf Context: #32581 (comment)
I think that this change might be the one responsible for a recent regression with // RUN: %target_swiftc -parse-as-library -parse-stdlib -module-name Swift -S -o - %s
@main
class S {
public static func main() {
}
} The |
I have another sample code which not builds:
Linker tells about unresolved externals:
Noticed on Windows, but I guess it is multiplatform. I am pretty sure it is caused by this change. Previous commit does not fail. |
We already did this for vtable entries, because its required for correctness; sink the ordering down into EmittedMembersRequest in service of reproducible builds as well.
This fixes an issue found by @davidungar where the driver's batching could flip the order of a class's default init() and deinit members, which tripped up our incremental build testing.
Along the way, I discovered that caching the emitted members list broke deriving the
Differentiable
conformance, because we might cache the member list before synthesizing the members; fixing this by forcing the conformance inEmittedMembersRequest
in turn uncovered some order dependencies in conformance derivation, which I already addressed in #32578.