Skip to content

Commit 4834047

Browse files
committed
this logic was incorrect, it didn’t handle the case of modules that refine conformances declared by other modules
1 parent 07b662b commit 4834047

File tree

1 file changed

+63
-33
lines changed

1 file changed

+63
-33
lines changed

Sources/SymbolGraphCompiler/SSGC.TypeChecker.swift

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}
414414
extension 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

Comments
 (0)