Skip to content
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-8326] Surprising interaction between casting to Any and optional promotion #50854

Open
lorentey opened this issue Jul 20, 2018 · 4 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself type checker Area → compiler: Semantic analysis

Comments

@lorentey
Copy link
Member

Previous ID SR-8326
Radar None
Original Reporter @lorentey
Type Bug
Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, TypeChecker
Assignee None
Priority Medium

md5: d3ffe3ad04c62bbbfa33f820cf0e61f4

relates to:

Issue Description:

Consider the code below.

let wat: Int? = nil
if let _: Int? = wat { print("1") }
if let _: Any? = wat { print("2") }

The first condition always succeeds because wat is promoted to Int??, and produces a nice compiler warning about it. However, there is a surprising mismatch between the first and second conditions – the coercion to Any disables optional promotion, and instead, it attempts to unwrap wat and cast it to an Optional<Any> value. When run, the program prints "1" only.

$ swift wat.swift
wat.swift:2:4: warning: explicitly specified type 'Int?' adds an additional level of optional to the initializer, making the optional check always succeed
if let _: Int? = wat { print("1") }
   ^      ~~~~   ~~~
          Int
1

I'd have expected the two conditions to either both pass, or both fail. (The latter option seems to make slightly more sense – it seems surprising that we allow optional promotion of an optional value in an optional check.)

@belkadan
Copy link
Contributor

Uh, hm. Here's the AST dump:

      (if_stmt
        (pattern
          (pattern_typed type='Any??'
            (pattern_optional_some implicit type='Any??'
              (pattern_let implicit type='Any?'
                (pattern_any type='Any?')))
)
          (optional_evaluation_expr implicit type='Any??' location=<stdin>:3:18 range=[<stdin>:3:18 - line:3:18]
            (inject_into_optional implicit type='Any??' location=<stdin>:3:18 range=[<stdin>:3:18 - line:3:18]
              (inject_into_optional implicit type='Any?' location=<stdin>:3:18 range=[<stdin>:3:18 - line:3:18]
                (erasure_expr implicit type='Any' location=<stdin>:3:18 range=[<stdin>:3:18 - line:3:18]
                  (bind_optional_expr implicit type='Int' location=<stdin>:3:18 range=[<stdin>:3:18 - line:3:18] depth=0
                    (declref_expr type='Int?' location=<stdin>:3:18 range=[<stdin>:3:18 - line:3:18] decl=main.(file).wat@<stdin>:1:5 direct_to_storage function_ref=unapplied)))))))

That's not the conversion path I would expect, but even with this one I wouldn't expect it to fail. @rudkx, @jckarter, any idea what's going on?

@rudkx
Copy link
Member

rudkx commented Jul 20, 2018

I came across this when doing the IUO work, but haven't had time to follow up on it.

The code in ExprRewriter::coerceOptionalToOptional will simply inject optionals as many levels deep as necessary if the underlying types are the same, but if any other conversion is required it wraps the injection in an OptionalEvaluationExpr / BindOptionalExpr, which aborts the conversion if the source operand is nil.

IMO the behavior here should be the behavior of the final statement in the example - we should always evaluate the condition based on the RHS, not based on the RHS-after-some-number-of-conversions-and-injections. At the very least that will require some discussion in additional the compiler change, and needs to be guarded under Swift version. For some reason when I came across this I was also convinced that its a nontrivial amount of work that would require reworking if let and guard let, but offhand I don't recall why.

This came up in the context of IUOs because when IUO was a separate type there was more than just injection going on, so you got the behavior of bailing if the RHS was nil.

@rudkx
Copy link
Member

rudkx commented Jul 20, 2018

I'll post something to Evolution Discussion about this. I realize now why it's not a trivial change to make.

@rudkx
Copy link
Member

rudkx commented Jul 20, 2018

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself type checker Area → compiler: Semantic analysis
Projects
None yet
Development

No branches or pull requests

3 participants