Skip to content

Commit 5862883

Browse files
committed
Sema: Clean up resolveTypeInContext() and generalize typealiases in protocols
Previously a typealias in a protocol either had to be concrete, or consist of a single path of member types from Self, eg Self.A.B. Lift this restriction. Fix a few other corner cases that came up in the validation suite, and clean up the function in general.
1 parent 02eb552 commit 5862883

14 files changed

+120
-157
lines changed

lib/AST/Type.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2917,15 +2917,8 @@ TypeSubstitutionMap TypeBase::getMemberSubstitutions(const DeclContext *dc) {
29172917
// If the member is part of a protocol or extension thereof, we need
29182918
// to substitute in the type of Self.
29192919
if (dc->getAsProtocolOrProtocolExtensionContext()) {
2920-
// We only substitute into archetypes for now for protocols.
2921-
// FIXME: This seems like an odd restriction. Whatever is depending on
2922-
// this, shouldn't.
2923-
if (!baseTy->is<ArchetypeType>() && isa<ProtocolDecl>(dc))
2924-
return substitutions;
2925-
29262920
// FIXME: This feels painfully inefficient. We're creating a dense map
29272921
// for a single substitution.
2928-
substitutions[dc->getProtocolSelf()->getArchetype()] = baseTy;
29292922
substitutions[dc->getProtocolSelf()->getDeclaredType()
29302923
->getCanonicalType()->castTo<GenericTypeParamType>()]
29312924
= baseTy;

lib/Sema/TypeCheckType.cpp

Lines changed: 100 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -193,23 +193,6 @@ void TypeChecker::forceExternalDeclMembers(NominalTypeDecl *nominalDecl) {
193193
}
194194
}
195195

196-
static Optional<Type>
197-
resolveAssociatedTypeInContext(TypeChecker &TC, AssociatedTypeDecl *assocType,
198-
DeclContext *DC, GenericTypeResolver *resolver) {
199-
auto protoSelf = DC->getProtocolSelf();
200-
auto selfTy = protoSelf->getDeclaredType()->castTo<GenericTypeParamType>();
201-
auto baseTy = resolver->resolveGenericTypeParamType(selfTy);
202-
203-
if (baseTy->isTypeParameter())
204-
return resolver->resolveSelfAssociatedType(baseTy, DC, assocType);
205-
206-
if (assocType->getDeclContext() != DC)
207-
return TC.substMemberTypeWithBase(DC->getParentModule(), assocType,
208-
protoSelf->getArchetype(),
209-
/*isTypeReference=*/true);
210-
return None;
211-
}
212-
213196
Type TypeChecker::resolveTypeInContext(
214197
TypeDecl *typeDecl,
215198
DeclContext *fromDC,
@@ -239,15 +222,12 @@ Type TypeChecker::resolveTypeInContext(
239222
nominalType = nullptr;
240223
}
241224

242-
// Walk up through the type scopes to find the context where the type
243-
// declaration was found. When we find it, substitute the appropriate base
244-
// type.
225+
// Walk up through the type scopes to find the context containing the type
226+
// being resolved.
245227
auto ownerDC = typeDecl->getDeclContext();
246228
bool nonTypeOwner = !ownerDC->isTypeContext();
247229
auto ownerNominal = ownerDC->getAsNominalTypeOrNominalTypeExtensionContext();
248230
auto assocType = dyn_cast<AssociatedTypeDecl>(typeDecl);
249-
auto alias = dyn_cast<TypeAliasDecl>(typeDecl);
250-
DeclContext *typeParent = nullptr;
251231
assert((ownerNominal || nonTypeOwner) &&
252232
"Owner must be a nominal type or a non type context");
253233

@@ -273,140 +253,126 @@ Type TypeChecker::resolveTypeInContext(
273253
return typeDecl->getDeclaredType();
274254

275255
// For the next steps we need our parentDC to be a type context
276-
if (!parentDC->isTypeContext()) {
256+
if (!parentDC->isTypeContext())
277257
continue;
278-
} else if (!typeParent) {
279-
// Remember the first type decl context in the hierarchy for later use
280-
typeParent = parentDC;
281-
}
282-
283-
// If we found an associated type in an inherited protocol, the base for our
284-
// reference to this associated type is our own `Self`. If we can't resolve
285-
// the associated type during this iteration, try again on the next.
286-
if (assocType) {
287-
if (auto proto = parentDC->getAsProtocolOrProtocolExtensionContext()) {
288-
auto assocProto = assocType->getProtocol();
289-
if (proto == assocProto || proto->inheritsFrom(assocProto)) {
290-
// If the associated type is from our own protocol or we inherit from
291-
// the associated type's protocol, resolve it
292-
if (auto resolved = resolveAssociatedTypeInContext(
293-
*this, assocType, parentDC, resolver))
294-
return *resolved;
295-
296-
} else if (auto ED = dyn_cast<ExtensionDecl>(parentDC)) {
297-
// Otherwise, if we are in an extension there might be other
298-
// associated types brought into the context through
299-
// `extension ... where Self : SomeProtocol`
300-
for (auto req : ED->getGenericParams()->getTrailingRequirements()) {
301-
// Reject requirements other than constraints with an subject other
302-
// than `Self`
303-
if (req.getKind() != RequirementReprKind::TypeConstraint ||
304-
!req.getSubject()->castTo<ArchetypeType>()->isSelfDerived())
258+
259+
// Search the type of this context and its supertypes (if its a
260+
// class) or refined protocols (if its a protocol).
261+
llvm::SmallPtrSet<const NominalTypeDecl *, 8> visited;
262+
llvm::SmallVector<Type, 8> stack;
263+
264+
// Start with the type of the current context.
265+
if (auto fromType = resolver->resolveTypeOfContext(parentDC))
266+
stack.push_back(fromType);
267+
268+
// If we are in a protocol extension there might be other type aliases and
269+
// nominal types brought into the context through requirements on Self,
270+
// for example:
271+
//
272+
// extension MyProtocol where Self : YourProtocol { ... }
273+
if (parentDC->getAsProtocolExtensionContext()) {
274+
auto ED = cast<ExtensionDecl>(parentDC);
275+
if (auto genericParams = ED->getGenericParams()) {
276+
for (auto req : genericParams->getTrailingRequirements()) {
277+
// We might be resolving 'req.getSubject()' itself.
278+
// This whole case feels like a hack -- there should be a
279+
// more principled way to represent extensions of protocol
280+
// compositions.
281+
if (req.getKind() == RequirementReprKind::TypeConstraint) {
282+
if (!req.getSubject() ||
283+
!req.getSubject()->is<ArchetypeType>() ||
284+
!req.getSubject()->castTo<ArchetypeType>()->getSelfProtocol())
305285
continue;
306286

307-
// If the associated type is defined in the same protocol which is
308-
// required for this extension, or if the required protocol inherits
309-
// from the protocol the associated type is declared in, we can
310-
// resolve the associated type with our `Self` as the reference
311-
// point.
312-
auto reqProto =
313-
req.getConstraint()->castTo<ProtocolType>()->getDecl();
314-
if (reqProto == assocProto || reqProto->inheritsFrom(assocProto)) {
315-
if (auto resolved = resolveAssociatedTypeInContext(
316-
*this, assocType, parentDC, resolver))
317-
return *resolved;
318-
break;
319-
}
287+
stack.push_back(req.getConstraint());
320288
}
321289
}
322290
}
323291
}
324-
325-
// If we found an alias type in an inherited protocol, resolve it based on our
326-
// own `Self`.
327-
if (alias && alias->hasInterfaceType()) {
328-
auto metaType = alias->getInterfaceType()->getAs<MetatypeType>();
329-
auto memberType = metaType ? metaType->getInstanceType()->getAs<DependentMemberType>() :
330-
nullptr;
331-
332-
if (memberType && parentDC->getAsProtocolOrProtocolExtensionContext()) {
333-
auto protoSelf = parentDC->getProtocolSelf();
334-
auto selfTy = protoSelf->getDeclaredType()->castTo<GenericTypeParamType>();
335-
auto baseTy = resolver->resolveGenericTypeParamType(selfTy);
336-
337-
SmallVector<DependentMemberType *, 4> memberTypes;
338-
do {
339-
memberTypes.push_back(memberType);
340-
memberType = memberType->getBase()->getAs<DependentMemberType>();
341-
} while (memberType);
342-
343-
auto module = parentDC->getParentModule();
344-
while (memberTypes.size()) {
345-
baseTy = memberTypes.back()->substBaseType(module, baseTy, nullptr);
346-
memberTypes.pop_back();
347-
}
348-
return baseTy;
349-
}
350-
}
351292

352-
// Search the type of this context and its supertypes.
353-
llvm::SmallPtrSet<const NominalTypeDecl *, 8> visited;
354-
for (auto fromType = resolver->resolveTypeOfContext(parentDC);
355-
fromType;
356-
fromType = getSuperClassOf(fromType)) {
293+
while (!stack.empty()) {
294+
auto fromType = stack.back();
295+
auto *fromProto = parentDC->getAsProtocolOrProtocolExtensionContext();
296+
297+
stack.pop_back();
298+
357299
// If we hit circularity, we will diagnose at some point in typeCheckDecl().
358300
// However we have to explicitly guard against that here because we get
359301
// called as part of validateDecl().
360302
if (!visited.insert(fromType->getAnyNominal()).second)
361-
break;
303+
continue;
304+
305+
// Handle this case:
306+
// - Current context: concrete type
307+
// - Nested type: associated type
308+
// - Nested type's context: protocol or protocol extension
309+
//
310+
if (assocType && fromProto == nullptr) {
311+
ProtocolConformance *conformance = nullptr;
312+
313+
// If the conformance check failed, the associated type is for a
314+
// conformance of an outer context.
315+
if (!options.contains(TR_InheritanceClause) &&
316+
conformsToProtocol(fromType,
317+
cast<ProtocolDecl>(ownerNominal),
318+
parentDC, ConformanceCheckFlags::Used,
319+
&conformance) &&
320+
conformance) {
321+
return conformance->getTypeWitness(assocType, this).getReplacement();
322+
}
323+
}
362324

363-
// If the nominal type declaration of the context type we're looking at
364-
// matches the owner's nominal type declaration, this is how we found
365-
// the member type declaration. Substitute the type we're coming from as
366-
// the base of the member type to produce the projected type result.
325+
// Handle these cases:
326+
// - Current context: concrete type
327+
// - Nested type: concrete type or type alias
328+
// - Nested type's context: concrete type
329+
//
330+
// - Current context: protocol or protocol extension
331+
// - Nested type: type alias
332+
// - Nested type's context: protocol or protocol extension
333+
//
334+
// Note: this is not supported yet, FIXME:
335+
// - Current context: concrete type
336+
// - Nested type: type alias
337+
// - Nested type's context: protocol or protocol extension
338+
//
367339
if (fromType->getAnyNominal() == ownerNominal) {
368-
// If we are referring into a protocol or extension thereof,
369-
// the base type is the 'Self'.
370-
if (ownerDC->getAsProtocolOrProtocolExtensionContext()) {
371-
auto selfTy = ownerDC->getProtocolSelf()->getDeclaredType()
372-
->castTo<GenericTypeParamType>();
373-
fromType = resolver->resolveGenericTypeParamType(selfTy);
340+
if (fromProto &&
341+
ownerNominal->getAsProtocolOrProtocolExtensionContext()) {
342+
// If we are looking up an associated type or a protocol's type alias
343+
// from a protocol or protocol extension, use the archetype for 'Self'
344+
// instead of the existential type.
345+
assert(fromType->is<ProtocolType>());
346+
347+
auto protoSelf = parentDC->getProtocolSelf();
348+
auto selfType = protoSelf
349+
->getDeclaredType()
350+
->castTo<GenericTypeParamType>();
351+
fromType = resolver->resolveGenericTypeParamType(selfType);
352+
353+
if (assocType) {
354+
// Odd special case, ask Doug to explain it over pizza one day
355+
if (fromType->isTypeParameter())
356+
return resolver->resolveSelfAssociatedType(
357+
fromType, parentDC, assocType);
358+
}
374359
}
375360

376-
// Perform the substitution.
377361
return substMemberTypeWithBase(parentDC->getParentModule(), typeDecl,
378362
fromType, /*isTypeReference=*/true);
379363
}
380364

381-
ProtocolConformance *conformance = nullptr;
382-
if (assocType &&
383-
!options.contains(TR_InheritanceClause) &&
384-
conformsToProtocol(fromType,
385-
cast<ProtocolDecl>(assocType->getDeclContext()),
386-
parentDC, ConformanceCheckFlags::Used,
387-
&conformance) &&
388-
conformance) {
389-
return conformance->getTypeWitness(assocType, this).getReplacement();
365+
if (auto superclassTy = getSuperClassOf(fromType))
366+
stack.push_back(superclassTy);
367+
else if (auto protoTy = fromType->getAs<ProtocolType>()) {
368+
for (auto *proto : protoTy->getDecl()->getInheritedProtocols(this))
369+
if (auto refinedTy = proto->getDeclaredTypeInContext())
370+
stack.push_back(refinedTy);
390371
}
391372
}
392373
}
393374

394-
// At this point by iterating through the decl context hierarchy we should
395-
// have encountered the first type context in the stack.
396-
assert(typeParent && "incomplete iteration");
397-
assert(!typeParent->isModuleContext());
398-
399-
// Substitute in the appropriate type for 'Self'.
400-
// FIXME: We shouldn't have to guess here; the caller should tell us.
401-
Type fromType;
402-
if (typeParent->getAsProtocolOrProtocolExtensionContext())
403-
fromType = typeParent->getProtocolSelf()->getArchetype();
404-
else
405-
fromType = resolver->resolveTypeOfContext(typeParent);
406-
407-
// Perform the substitution.
408-
return substMemberTypeWithBase(typeParent->getParentModule(), typeDecl,
409-
fromType, /*isTypeReference=*/true);
375+
llvm_unreachable("Cannot resolve type");
410376
}
411377

412378
Type TypeChecker::applyGenericArguments(Type type, SourceLoc loc,
@@ -537,7 +503,8 @@ Type TypeChecker::applyUnboundGenericArguments(
537503

538504
// Check the generic arguments against the generic signature.
539505
auto genericSig = unbound->getDecl()->getGenericSignature();
540-
if (unbound->getDecl()->isValidatingGenericSignature()) {
506+
if (!unbound->getDecl()->hasType() ||
507+
unbound->getDecl()->isValidatingGenericSignature()) {
541508
diagnose(loc, diag::recursive_requirement_reference);
542509
return nullptr;
543510
}
@@ -2474,7 +2441,6 @@ Type TypeChecker::substMemberTypeWithBase(Module *module,
24742441
Type memberType = isTypeReference
24752442
? cast<TypeDecl>(member)->getDeclaredInterfaceType()
24762443
: member->getInterfaceType();
2477-
24782444
if (isTypeReference) {
24792445
// The declared interface type for a generic type will have the type
24802446
// arguments; strip them off.

test/decl/typealias/typealias.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,12 @@ protocol Col {
158158
protocol CB {
159159
associatedtype C : Col
160160
typealias E = C.Elem
161+
typealias OE = C.Elem?
162+
typealias FE = (C) -> (C.Elem) -> Self
161163

162164
func setIt(_ element: E)
165+
func setItMaybe(_ element: OE)
166+
func teleport(_ fn: FE)
163167
}
164168

165169
func go1<T : CB, U : Col where U.Elem == T.E>(_ col: U, builder: T) { // OK

validation-test/IDE/crashers/021-swift-typechecker-resolveidentifiertype.swift

Lines changed: 0 additions & 2 deletions
This file was deleted.

validation-test/IDE/crashers/024-swift-archetypebuilder-potentialarchetype-getrepresentative.swift

Lines changed: 0 additions & 4 deletions
This file was deleted.

validation-test/IDE/crashers/044-swift-dependentgenerictyperesolver-resolveselfassociatedtype.swift

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// RUN: %target-swift-ide-test -code-completion -code-completion-token=A -source-filename=%s
2+
{class T{protocol b{#^A^#associatedtype b:B<T>struct B<b
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// RUN: %target-swift-ide-test -code-completion -code-completion-token=A -source-filename=%s
2+
protocol c{func a:P
3+
protocol P{#^A^#func a:b
4+
associatedtype b:a
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// RUN: %target-swift-ide-test -code-completion -code-completion-token=A -source-filename=%s
2+
protocol A{protocol A{
3+
associatedtype e:a
4+
func a<T:e
5+
enum b{func a{#^A^#

validation-test/compiler_crashers/01766-swift-typechecker-validatedecl.swift renamed to validation-test/compiler_crashers_fixed/01766-swift-typechecker-validatedecl.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// See http://swift.org/LICENSE.txt for license information
66
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
77

8-
// RUN: not --crash %target-swift-frontend %s -parse
8+
// RUN: not %target-swift-frontend %s -parse
99
protocol a {
1010
protocol A {
1111
}

validation-test/compiler_crashers/26083-std-function-func-swift-type-subst.swift renamed to validation-test/compiler_crashers_fixed/26083-std-function-func-swift-type-subst.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
77

88
// DUPLICATE-OF: 01766-swift-typechecker-validatedecl.swift
9-
// RUN: not --crash %target-swift-frontend %s -parse
9+
// RUN: not %target-swift-frontend %s -parse
1010
protocol e:A{protocol e:A class A}protocol A{enum A}protocol B:e

validation-test/compiler_crashers/27156-swift-typechecker-applygenericarguments.swift renamed to validation-test/compiler_crashers_fixed/27156-swift-typechecker-applygenericarguments.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// See http://swift.org/LICENSE.txt for license information
66
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
77

8-
// RUN: not --crash %target-swift-frontend %s -parse
8+
// RUN: not %target-swift-frontend %s -parse
99
struct B<T{protocol A{
1010
class B<T
1111
associatedtype e:B<T>

validation-test/compiler_crashers/28193-swift-typechecker-lookupmembertype.swift renamed to validation-test/compiler_crashers_fixed/28193-swift-typechecker-lookupmembertype.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
// See http://swift.org/LICENSE.txt for license information
66
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
77

8-
// RUN: not --crash %target-swift-frontend %s -parse
8+
// RUN: not %target-swift-frontend %s -parse
99
// REQUIRES: asserts
1010
var d:Collection{for c d in

validation-test/compiler_crashers/28233-swift-typebase-getmembersubstitutions.swift renamed to validation-test/compiler_crashers_fixed/28233-swift-typebase-getmembersubstitutions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
77

88
// DUPLICATE-OF: 01766-swift-typechecker-validatedecl.swift
9-
// RUN: not --crash %target-swift-frontend %s -parse
9+
// RUN: not %target-swift-frontend %s -parse
1010
protocol A{class A}protocol a:A{protocol P{associatedtype e:A}}a

0 commit comments

Comments
 (0)