[swift-users] Can anyone please explain this behavior?

Jens Persson jens at bitcycle.com
Thu Sep 22 13:03:34 CDT 2016


Thank you for the thorough explanation!
/Jens

On Thu, Sep 22, 2016 at 7:28 PM, Jordan Rose <jordan_rose at apple.com> wrote:

> Yep, it really is a long-standing bug. Script-mode top-level locals are
> treated as globals (module-scope bindings) by the compiler, but their
> initial bindings are evaluated eagerly instead of lazily (as you’d want in
> a script). Taken together, this means that you can get this completely
> unsafe behavior.
>
> So, why is ‘a’ accepted but ‘b’ not in your original example?
>
> func foo() -> Int { return b }
> let a = 1
> let b = 2
> print(foo())
>
>
> The secret to the current behavior is that script mode is executed
> interactively, instead of parsing it all up front. To make things a little
> better, it *actually* parses any number of declarations until it sees
> something it actually needs to execute—a statement or a declaration with an
> initial value expression. This allows for recursive functions while still
> being “live”.
>
> The consequence here is that *one* top-level binding after a series of
> functions may be visible. This is obviously not optimal.
>
> To fix this, we should:
>
> - Distinguish between script-mode top-level locals and module-scope
> variables that happen to be declared. My personal preference is to treat
> anything with explicit access control as a normal lazy global and anything
> without access as a top-level local.
>
> - Consider parsing everything up front, even if we don’t type-check it, so
> that we can say “use of ‘b’ before it’s initialized” instead of “undeclared
> name ‘b’”
>
> Note that we *do* need to be conservative here. This code should continue
> to be rejected, even though ‘f’ doesn’t refer to ‘local’ directly, because
> *calling* ‘f' would be dangerous before the initialization of ‘local':
>
> internal func f() -> Int {
>   return g()
> }
> *// more code here*
>
> let local = 42
> private func g() -> Int {
>   return local
> }
>
>
> Thanks for bringing this up, if only so I have an opportunity to write out
> the issue. :-)
> Jordan
>
>
> On Sep 21, 2016, at 23:04, Jens Persson <jens at bitcycle.com> wrote:
>
> Did you see the other code examples that came up in that twitter
> conversations?
> For example:
>
> This worrying little program compiles:
> func f() -> Int {
>     return a
> }
> let a = f()
>
>
> It also compiles if you print(a) at the end, and it will print 0.
>
> If we replace Int with [Int] it will still compile but crash when run.
>
> And also this:
>
> AnotherFile.swift containing:
> func f() -> Int {
>     return a
> }
> let a = f()
>
> main.swift containing
> print(a)
>
> Compile, run (for eternity, at 0% CPU).
>
> /Jens
>
>
> On Thu, Sep 22, 2016 at 3:13 AM, Joe Groff <jgroff at apple.com> wrote:
>
>>
>> > On Sep 21, 2016, at 2:22 PM, Jens Persson via swift-users <
>> swift-users at swift.org> wrote:
>> >
>> > // This little Swift program compiles (and runs) fine:
>> >
>> > func foo() -> Int { return a }
>> > let a = 1
>> > let b = 2
>> > print(foo())
>> >
>> > But if `foo()` returns `b` instead of `a`, I get this compile time
>> error:
>> > "Use of unresolved identifier `b`"
>>
>> This looks like a bug to me (cc-ing Jordan, who's thought about global
>> scoping issues more than me). In "script mode", it shouldn't be possible to
>> refer to a variable before its initialization is executed. However, the way
>> this is currently modeled is…problematic, to say the least, among other
>> reasons because script globals are still visible to "library" files in the
>> same module.
>>
>> -Joe
>
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20160922/11a259f4/attachment.html>


More information about the swift-users mailing list