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-12672] Misleading diagnostic "type 'Any' has no member" #55116

Closed
typesanitizer opened this issue Apr 26, 2020 · 9 comments
Closed

[SR-12672] Misleading diagnostic "type 'Any' has no member" #55116

typesanitizer opened this issue Apr 26, 2020 · 9 comments
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

Comments

@typesanitizer
Copy link

Previous ID SR-12672
Radar rdar://problem/62383753
Original Reporter @typesanitizer
Type Bug
Status Resolved
Resolution Done
Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, DiagnosticsQoI
Assignee None
Priority Medium

md5: 166b2b63257c728a511b480a2a2e5a0b

Issue Description:

If one has code like

enum E1 {case e}
enum E2 {case e}
let z = [.e] // error: type 'Any' has no member 'e'

This is a compiler from GitHub master on Apr 23 0bbf378.

The diagnostic isn't helpful IMO; the user didn't write `Any` in the source code so it shouldn't be shown in the message unless absolutely necessary. Instead, it should be saying something like

enum E1 {case e}
enum E2 {case e}
let z = [.e] // error: ambiguous base type for member 'e'
             // note: the types 'E1' and 'E2' both have a member 'e'  
@typesanitizer
Copy link
Author

@swift-ci create

@typesanitizer
Copy link
Author

I just realized my mistake; I've had the wrong mental model for the past 9 months! We don't infer base types, we only check them. So it's not really possible to provide a diagnostic like "// note: the types 'E1' and 'E2' both have a member 'e' " because we don't have that information available; figuring that out would require going through all the types in scope (I had guessed that we were already doing that to infer base types, but apparently not).

Instead, we should be saying something like

error: cannot infer contextual base in reference to member 'e' 

which is what would happen if the code had something like `.some(.e)` instead of `[.e]`.

Maybe I'm missing something on why we don't provide that error and talk about `Any` here.

@LucianoPAlmeida
Copy link
Collaborator

theindigamer (JIRA User) I was looking into this, seems that because of the Array literal initializer and the constraint where ArrayLiteralElement can default to Any the solver is attempting a binding for the array type Array<Any> which leads to .e := Any and recording a define missing member fix. And I think because of the rules around score and impact that end up the solution being diagnosed instead of many ambiguous fixes produced and diagnose it on DefineMemberBasedOnUse::diagnoseForAmbiguity where the unresolved_member_no_inference is produced.
I don't really know what would be a solution for this, maybe tweak impact and score... not really sure cc @xedin @hborla

@typesanitizer
Copy link
Author

Thanks for looking into it Luciano! You mention

> constraint where ArrayLiteralElement can default to Any

That seems surprising to me. I'm looking at the test cases in the compiler and it seems like when we are doing inference for array literals, we don't really return `Array<Any>` as the type of an array literal – is that correct? We only assign the type `Array<Any>` to a literal in checking mode. We even have an error for this:

heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional 

If my understanding is correct, could we make it so that:

  1. Checking an array literal continues like it does today.

  2. During inference, we don't use `Any` at all and infer the type of the array.

    1. If inference succeeds then great, we are done!

    2. If inference fails, check that the literal would type-check if we had annotated it with a type `Array<Any>`.

      1. If the check succeeds, great, provide the `[Any]` diagnostic we provide today about the explicit type annotation (the one quoted above).

      2. If the check fails, give up and provide some fallback diagnostic.

What do you think?

(To be clear, I'm describing things in terms of my mental model of how the type-checker works, which may not match how it actually works.)

@LucianoPAlmeida
Copy link
Collaborator

Hey theindigamer (JIRA User) : )
I just mentioned the array literal constraint as is was the output I saw in debug-constraints and seems the place from where the Any binding could be coming from.
Here is a snipet:

 $T4 conv $T0 [[locator@0x11c372758 [Assign@/Users/lucianoalmeida/Documents/Programming/swift-lang/swift/test/expr/test.swift:6:3]]];
  ($T4 literal=1 involves_type_vars bindings={(subtypes of) (default from ExpressibleByArrayLiteral) Array})
  Initial bindings: $T4 := Array
  (attempting type variable $T4 := Array
    ($T0 involves_type_vars bindings={(supertypes of) [$T6]})
    ($T6 potentially_incomplete involves_type_vars #defaultable_bindings=1 bindings={Any})
    Initial bindings: $T0 := [$T6]
    (attempting type variable $T0 := [$T6]
      ($T6 potentially_incomplete involves_type_vars #defaultable_bindings=1 bindings={Any})
      Initial bindings: $T6 := Any
      (attempting type variable $T6 := Any
        ($T3 subtype_of_existential involves_type_vars bindings={(subtypes of) Any})
        Initial bindings: $T3 := Any
        (attempting type variable $T3 := Any
          (failed constraint $T1.Type[(implicit) .e: value] == $T2 [[locator@0x11c372288 [UnresolvedMember@/Users/lucianoalmeida/Documents/Programming/swift-lang/swift/test/expr/test.swift:6:7 -> unresolved member]]];)
        )
      )
    )
  )

> During inference, we don't use `Any` at all and infer the type of the array.
I'm also not realy sure if we could reject the Any binding for this case, since my knowledge on the rules for inference is not too deep. Or maybe I didn't understand it correctly...

Also, in this case, the Any binding always fails(because Any don't satisfy the member constraint) and records the define missing member fix. The problem is that for some reason that is the solution that ends up being diagnosed. I think we have to find the reason why this solution (score or fix impact or some other reason I don't know?) instead of diagnosing the contextual ambiguity.

I've ping @xedin and @hborla to see if that makes sense or if it may be some other problem 🙂

@xedin
Copy link
Member

xedin commented Apr 28, 2020

@LucianoPAlmeida I think the fix is correct we just need to add some special handling for situations like this to the `MissingMemberFailure` diagnostic (`cannot infer contextual base ...` is a pretty good suggestion IMHO). There is a way to identify that member was used as an element to an array literal and whether element type has been defaulted to `Any`.

@LucianoPAlmeida
Copy link
Collaborator

Makes sense, thank you @xedin 🙂

@LucianoPAlmeida
Copy link
Collaborator

Here a PR #31366

@LucianoPAlmeida
Copy link
Collaborator

cc theindigamer (JIRA User)

@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
Projects
None yet
Development

No branches or pull requests

3 participants