@@ -171,7 +171,7 @@ extension SSGC.TypeChecker
171171 // which is why it is done after the inheritance relationships are recorded.
172172 for type : SSGC . DeclObject in typesConformed
173173 {
174- try self . comptuteConformances ( of: type, by: id)
174+ try self . computeConformances ( of: type, by: id)
175175 }
176176
177177 // lib/SymbolGraphGen fails to emit a `memberOf` edge if the member is a default
@@ -413,45 +413,75 @@ extension SSGC.TypeChecker
413413}
414414extension SSGC . TypeChecker
415415{
416+ private
417+ func computeConformance( where conditions: Set < Set < GenericConstraint < Symbol . Decl > > > ,
418+ to target: Symbol . Decl ,
419+ of type: SSGC . DeclObject ) throws -> Set < GenericConstraint < Symbol . Decl > >
420+ {
421+ do
422+ {
423+ return try conditions. simplified ( with: self . declarations)
424+ }
425+ catch SSGC . ConstraintReductionError . chimaeric( let reduced, from: let lists)
426+ {
427+ throw AssertionError . init ( message: """
428+ Failed to simplify constraints for conditional conformance \
429+ ( \( target) ) of ' \( type. value. path) ' because multiple conflicting \
430+ conformances unify to a heterogeneous set of constraints
431+
432+ Declared constraints: \( lists)
433+ Simplified constraints: \( reduced)
434+ """ )
435+ }
436+ catch SSGC . ConstraintReductionError . redundant( let reduced, from: let lists)
437+ {
438+ throw AssertionError . init ( message: """
439+ Failed to simplify constraints for conditional conformance \
440+ ( \( target) ) of ' \( type. value. path) ' because at least one of the \
441+ constraint lists had redundancies within itself
442+
443+ Declared constraints: \( lists)
444+ Simplified constraints: \( reduced)
445+ """ )
446+ }
447+ }
416448 private mutating
417- func comptuteConformances ( of type: SSGC . DeclObject , by culture: Symbol . Module ) throws
449+ func computeConformances ( of type: SSGC . DeclObject , by culture: Symbol . Module ) throws
418450 {
419- for (conformance , overlapping) : ( Symbol . Decl , Set < Set < GenericConstraint < Symbol . Decl > > > )
451+ for (target , overlapping) : ( Symbol . Decl , Set < Set < GenericConstraint < Symbol . Decl > > > )
420452 in type. conformanceStatements
421453 {
422- let canonical : Set < GenericConstraint < Symbol . Decl > >
423- do
424- {
425- canonical = try overlapping. simplified ( with: self . declarations)
426- }
427- catch SSGC . ConstraintReductionError . chimaeric( let reduced, from: let lists)
428- {
429- throw AssertionError . init ( message: """
430- Failed to simplify constraints for conditional conformance \
431- ( \( conformance) ) of ' \( type. value. path) ' because multiple conflicting \
432- conformances unify to a heterogeneous set of constraints
454+ try
455+ {
456+ // A Swift type may only conform to a protocol once, even with different
457+ // conditional constraints.
458+ //
459+ // Exiting here not only prevents us from doing unnecessary simplification
460+ // work, but also prevents us from accidentally capturing another module’s
461+ // conformances as our own.
462+ //
463+ // For example: `Foundation` conforms `Array` to `Sequence` where `Element`
464+ // is `UInt8`. Because the constraints are different (and tighter) than the
465+ // original conformance, our regular de-duplication logic would not flag this
466+ // as a duplicate were it not for this guard.
467+ guard case nil = $0
468+ else
469+ {
470+ return
471+ }
433472
434- Declared constraints: \( lists)
435- Simplified constraints: \( reduced)
436- """ )
437- }
438- catch SSGC . ConstraintReductionError . redundant( let reduced, from: let lists)
439- {
440- throw AssertionError . init ( message: """
441- Failed to simplify constraints for conditional conformance \
442- ( \( conformance) ) of ' \( type. value. path) ' because at least one of the \
443- constraint lists had redundancies within itself
473+ let canonical : Set < GenericConstraint < Symbol . Decl > > = try self . computeConformance (
474+ where: overlapping,
475+ to: target,
476+ of: type)
444477
445- Declared constraints: \( lists)
446- Simplified constraints: \( reduced)
447- """ )
448- }
478+ // Generate an implicit, internal extension for this conformance,
479+ // if one does not already exist.
480+ self . extensions [ extending: type, where: canonical] . add ( conformance: target,
481+ by: culture)
482+ $0 = canonical
449483
450- type. conformances [ conformance] = canonical
451- // Generate an implicit, internal extension for this conformance,
452- // if one does not already exist.
453- self . extensions [ extending: type, where: canonical] . add ( conformance: conformance,
454- by: culture)
484+ } ( & type. conformances [ target] )
455485 }
456486
457487 // We don’t need this table anymore.
0 commit comments