-
Notifications
You must be signed in to change notification settings - Fork 10.4k
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
[Sema] Synthesize default values for memberwise init #19743
Conversation
@Azoy Instead of moving the initializer into a helper function, a different strategy would be to add a new DefaultArgumentKind that simply points at the VarDecl. VarDecls with an initial value already emit a generator function in SILGen, and this function can itself be used as the default argument for the constructor. This way you avoid emitting a helper function entirely. I would prefer this strategy because it should be simpler to implement and is less error prone (imagine if in the future we add another type of Expr that requires special handling here, we'd have to remember to update it). |
@slavapestov what value can I give the parameter for 1. the type checker to be happy and 2. be able to call the variable initializer? |
@Azoy Sorry, I'm not sure what you mean by "parameter" |
The parameter for the constructor. struct X {
var y = 0
// synthesized
init(y: Int = 0)
} In this example the parameter being |
Yes, the 'default value' is an Expr only used with DefaultArgumentKind::Normal. I would add a new DefaultArgumentKind::StoredProperty (or something similar).
I think you want to explicitly construct a ParamDecl with this DefaultArgumentKind when you synthesize the memberwise initializer. However when calling a memberwise initializer, nothing needs to be special cased I think -- Sema doesn't really care about the presence of the expression when calling the initializer.
Add the VarDecl to |
@slavapestov Apologies if something here isn't correct, I'm new to the SILGen codebase, but I think for the most part this looks ok? You were right about Sema not caring about the presence of the expression, so that was a reliever! struct X {
var y = 0
} The memberwise constructor's default argument looks like this:
Currently the stored property kind is printed as the variable name, so @jrose-apple or @harlanhaskins might have some input on how to properly print it. |
6e72a19
to
2201564
Compare
I’ll take a look tomorrow. I don’t think it matters how we print these default arguments since memberwise initializers are never public. Also your PR needs tests :) |
Yes, tests will be added shortly! |
@slavapestov I've run into some interesting behavior with optionals concerning with the creation of the implicit constructor(s). struct X {
var y: Int?
// Compiler synthesized
init(y: Int?)
// Compiler synthesized
init() { return }
}
let x = X() Would it make sense to assign the default value of struct X {
var y: Int?
// Compiler synthesized
init(y: Int? = nil)
}
let x = X() |
@Azoy, yes, properties with optional type have a "default" initial value of nil. I suggested to swift-evolution once to remove this edge case, but there was push back so we're stuck with it. :) |
I'm having some trouble with some of the SIL being generated for the default argument for some types. import Foundation
struct X {
var y = Data(bytes: [])
} Default argument function:
I see what it's trying to do with ResultPlan noticing the indirect |
8d5c642
to
15c447b
Compare
@slavapestov I'm running tests locally and I'm getting some weird behavior with some structures not wanting to print their stored property. For example, in // CHECK-LABEL: private{{(\*/)?}} struct BB_PrivateStruct {
private struct BB_PrivateStruct {
// CHECK: internal var x
var x = 0
// CHECK: internal init(x: Int)
// CHECK: internal init()
} // CHECK: {{^[}]}} It complains that it couldn't find the first init, but it suggests |
c0e5708
to
5b40480
Compare
@harlanhaskins Could you take a look at the latest commit? I haven't run validation tests yet, but the only failing test I'm getting is with |
5b40480
to
19c18fd
Compare
@slavapestov is there a specific test file for the memberwise constructor that I'm missing, or is it some other test file? |
37d9d67
to
ac4eeec
Compare
ac4eeec
to
0adae2b
Compare
@Azoy This looks great, glad to see it coming up for review! Would you mind resolving the conflict? I'd like to build a toolchain with this feature. |
f0c4fe4
to
904a988
Compare
@natecook1000 done and done! |
Build failed |
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 to come back with more review feedback at this stage; I also missed the 'lazy' case and I want to make sure we get it right because 'lazy' has tripped all of us up before.
lib/Sema/CodeSynthesis.cpp
Outdated
// Note, this will always be the sugared T? because we don't default init an | ||
// explicit Optional<T>. | ||
if (isa<OptionalType>(var->getType().getPointer()) && !var->hasInitialValue()) | ||
arg->setDefaultArgumentKind(DefaultArgumentKind::NilLiteral); |
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.
... in fact this already appears to correctly handle the lazy property case. Maybe just remove the areLazyAccessorsSynthesized
check above and see what happens?
Also, can you restructure this code a bit so that setDefaultArgumentKind() is only called once? It's a bit confusing to see it overwritten later. I'm guessing you don't really need to call setStoredProperty() either in the NilLiteral case?
clean up lazy vars more docs
0c6bc2d
to
dcedba7
Compare
@slavapestov ok, I think this is the one! I made it so that we check for |
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.
The new version looks great! I still have a small cleanup suggestion, and one concern related to serialization with -enable-testing but I think we should merge this now, and address the below in a follow-up PR.
if (ctor->isMemberwiseInitializer() && ctor->isImplicit()) { | ||
auto param = ctor->getParameters()->get(info.destIndex); | ||
|
||
if (auto var = param->getStoredProperty()) { |
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.
Does this work across modules? Normally you can’t call a memberwise initializer since they’re internal, but if a module is built with -enable-testing and imported with @testable import you can. I might be wrong but I don’t see ParamDecl::getStoredProperty() serialized anywhere.
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 tried to spin up a quick test to test the current behavior, and I don't believe that we currently deserialize any default value from a module, but rather just the arg kind and the string representation. I'd be curious to hear how we could go about setting the stored property arg after we deserialize (maybe just take param name and look for the corresponding property?). Right now this emits an apply to the default arg generator (which there is none, so I believe the linker will complain...).
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 the right solution is to change the code they serializes a ParamDecl to serialize a cross reference to the corresponding VarDecl. I would try to avoid a name-based lookup here. @jrose-apple might have more suggestions
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 emitting an apply to the default arg generator is the correct behavior. The fact that the expression comes from a stored property shouldn't matter except when generating the 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.
Emitting a default arg generator in this case doesn't work because the module with the stored property default arg doesn't emit a default arg generator function. So when we go to deserialize a stored property default arg, if we emit a default arg generator apply then we're going to be linking against undefined symbols.
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.
Emitting a default arg generator in this case doesn't work because the module with the stored property default arg doesn't emit a default arg generator function.
Hm. Why doesn't it? Isn't this a bug? (under -enable-testing
, anyway)
@Azoy do you mind updating the PR description now that the proposal has been accepted and the implementation has moved away from synthesizing closure? |
@swift-ci Please test source compatibility |
@swift-ci Please test |
Looks like CI is having problems. I'm on vacation until Tuesday; @DougGregor or @tkremenek will help you get this merged in the mean time. |
Build failed |
@swift-ci Please test Linux platform |
@slavapestov has approved, so I am merging. |
@Azoy: Please create a PR against |
I just noticed the recent commentary on this PR. I think this is OK to merge now for incremental development, but let's address the remaining concerns in a separate PR. A 5.1 PR can contain changes from both PRs. |
When creating PR for |
@Azoy: is there a 5.1 PR for this yet? |
@tkremenek No, sorry not yet. I'm working with Jordan and Slava to get #23648 merged. Once that's merged (which is hopefully tonight), I will submit a 5.1 PR. |
[Sema] Synthesize default values for memberwise init module minor version
Previously we were synthesizing something like the following:
This patch is a QoL change to the way we synthesize the memberwise constructor for structures.
Swift Evolution review: https://forums.swift.org/t/se-0242-synthesize-default-values-for-the-memberwise-initializer/20618
cc: @jrose-apple @harlanhaskins