[SR-3492] Unexpected inheritance of nested types Created: 26 Dec 2016  Updated: 6 Jan 2017

Status: Open
Project: Swift
Component/s: Compiler

Type: Bug Priority: Medium
Reporter: Marcin Krzyzanowski Assignee: Slava Pestov
Resolution: Unresolved Votes: 3
Labels: None
Environment:

Swift 3.0.2



 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???


 Comments   
Comment by Robert Widmann [ 1 Jan 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.

Comment by Jacob Bandes-Storch [ 1 Jan 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?

Comment by Marcin Krzyzanowski [ 1 Jan 2017 ]

That's…not how I'd expect it to behave, I think Joe Groff 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()'
Comment by Robert Widmann [ 1 Jan 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`. Slava Pestov Do you foresee any problems with that?

Comment by Slava Pestov [ 1 Jan 2017 ]

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.

Comment by Jordan Rose [ 5 Jan 2017 ]

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

Comment by Slava Pestov [ 6 Jan 2017 ]

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

Generated at Wed Feb 21 08:43:37 CST 2018 using JIRA 7.3.4#73015-sha1:a262b3457b3605f12635df4b0a0c3dc71d631a1e.