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-10509] Optional map with nil coalesce fails to compile in if-statement #52909

Closed
glbrntt opened this issue Apr 18, 2019 · 6 comments
Closed
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself diagnostics QoI Bug: Diagnostics Quality of Implementation parser Area → compiler: The legacy C++ parser

Comments

@glbrntt
Copy link

glbrntt commented Apr 18, 2019

Previous ID SR-10509
Radar None
Original Reporter @glbrntt
Type Bug
Status Resolved
Resolution Duplicate
Environment
  • macOS 10.14.4

The results are the same on all three toolchains:

  • swift-5.1-DEVELOPMENT-SNAPSHOT-2019-04-16-a

  • swift-5.0-RELEASE

  • swift-4.1-RELEASE

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, DiagnosticsQoI, Parser
Assignee None
Priority Medium

md5: 700c8a911c5e2a7709e3e9b418643ec0

duplicates:

  • SR-5869 trailing closure not parsed correctly in if-statement conditional

Issue Description:

The following fails to compile:

func checkFoo(value: String?) {
  if value.map { $0 == "foo" } ?? false {
    // ...
  }
}

Here's the output:

NilCoalesce.swift:2:18: error: anonymous closure argument not contained in a closure
  if value.map { $0 == "foo" } ?? false {
                 ^
NilCoalesce.swift:2:31: error: consecutive statements on a line must be separated by ';'
  if value.map { $0 == "foo" } ?? false {
                              ^
                              ;
NilCoalesce.swift:2:32: error: unary operator cannot be separated from its operand
  if value.map { $0 == "foo" } ?? false {
                               ^ ~
                                 
NilCoalesce.swift:2:40: error: consecutive statements on a line must be separated by ';'
  if value.map { $0 == "foo" } ?? false {
                                       ^
                                       ;
NilCoalesce.swift:2:12: error: generic parameter 'U' could not be inferred
  if value.map { $0 == "foo" } ?? false {
           ^
NilCoalesce.swift:2:32: error: '??' is not a prefix unary operator
  if value.map { $0 == "foo" } ?? false {
                               ^
NilCoalesce.swift:2:41: error: closure expression is unused
  if value.map { $0 == "foo" } ?? false {
                                        ^
NilCoalesce.swift:2:41: note: did you mean to use a 'do' statement?
  if value.map { $0 == "foo" } ?? false {
                                        ^
                                        do

The following does compile:

  • With parentheses:

    func checkFoo(value: String?) {
      if (value.map { $0 == "foo" } ?? false) {
        // ...
      }
    }
  • Assigning to value:

    func checkFoo(value: String?) {
      let _ = value.map { $0 == "foo" } ?? false
    }

    I expect the code to compile without extra parentheses or having to assign it to a value first.

@belkadan
Copy link
Contributor

This is a general rule about trailing closures in conditions because in their simplest form they're ambiguous (well, nearly):

if foo.contains { $0.name == "Swift" }
{
  print("okay")
}

if foo.containsSwift { print("okay" }
{
  print("oops, I forgot a 'do' at the start of this block")
}

But it's worth trying to improve the diagnostic here, at least.

@belkadan
Copy link
Contributor

@rintaro, do we have other bugs tracking this?

@glbrntt
Copy link
Author

glbrntt commented Apr 18, 2019

Better diagnostics would certainly help; it wasn't at all clear what the problem was.

Why does adding parentheses work in this case, it's not obvious to me at all?

if (value.map { $0 == "foo" } ?? false) {
  // ...
}

@belkadan
Copy link
Contributor

The rule is "because of the near-ambiguity, you can't put trailing closures directly into the condition of an if". Parentheses provide a way for the compiler to be sure that you meant the trailing closure to be part of the condition.

The rule here could be made more complicated to account for all the cases where the compiler could figure it out, but we thought that (1) that would feel more capricious to developers who did run into the problem cases, and (2) it would lead to worse behavior around incomplete code (say, for code completion).

@glbrntt
Copy link
Author

glbrntt commented Apr 18, 2019

Ahh got it, thanks for the explanation!

@rintaro
Copy link
Mannequin

rintaro mannequin commented Apr 18, 2019

This is https://bugs.swift.org/browse/SR-5869 .

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
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 diagnostics QoI Bug: Diagnostics Quality of Implementation parser Area → compiler: The legacy C++ parser
Projects
None yet
Development

No branches or pull requests

2 participants