Skip to content

Commit c609993

Browse files
committed
[Sema] Fix resolveDependentMemberType to properly handle nested types found in enum/struct/class
Currently if member has been found through same-type constraint it would only be properly handled when it comes from a class type, because that's the only time when base type gets replaced with "concrete" type from equivalence class, but nested types could also come from structs, enums and sometimes protocols (e.g. typealias) which `resolveDependentMemberType` has to handle. Resolves: rdar://problem/47334176 (cherry picked from commit 33cff97)
1 parent 3999fb3 commit c609993

File tree

3 files changed

+46
-10
lines changed

3 files changed

+46
-10
lines changed

lib/Sema/TypeCheckType.cpp

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -212,23 +212,36 @@ Type TypeResolution::resolveDependentMemberType(
212212
return DependentMemberType::get(baseTy, assocType);
213213
}
214214

215-
// Otherwise, the nested type comes from a concrete type. Substitute the
216-
// base type into it.
215+
// Otherwise, the nested type comes from a concrete type,
216+
// or it's a typealias declared in protocol or protocol extension.
217+
// Substitute the base type into it.
217218
auto concrete = ref->getBoundDecl();
218219
auto lazyResolver = ctx.getLazyResolver();
219220
if (lazyResolver)
220221
lazyResolver->resolveDeclSignature(concrete);
221222
if (!concrete->hasInterfaceType())
222223
return ErrorType::get(ctx);
223224

224-
if (concrete->getDeclContext()->getSelfClassDecl()) {
225-
// We found a member of a class from a protocol or protocol
226-
// extension.
227-
//
228-
// Get the superclass of the 'Self' type parameter.
229-
baseTy = (baseEquivClass->concreteType
230-
? baseEquivClass->concreteType
231-
: baseEquivClass->superclass);
225+
// Make sure that base type didn't get replaced along the way.
226+
assert(baseTy->isTypeParameter());
227+
228+
// There are two situations possible here:
229+
//
230+
// 1. Member comes from the protocol, which means that it has been
231+
// found through a conformance constraint placed on base e.g. `T: P`.
232+
// In this case member is a `typealias` declaration located in
233+
// protocol or protocol extension.
234+
//
235+
// 2. Member comes from struct/enum/class type, which means that it
236+
// has been found through same-type constraint on base e.g. `T == Q`.
237+
//
238+
// If this is situation #2 we need to make sure to switch base to
239+
// a concrete type (according to equivalence class) otherwise we'd
240+
// end up using incorrect generic signature while attempting to form
241+
// a substituted type for the member we found.
242+
if (!concrete->getDeclContext()->getSelfProtocolDecl()) {
243+
baseTy = baseEquivClass->concreteType ? baseEquivClass->concreteType
244+
: baseEquivClass->superclass;
232245
assert(baseTy);
233246
}
234247

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
public protocol P : class {
2+
associatedtype V
3+
}
4+
5+
public protocol R {
6+
associatedtype V
7+
}
8+
9+
public enum E<V> : R {}
10+
public class C<V> : R {}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -o %t/rdar47334176_types.swiftmodule %S/Inputs/rdar47334176_types.swift
3+
// RUN: %target-swift-frontend -I %t -typecheck %s
4+
5+
import rdar47334176_types
6+
7+
// To test all possibilities let's declare one of the types
8+
// in the same module as function declaration which uses it.
9+
struct S<V> : R {}
10+
11+
func foo<T : P, U>(_: T?, _: (T.V.V) -> Void) where T.V == E<U> {} // Ok
12+
func bar<T : P, U>(_: T?, _: (T.V.V) -> Void) where T.V == S<U> {} // Ok
13+
func baz<T : P, U>(_: T?, _: (T.V.V) -> Void) where T.V == C<U> {} // Ok

0 commit comments

Comments
 (0)