Merge pull request #22008 from xedin/rdar-47334176
[Sema] Fix `resolveDependentMemberType` to properly handle nested typ…
diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp
index 7053be5..2572480 100644
--- a/lib/Sema/TypeCheckType.cpp
+++ b/lib/Sema/TypeCheckType.cpp
@@ -216,8 +216,9 @@
return DependentMemberType::get(baseTy, assocType);
}
- // Otherwise, the nested type comes from a concrete type. Substitute the
- // base type into it.
+ // Otherwise, the nested type comes from a concrete type,
+ // or it's a typealias declared in protocol or protocol extension.
+ // Substitute the base type into it.
auto concrete = ref->getBoundDecl();
auto lazyResolver = ctx.getLazyResolver();
if (lazyResolver)
@@ -225,14 +226,26 @@
if (!concrete->hasInterfaceType())
return ErrorType::get(ctx);
- if (concrete->getDeclContext()->getSelfClassDecl()) {
- // We found a member of a class from a protocol or protocol
- // extension.
- //
- // Get the superclass of the 'Self' type parameter.
- baseTy = (baseEquivClass->concreteType
- ? baseEquivClass->concreteType
- : baseEquivClass->superclass);
+ // Make sure that base type didn't get replaced along the way.
+ assert(baseTy->isTypeParameter());
+
+ // There are two situations possible here:
+ //
+ // 1. Member comes from the protocol, which means that it has been
+ // found through a conformance constraint placed on base e.g. `T: P`.
+ // In this case member is a `typealias` declaration located in
+ // protocol or protocol extension.
+ //
+ // 2. Member comes from struct/enum/class type, which means that it
+ // has been found through same-type constraint on base e.g. `T == Q`.
+ //
+ // If this is situation #2 we need to make sure to switch base to
+ // a concrete type (according to equivalence class) otherwise we'd
+ // end up using incorrect generic signature while attempting to form
+ // a substituted type for the member we found.
+ if (!concrete->getDeclContext()->getSelfProtocolDecl()) {
+ baseTy = baseEquivClass->concreteType ? baseEquivClass->concreteType
+ : baseEquivClass->superclass;
assert(baseTy);
}
diff --git a/validation-test/Sema/Inputs/rdar47334176_types.swift b/validation-test/Sema/Inputs/rdar47334176_types.swift
new file mode 100644
index 0000000..f5266f1
--- /dev/null
+++ b/validation-test/Sema/Inputs/rdar47334176_types.swift
@@ -0,0 +1,10 @@
+public protocol P : class {
+ associatedtype V
+}
+
+public protocol R {
+ associatedtype V
+}
+
+public enum E<V> : R {}
+public class C<V> : R {}
diff --git a/validation-test/Sema/rdar47334176.swift b/validation-test/Sema/rdar47334176.swift
new file mode 100644
index 0000000..cca1c66
--- /dev/null
+++ b/validation-test/Sema/rdar47334176.swift
@@ -0,0 +1,13 @@
+// RUN: %empty-directory(%t)
+// RUN: %target-swift-frontend -emit-module -o %t/rdar47334176_types.swiftmodule %S/Inputs/rdar47334176_types.swift
+// RUN: %target-swift-frontend -I %t -typecheck %s
+
+import rdar47334176_types
+
+// To test all possibilities let's declare one of the types
+// in the same module as function declaration which uses it.
+struct S<V> : R {}
+
+func foo<T : P, U>(_: T?, _: (T.V.V) -> Void) where T.V == E<U> {} // Ok
+func bar<T : P, U>(_: T?, _: (T.V.V) -> Void) where T.V == S<U> {} // Ok
+func baz<T : P, U>(_: T?, _: (T.V.V) -> Void) where T.V == C<U> {} // Ok