Skip to content

AST: Fix compareDependentTypes() for protocol typealiases #7081

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2926,6 +2926,8 @@ getGenericFunctionRecursiveProperties(Type Input, Type Result) {
RecursiveTypeProperties properties;
if (Result->getRecursiveProperties().hasDynamicSelf())
properties |= RecursiveTypeProperties::HasDynamicSelf;
if (Result->getRecursiveProperties().hasError())
properties |= RecursiveTypeProperties::HasError;
return properties;
}

Expand Down Expand Up @@ -3127,17 +3129,23 @@ SILFunctionType::SILFunctionType(GenericSignature *genericSig, ExtInfo ext,

for (auto param : getParameters()) {
(void)param;
assert(!param.getType()->hasError()
&& "interface type of parameter should not contain error types");
assert(!param.getType()->hasArchetype()
&& "interface type of generic type should not contain context archetypes");
&& "interface type of parameter should not contain context archetypes");
}
for (auto result : getResults()) {
(void)result;
assert(!result.getType()->hasError()
&& "interface type of result should not contain error types");
assert(!result.getType()->hasArchetype()
&& "interface type of generic type should not contain context archetypes");
&& "interface type of result should not contain context archetypes");
}
if (hasErrorResult()) {
assert(!getErrorResult().getType()->hasError()
&& "interface type of result should not contain error types");
assert(!getErrorResult().getType()->hasArchetype()
&& "interface type of generic type should not contain context archetypes");
&& "interface type of result should not contain context archetypes");
}
}
#endif
Expand Down
41 changes: 20 additions & 21 deletions lib/AST/ArchetypeBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,12 @@ static int compareDependentTypes(
if (a->isGenericParam() != b->isGenericParam())
return a->isGenericParam() ? -1 : +1;

// Typealiases must be ordered *after* everything else, to ensure they
// don't become representatives in the case where a typealias is equated
// with an associated type.
if (!!a->getTypeAliasDecl() != !!b->getTypeAliasDecl())
return a->getTypeAliasDecl() ? +1 : -1;

// - Dependent members
auto ppa = a->getParent();
auto ppb = b->getParent();
Expand Down Expand Up @@ -411,27 +417,22 @@ static int compareDependentTypes(
// Make sure typealiases are properly ordered, to avoid crashers.
// FIXME: Ideally we would eliminate typealiases earlier.
if (auto *aa = a->getTypeAliasDecl()) {
if (auto *ab = b->getTypeAliasDecl()) {
// - by protocol, so t_n_m.`P.T` < t_n_m.`Q.T` (given P < Q)
auto protoa =
aa->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
auto protob =
ab->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
auto *ab = b->getTypeAliasDecl();
assert(ab != nullptr && "Should have handled this case above");

if (int compareProtocols
= ProtocolType::compareProtocols(&protoa, &protob))
return compareProtocols;
// - by protocol, so t_n_m.`P.T` < t_n_m.`Q.T` (given P < Q)
auto protoa =
aa->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
auto protob =
ab->getDeclContext()->getAsProtocolOrProtocolExtensionContext();

// FIXME: Arbitrarily break the result here.
if (aa != ab)
return aa < ab ? -1 : +1;
} else {
// A resolved archetype is always ordered before an unresolved one.
return -1;
}
} else if (b->getTypeAliasDecl()) {
// A resolved archetype is always ordered before an unresolved one.
return +1;
if (int compareProtocols
= ProtocolType::compareProtocols(&protoa, &protob))
return compareProtocols;

// FIXME: Arbitrarily break the result here.
if (aa != ab)
return aa < ab ? -1 : +1;
}

// Along the error path where one or both of the potential archetypes was
Expand Down Expand Up @@ -533,8 +534,6 @@ auto ArchetypeBuilder::PotentialArchetype::getNestedType(
continue;

auto type = alias->getDeclaredInterfaceType();
SmallVector<Identifier, 4> identifiers;

if (auto existingPA = builder.resolveArchetype(type)) {
builder.addSameTypeRequirementBetweenArchetypes(pa, existingPA,
redundantSource);
Expand Down
17 changes: 17 additions & 0 deletions test/SILGen/same_type_abstraction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,20 @@ extension MyProtocol where Data == (ReadData, ReadData) {
return (readData(), readData())
}
}

// Problem with protocol typealiases, which are modeled as same-type
// constraints

protocol Refined : Associated {
associatedtype Key
typealias Assoc = Key

init()
}

extension Refined {
// CHECK-LABEL: sil hidden @_T021same_type_abstraction7RefinedPAAEx3KeyQz12withElements_tcfC : $@convention(method) <Self where Self : Refined> (@in Self.Key, @thick Self.Type) -> @out Self
init(withElements newElements: Key) {
self.init()
}
}
14 changes: 14 additions & 0 deletions validation-test/compiler_crashers_2_fixed/0066-sr3687.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// RUN: %target-swift-frontend %s -emit-ir

public protocol QHash : Collection, ExpressibleByArrayLiteral {
associatedtype Key
typealias Element = Key

init()
}

extension QHash {
init(withElements newElements: Key...) {
self.init()
}
}