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-3492] Unexpected inheritance of nested types #46080

Closed
krzyzanowskim opened this issue Dec 26, 2016 · 10 comments
Closed

[SR-3492] Unexpected inheritance of nested types #46080

krzyzanowskim opened this issue Dec 26, 2016 · 10 comments
Assignees
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself

Comments

@krzyzanowskim
Copy link
Contributor

Previous ID SR-3492
Radar None
Original Reporter @krzyzanowskim
Type Bug
Status Resolved
Resolution Done
Environment

Swift 3.0.2

Additional Detail from JIRA
Votes 6
Component/s Compiler
Labels Bug
Assignee @slavapestov
Priority Medium

md5: 4512528e0c89827aea622ea49002f198

is duplicated by:

  • SR-10563 Shadowing Base Class Generic With Another Generic Type Causes Compile Error

Issue Description:

The nested enum type inherits it's options, while it shouldn't inherit anything.

This code should compile without errors:

class A {
    enum Reusable {
        case option1
    }
}

class B: A {
    enum Reusable {
        case option1
    }
    
    func process() {
        _ = B.Reusable.option1
        
        //      Untitled.swift:13:9: error: ambiguous use of 'option1'
        //              _ = B.Reusable.option1 // how this is ambiguous?
        //                    ^
        //      Untitled.swift:9:8: note: found this candidate
        //              case option1
        //                   ^
        //      Untitled.swift:3:8: note: found this candidate
        //              case option1
        //                   ^
        
    }
}

let a = A()
let b = B()

https://gist.github.com/krzyzanowskim/d02732571b45902154dcf3939a735eb7

this code souldn't compile

class A {
    enum Reusable {
        case option1
    }
}

class B: A {
    enum Reusable { }
}

let _ = A.Reusable.option1
let _ = B.Reusable.option1 // wat???
@CodaFi
Copy link
Member

CodaFi commented Jan 1, 2017

There isn't some notion of "absolute uniqueness" (as though "B.X.Y" were some kind of canonical index into the type) in local scope. When doing a lookup there in the first non-functioning example we gather every possibility. Because `. Reusable` exists as a member in the superclass and in the subclass, and it is not marked private, it is still accessible from `B` so we find it. When Sema sees two valid overloads, it checks both and finds that both references are valid and require no conversions to instantiate the type variable left by the underbar pattern, hence the two solution sets are indistinguishable enough to warrant a diagnostic about ambiguity. In global scope we consider the stricter access you seem to want in the inner scope.

@jtbandes
Copy link
Collaborator

jtbandes commented Jan 1, 2017

If something like B.Reusable can never distinguish the version declared as a nested type of A from the nested type of B, does that mean there is no way to refer to the nested type of B? If that's the case, shouldn't we reject the second nested type declaration to prevent the user from ever getting into this situation?

@krzyzanowskim
Copy link
Contributor Author

That's…not how I'd expect it to behave, I think @jckarter agree on that (https://twitter.com/jckarter/status/810896048843583488)
If it's by design, is there a way to adjust the design? 🙂

class A {
    struct S1 {
        func desc() {
            print("S1")
        }
    }
}

class B: A {
    struct S1 {
        func desc() {
            print("S2")
        }       
    }   
}


A.S1().desc() // ok
B.S1().desc() // ambiguous use of 'init()'

@CodaFi
Copy link
Member

CodaFi commented Jan 1, 2017

Given there is qualification here we could change lookup to check if it already has a possible qualified overload and reject the overload that requires substituting `B`. @slavapestov Do you foresee any problems with that?

@slavapestov
Copy link
Member

There is code out there that relies on this working:

class Base {
  struct T {}
}

class Derived : Base {}

let x: Derived.T = ...

As for the case where we have two nested types, I think the best fix is to tighten up the shadowing and override checks in name lookup. If both a base class A and a derived class B define the same member type 'foo', a lookup of 'foo' into 'B' should not find 'A.foo'. For non-type members this is handled by virtue of the fact that they must either be declared 'override', or become invalid.

For nested type members, we can extend the override and shadowing filter to handle nested types by dropping the base class's type.

@belkadan
Copy link
Contributor

belkadan commented Jan 6, 2017

I agree with Slava's analysis. Nested types are intended to be visible on subclasses, but they should also be shadowable.

@slavapestov
Copy link
Member

I'll take a look at this soon. I've wanted to investigate the shadowing logic for a while since I suspect it's a big mess 🙂

@AliSoftware
Copy link
Contributor

Hey @slavapestov 🙂

Does that "taking a look at this soon" finally happen since your last comment ? Would be interested in the follow-up, since we might encounter that exact case on our end too 😉

@swift-ci
Copy link
Collaborator

swift-ci commented Dec 4, 2018

Comment by Jason Bobier (JIRA)

I just ran into this as well with:

class MyClass {
  enum Error: Swift.Error {
    case someError
  }
}

class SubClass: MyClass {
  enum Error: Swift.Error {
    case anotherError
  }
}

let error: Swift.Error = SubClass.Error.anotherError
let otherError = error as? SubClass.Error

and yet this works:

let error: Swift.Error = MyClass.Error.someError
let otherError = error as? MyClass.Error

@slavapestov
Copy link
Member

#27365

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

No branches or pull requests

7 participants