Skip to content

Commit 07b662b

Browse files
committed
fix failure to deduplicate protocol conformances
1 parent 8599c5b commit 07b662b

File tree

4 files changed

+101
-84
lines changed

4 files changed

+101
-84
lines changed

Sources/SymbolGraphCompiler/Declarations/SSGC.DeclObject.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ extension SSGC
2727
///
2828
/// In the example above, the second conformance likely originated from a
2929
/// `Foo<T>:Hashable where T:Hashable` conformance.
30-
var conformances:[Symbol.Decl: Set<Set<GenericConstraint<Symbol.Decl>>>]
30+
var conformanceStatements:[Symbol.Decl: Set<Set<GenericConstraint<Symbol.Decl>>>]
31+
var conformances:[Symbol.Decl: Set<GenericConstraint<Symbol.Decl>>]
3132

3233
/// The type of the superforms tracked by ``\.value.superforms``.
3334
var superforms:(any SuperformRelationship.Type)?
@@ -54,6 +55,7 @@ extension SSGC
5455
self.culture = culture
5556
self.access = access
5657

58+
self.conformanceStatements = [:]
5759
self.conformances = [:]
5860
self.superforms = nil
5961
self.scopes = []
@@ -62,6 +64,14 @@ extension SSGC
6264
}
6365
}
6466
}
67+
extension SSGC.DeclObject:Equatable
68+
{
69+
static func == (a:SSGC.DeclObject, b:SSGC.DeclObject) -> Bool { a === b }
70+
}
71+
extension SSGC.DeclObject:Hashable
72+
{
73+
func hash(into hasher:inout Hasher) { hasher.combine(ObjectIdentifier.init(self)) }
74+
}
6575
extension SSGC.DeclObject
6676
{
6777
var id:Symbol.Decl

Sources/SymbolGraphCompiler/SSGC.TypeChecker.swift

Lines changed: 88 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -138,26 +138,42 @@ extension SSGC.TypeChecker
138138
}
139139

140140
// Pass II. Populate protocol conformance tables and nesting relationships.
141-
for part:SSGC.SymbolDump in culture.symbols
141+
let typesConformed:Set<SSGC.DeclObject> = try culture.symbols.reduce(into: [])
142142
{
143-
for conformance:Symbol.ConformanceRelationship in part.conformances
143+
for conformance:Symbol.ConformanceRelationship in $1.conformances
144144
{
145-
try conformance.do { try self.insert($0, by: id) }
145+
let typeConformed:SSGC.DeclObject? = try conformance.do
146+
{
147+
try self.collectConformance($0, by: id)
148+
}
149+
if let typeConformed:SSGC.DeclObject
150+
{
151+
$0.insert(typeConformed)
152+
}
146153
}
147-
for inheritance:Symbol.InheritanceRelationship in part.inheritances
154+
155+
for inheritance:Symbol.InheritanceRelationship in $1.inheritances
148156
{
149157
try inheritance.do { try self.insert($0, by: id) }
150158
}
151159

152-
for nesting:Symbol.RequirementRelationship in part.requirements
160+
for nesting:Symbol.RequirementRelationship in $1.requirements
153161
{
154162
try nesting.do { try self.assign($0) }
155163
}
156-
for nesting:Symbol.MemberRelationship in part.memberships
164+
for nesting:Symbol.MemberRelationship in $1.memberships
157165
{
158166
try nesting.do { try self.assign($0, by: id) }
159167
}
160168
}
169+
170+
// This uses information about the superforms of a type to simplify the constraints,
171+
// which is why it is done after the inheritance relationships are recorded.
172+
for type:SSGC.DeclObject in typesConformed
173+
{
174+
try self.comptuteConformances(of: type, by: id)
175+
}
176+
161177
// lib/SymbolGraphGen fails to emit a `memberOf` edge if the member is a default
162178
// implementation of a protocol requirement. Because the requirement might be a
163179
// requirement from a different protocol than the protocol containing the default
@@ -306,19 +322,18 @@ extension SSGC.TypeChecker
306322
extension SSGC.TypeChecker
307323
{
308324
private mutating
309-
func insert(_ conformance:Symbol.ConformanceRelationship, by culture:Symbol.Module) throws
325+
func collectConformance(_ conformance:Symbol.ConformanceRelationship,
326+
by culture:Symbol.Module) throws -> SSGC.DeclObject?
310327
{
311328
guard
312329
let target:SSGC.DeclObject = self.declarations[visible: conformance.target]
313330
else
314331
{
315-
return
332+
return nil
316333
}
317334

318335
let conditions:Set<GenericConstraint<Symbol.Decl>> = .init(conformance.conditions)
319-
320-
let typeExtension:SSGC.ExtensionObject
321-
let typeConformed:SSGC.DeclObject
336+
let conformer:SSGC.DeclObject
322337

323338
switch conformance.source
324339
{
@@ -331,7 +346,7 @@ extension SSGC.TypeChecker
331346
let type:SSGC.DeclObject = self.declarations[visible: symbol]
332347
else
333348
{
334-
return
349+
return nil
335350
}
336351

337352
if let origin:Symbol.Decl = conformance.origin
@@ -346,35 +361,33 @@ extension SSGC.TypeChecker
346361
try type.add(superform: Symbol.InheritanceRelationship.init(
347362
by: type.id,
348363
of: target.id))
349-
return
364+
return nil
350365
}
351-
// Generate an implicit, internal extension for this conformance,
352-
// if one does not already exist.
353-
typeExtension = self.extensions[extending: type, where: conditions]
354-
typeConformed = type
366+
367+
conformer = type
355368

356369
case .block(let symbol):
357370
// Look up the extension associated with this block name.
358-
typeExtension = try self.extensions[named: symbol]
371+
let named:SSGC.ExtensionObject = try self.extensions[named: symbol]
359372

360373
guard
361-
let type:SSGC.DeclObject = self.declarations[visible: typeExtension.extendee]
374+
let type:SSGC.DeclObject = self.declarations[visible: named.extendee]
362375
else
363376
{
364-
return
377+
return nil
365378
}
366379

367-
typeConformed = type
380+
conformer = type
368381

369-
guard typeExtension.conditions == conditions
382+
guard named.conditions == conditions
370383
else
371384
{
372-
throw SSGC.ExtensionSignatureError.init(expected: typeExtension.signature)
385+
throw SSGC.ExtensionSignatureError.init(expected: named.signature)
373386
}
374387
}
375388

376-
typeConformed.conformances[target.id, default: []].insert(conditions)
377-
typeExtension.add(conformance: target.id, by: culture)
389+
conformer.conformanceStatements[target.id, default: []].insert(conditions)
390+
return conformer
378391
}
379392

380393
private mutating
@@ -397,7 +410,56 @@ extension SSGC.TypeChecker
397410
superform.kinks[is: .implemented] = true
398411
}
399412
}
413+
}
414+
extension SSGC.TypeChecker
415+
{
416+
private mutating
417+
func comptuteConformances(of type:SSGC.DeclObject, by culture:Symbol.Module) throws
418+
{
419+
for (conformance, overlapping):(Symbol.Decl, Set<Set<GenericConstraint<Symbol.Decl>>>)
420+
in type.conformanceStatements
421+
{
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
433+
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
444+
445+
Declared constraints: \(lists)
446+
Simplified constraints: \(reduced)
447+
""")
448+
}
449+
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)
455+
}
400456

457+
// We don’t need this table anymore.
458+
type.conformanceStatements = [:]
459+
}
460+
}
461+
extension SSGC.TypeChecker
462+
{
401463
private mutating
402464
func insert(_ relationship:Symbol.FeatureRelationship, by culture:Symbol.Module) throws
403465
{
@@ -454,37 +516,8 @@ extension SSGC.TypeChecker
454516
""")
455517
}
456518

457-
let conditions:Set<GenericConstraint<Symbol.Decl>>?
458-
do
459-
{
460-
conditions = try heir.conformances[conformance]?.simplify(
461-
with: self.declarations)
462-
}
463-
catch SSGC.ConstraintReductionError.chimaeric(let reduced, from: let lists)
464-
{
465-
throw AssertionError.init(message: """
466-
Failed to simplify constraints for conditional conformance \
467-
(\(conformance)) of '\(heir.value.path)' because multiple conflicting \
468-
conformances unify to a heterogeneous set of constraints
469-
470-
Declared constraints: \(lists)
471-
Simplified constraints: \(reduced)
472-
""")
473-
}
474-
catch SSGC.ConstraintReductionError.redundant(let reduced, from: let lists)
475-
{
476-
throw AssertionError.init(message: """
477-
Failed to simplify constraints for conditional conformance \
478-
(\(conformance)) of '\(heir.value.path)' because at least one of the \
479-
constraint lists had redundancies within itself
480-
481-
Declared constraints: \(lists)
482-
Simplified constraints: \(reduced)
483-
""")
484-
}
485-
486519
guard
487-
let conditions:Set<GenericConstraint<Symbol.Decl>>
520+
let conditions:Set<GenericConstraint<Symbol.Decl>> = heir.conformances[conformance]
488521
else
489522
{
490523
throw AssertionError.init(message: """

Sources/SymbolGraphCompiler/Set (ext).swift

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,6 @@ import Symbols
33

44
extension Set<Set<GenericConstraint<Symbol.Decl>>>
55
{
6-
mutating
7-
func simplify(
8-
with declarations:SSGC.Declarations) throws -> Set<GenericConstraint<Symbol.Decl>>
9-
{
10-
guard
11-
var first:Set<GenericConstraint<Symbol.Decl>> = self.first
12-
else
13-
{
14-
return []
15-
}
16-
17-
if self.count == 1
18-
{
19-
return first
20-
}
21-
else
22-
{
23-
first = try self.simplified(with: declarations)
24-
}
25-
26-
// Cache the simplified constraints so that the next query is faster.
27-
self = [first]
28-
return first
29-
}
30-
31-
private
326
func simplified(
337
with declarations:SSGC.Declarations) throws -> Set<GenericConstraint<Symbol.Decl>>
348
{

Sources/SymbolGraphCompiler/SymbolRelationship (ext).swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import SymbolGraphParts
22

33
extension SymbolRelationship
44
{
5-
func `do`(_ body:(Self) throws -> Void) rethrows
5+
func `do`<T>(_ body:(Self) throws -> T) rethrows -> T
66
{
77
do
88
{
9-
try body(self)
9+
return try body(self)
1010
}
1111
catch let error
1212
{

0 commit comments

Comments
 (0)