Skip to content
Closed
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
35 changes: 33 additions & 2 deletions lib/IRGen/Fulfillment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,13 +374,44 @@ bool FulfillmentMap::searchNominalTypeMetadata(IRGenModule &IGM,

bool hadFulfillment = false;

auto subs = type->getContextSubstitutionMap();
std::optional<SubstitutionMap> subs = type->getContextSubstitutionMap();

// FIXME: Today, the type is either a contextual type for a generic
// environment (in which case the substitution map's replacement types
// do not contain any type parameters), or it is *exactly* the declared
// interface type of its nominal type declaration (in which case we can
// skip the substitution and use the original type parameter as the
// lookup key in our fulfillment map).
//
// If this invariant no longer holds in the future and we legitimately
// end up here with a substitution map whose replacement types also
// contain type parameters, please do not comment out the assertion,
// because if it fails to hold, the lookup below will no longer work.
// Indeed, types that contain type parameters might be equivalent with
// respect to a generic signature, but not equal as canonical types.
//
// The correct fix in that case would be to plumb through the generic
// signature that describes the substitution map's replacement types
// (*not* the substitution map's input generic signature), so that we can
// reduce the result of the call to subst() below with respect to this
// signature before forming the key.
if (type->hasTypeParameter()) {
ASSERT(subs->isIdentity());
subs = std::nullopt;
}

GenericTypeRequirements requirements(IGM, nominal);

for (unsigned reqtIndex : indices(requirements.getRequirements())) {
auto requirement = requirements.getRequirements()[reqtIndex];
auto arg = requirement.getTypeParameter().subst(subs)->getCanonicalType();
auto arg = requirement.getTypeParameter();
// Only substitute if we have to.
if (subs) {
arg = arg.subst(*subs)->getCanonicalType();
// Otherwise, we cannot guarantee that two equivalent types
// are actually going to be canonically equal.
ASSERT(!arg->hasTypeParameter());
}

// Skip uninteresting type arguments.
if (!keys.hasInterestingType(arg))
Expand Down
40 changes: 40 additions & 0 deletions test/IRGen/fulfillment_map_key_equality.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// RUN: %target-swift-frontend -emit-ir %s -enable-library-evolution | %FileCheck %s

// rdar://160649141

public protocol P1 {}

public protocol P2 {
associatedtype A1
}

public protocol P5 {
associatedtype A2: P2
}

public protocol P3 where A4.A1 == A3.A2.A1 {
associatedtype A3: P5
associatedtype A4: P2

var x: Int { get }
}

public protocol P6: P3 {}

public protocol P4 {
associatedtype A4: P2
}

public struct G1<A1>: P2 {}

public struct G2<A2: P2>: P5 {}

public struct G3<T: P6 & P4>: P3 where T.A4.A1: P1 {
public typealias A4 = G1<T.A3.A2.A1>
public typealias A3 = G2<T.A3.A2>

// Make sure this witness thunk doesn't have any additional bogus parameters.

// CHECK-LABEL: define internal swiftcc i64 @"$s28fulfillment_map_key_equality2G3VyxGAA2P3A2aEP1xSivgTW"(ptr noalias swiftself captures(none) %0, ptr %Self, ptr %SelfWitnessTable) {{.*}} {
public var x: Int { fatalError() }
}