diff --git a/lib/IRGen/Fulfillment.cpp b/lib/IRGen/Fulfillment.cpp index 13cce27cc03a..feb9547a3111 100644 --- a/lib/IRGen/Fulfillment.cpp +++ b/lib/IRGen/Fulfillment.cpp @@ -374,13 +374,44 @@ bool FulfillmentMap::searchNominalTypeMetadata(IRGenModule &IGM, bool hadFulfillment = false; - auto subs = type->getContextSubstitutionMap(); + std::optional 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)) diff --git a/test/IRGen/fulfillment_map_key_equality.swift b/test/IRGen/fulfillment_map_key_equality.swift new file mode 100644 index 000000000000..88c68ccb85a1 --- /dev/null +++ b/test/IRGen/fulfillment_map_key_equality.swift @@ -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: P2 {} + +public struct G2: P5 {} + +public struct G3: P3 where T.A4.A1: P1 { + public typealias A4 = G1 + public typealias A3 = G2 + + // 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() } +}