Skip to content

Commit 33cff97

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
1 parent d4598ec commit 33cff97

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
@@ -216,23 +216,36 @@ Type TypeResolution::resolveDependentMemberType(
216216
return DependentMemberType::get(baseTy, assocType);
217217
}
218218

219-
// Otherwise, the nested type comes from a concrete type. Substitute the
220-
// base type into it.
219+
// Otherwise, the nested type comes from a concrete type,
220+
// or it's a typealias declared in protocol or protocol extension.
221+
// Substitute the base type into it.
221222
auto concrete = ref->getBoundDecl();
222223
auto lazyResolver = ctx.getLazyResolver();
223224
if (lazyResolver)
224225
lazyResolver->resolveDeclSignature(concrete);
225226
if (!concrete->hasInterfaceType())
226227
return ErrorType::get(ctx);
227228

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

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)