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-12024] Type inference gone wrong #54461

Open
swift-ci opened this issue Jan 14, 2020 · 4 comments
Open

[SR-12024] Type inference gone wrong #54461

swift-ci opened this issue Jan 14, 2020 · 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

@swift-ci
Copy link
Collaborator

Previous ID SR-12024
Radar rdar://problem/58571328
Original Reporter eimantas (JIRA User)
Type Bug
Environment

macOS: 10.15.2
Xcode: 11.3

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

md5: 182f9aa190723b7abb449c702c036d42

Issue Description:

Consider the following code sample:

import Foundation

protocol Proto { }

struct Stru_00: Proto { }
struct Stru_55: Proto { }

var maybeStru_00: Stru_00? = Stru_00()
var maybeStru_55: Stru_55? = Stru_55()

if let stru_00 = maybeStru_00 {
    var arr: [Proto] = [stru_00, maybeStru_55].compactMap { $0 }
    print(arr)
}

When running this code in playground I get the following error:

error: Sandbox.playground:13:25: error: cannot convert value of type 'Stru_00' to expected element type 'Stru_55?'
    var arr: [Proto] = [stru_00, maybeStru_55].compactMap { $0 }

I believe this to be a bug because var arr is explicitly typed to be an array of Proto conforming objects.

@theblixguy
Copy link
Collaborator

I have renamed types in your code to make it easier to read:

protocol Proto { }

struct A: Proto { }
struct B: Proto { }

var optA: A? = A()
var optB: B? = B()

if let optA = optA {
  let arr: [Proto] = [optA, optB].compactMap { $0 }
  print(arr)
}

I think there are two problems here:

  1. You have a mixed array i.e a non-optional value and an optional value mixed.

  2. The type of the intermediate array is [Any] (since you have a mixed array)

If you force-unwrap optB, then you'll see the real problem, which is that the closure's return type is Proto? and the argument is of type Any, and so Any cannot be converted to Proto?.

To fix your problem, you should cast the intermediate array before invoking compactMap on it:

let arr: [Proto] = ([optA, optB] as [Proto?]).compactMap({$0})

@theblixguy
Copy link
Collaborator

cc @xedin

@xedin
Copy link
Member

xedin commented Jan 14, 2020

Yeah, the problem here is that type-checker doesn't try to join types up to protocols so it would attempt element types or `Any`. The problem that `compactMap` disconnects result type from element type (signature is something like `((S.Element) -> Result) -> [Result]`, if it didn't, we'd be able to infer `Proto` for the element type.

@xedin
Copy link
Member

xedin commented Jan 14, 2020

@swift-ci create

@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