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
[SR-11777] 'var something: 'Type?' always get double initialised #54184
Comments
@swift-ci create |
> The issue seems to be that [..] gets unconditionally treated as [if there was an explicit initialization with nil]. IMO, by itself, that is very unsurprising behavior. This is something that is guaranteed – Optional properties are default-initialized to nil if one does not spell out a default initial value in the source. I feel the more surprising thing is: why is the " |
See SR-11768. |
I think Johannes concern is that even if `init` does spell out a default value, the init-to-nil happens anyway. That means the only way to spell a default initial value is to do so at the definition site, which is quite a surprise. |
indeed |
> I think Johannes concern is that even if `init` does spell out a default value, the init-to-nil happens anyway. That means the only way to spell a default initial value is to do so at the definition site, which is quite a surprise. I got that. Let me rephrase my point and provide more context for my thought process, at the risk of explaining things that you already know. If the compiler were not an optimizing compiler, the most simple thing to do would be to do what is doing right now – default initialize based on the definition site (in this case nothing is specified, so it will default initialize using nil), and then perform the assignments as the user wrote out in the Notably, this naive codegen does not involve looking at what is written in the Usually, an optimizing compiler would be organized so that it still does the same naive codegen, and then adds extra passes to clean up the IR to something reasonable. In this particular case, since the thing that needs to be cleaned up is a call (you can check that this actually needs to happen using 1. The call gets inlined - then constant propagation + copy propagation + dead code elimination can take care of the rest. Usually, the hammer of choice is inlining. Which is why I pointed out that I was surprised that the call didn't get inlined. To summarize, there are two different perspectives here: 1. That call shouldn't get generated in the first place because the init has a default value for the field – if I understand correctly, this is what Johannes is suggesting. — As an aside, I'm not sure why this bug report has the LibraryEvolution label on it. The first struct declaration in public struct NothingOptional {
@usableFromInline var i: Int?
@inlinable public init() {
self.i = 0xdeadbef
}
} If you add a |
theindigamer (JIRA User) I agree with all of that. Our guess is that the 'variable initialisation expression' function is not inlined because the Swift compiler deliberately chooses not to because it couldn't do that if this were compiled for library evolution. Now we all agree that we're not using the library evolution mode here so it could be inlined and that is one bug (SR-11768). The other bug (which is what this ticket is about) is that all optional variables actually get double initialised, first to |
SR-XX being SR-11768, which is required reading for this bug I think. |
theindigamer (JIRA User) another important clue why all of this is probably related to some library evolution code (that should not be used here) is that if I put the whole program in one module, then the functions all have the assembly that we expect (because now the library evolution stuff is truly not used because we call the inits from the same module instead of across):
|
thanks @Lukasa, forgot to replace the reference at first, edited now. |
theindigamer (JIRA User) regarding > > The issue seems to be that [..] gets unconditionally treated as [if there was an explicit initialization with nil]. You may find this "unsurprising" but my expectation would be (and that is coincidentally our workaround of choice for SR-17768) that there is some spelling in the source language makes the language not pre-initialise an optional ivar to So far my expectation was that {{Optional<T>}}s are a little special and they only Consider this program
Lines 1 to 12 are totally valid Swift, Lines 13 to 19 however will not compile because I can't re-initialise |
Thanks for elaborating Johannes and Cory. I think I get the difference between the two Jiras now. > Now we all agree that we're not using the library evolution mode here so it could be inlined and that is one bug (SR-11768) Understood. But then my question is: why does this Jira have the LibraryEvolution label? I get why SR-11768 has LibraryEvolution but should the label be removed from this Jira? Or is this Jira (double-initialization) especially bad in the presence of library evolution? > The other bug (which is what this ticket is about) is that all optional variables actually get double initialised, first to nil and then to the actual value. I feel like this is not a bug. But that's just my hunch. I should defer to more experienced people here to judge that. > You may find this "unsurprising" but my expectation would be (and that is coincidentally our workaround of choice for SR-17768) that there is some spelling in the source language makes the language not pre-initialise an optional ivar to nil That makes a lot of sense. If there were an opt-in way to do this easy (cannot be opt-out because of source breakage), say something like an attribute > So far my expectation was that Ah, I see![]( Now I understand why you are expecting that behavior for {{var}}) Here's why (I think) it doesn't work the same way for
All that said and done, I think there is a solution! Don't use the ? sugar, use Optional. That has different behavior for default initialization (made clear in this commit?): 25e4ca9 struct S {
var x: Optional<Int>
init() { } // error: self.x is not initialized
}
struct S {
var x: Int?
init() { } // ok: self.x does get default initialization
} |
theindigamer (JIRA User) OMG, TIL, thank you so much! I change NIO's uses of
the only difference between |
this is the SwiftNIO PR to get rid of |
Marking as resolved since using Optional<T> instead of T? seems to have the desired effect. 🙂 |
@weissi I read your PR and you mention it is a "workaround" – does this not resolve the issue? The other option would be to change the behavior of Do you still want some kind of special annotation on top of |
thanks theindigamer (JIRA User). For now, I'm happy with the workaround but I do think that SR-11768 is still a bug, I think with resilience off, we shouldn't need ( |
That |
> For now, I'm happy with the workaround but I do think that SR-11768is still a bug, I think with resilience off, we shouldn't need ( Ok, so this one's resolved, you can continue discussing with Slava for that one. 🙂 > That I was sufficiently alarmed about it to make a tweet referencing this discovery https://twitter.com/cutculus/status/1195050506177003521 But yeah, it would be heavily source-breaking, so we can't change this unless we apply an automated refactoring that goes and adds nils in places where people are using |
Attachment: Download
Additional Detail from JIRA
md5: e292409f7b3781399988c2039678ebcc
relates to:
Issue Description:
demo program
consider the following program
The main module (code just below) literally just initialises three structures called
Nothing*
. Each of them has a trivial@inlinable
init that intialises its solevar
with0xdeadbeef
The
Foo
module has this codethe bug
I would expect the assembly of all the three
doNothing*
methods to look likeand indeed, for the
doNothingInt
anddoNothingOptionalPrevent
that is the case:for the
doNothingOptional
however, we see suboptimal code:please note
call _$s3Foo15NothingOptionalV1iSiSgvpfi ; variable initialization expression of Foo.NothingOptional.i : Swift.Int?
!the
variable initialization expression
function has the following code:which initialises the
var
tonil
, to then only be overridden immediately by the remainder of theinit
to non-nil0xdeadbeef
.what's the issue
The issue seems to be that
unconditionally gets treated as if it had a
even if the
init
explicitly sets it which is the workaround for SR-11768 but unfortunately, it doesn't seem to work for anything optional.workaround
The only way to escape this auto-setting of
nil
seems to be to useThe text was updated successfully, but these errors were encountered: