Skip to content

Commit a26917a

Browse files
authored
Merge pull request #330 from tayloraswift/linker-lite
lightweight dynamic linker optimized for SGABI ≥ 0.10
2 parents 7730a7f + 783078a commit a26917a

16 files changed

+928
-99
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: 118 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.computeConformances(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,86 @@ extension SSGC.TypeChecker
397410
superform.kinks[is: .implemented] = true
398411
}
399412
}
413+
}
414+
extension SSGC.TypeChecker
415+
{
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+
}
448+
private mutating
449+
func computeConformances(of type:SSGC.DeclObject, by culture:Symbol.Module) throws
450+
{
451+
for (target, overlapping):(Symbol.Decl, Set<Set<GenericConstraint<Symbol.Decl>>>)
452+
in type.conformanceStatements
453+
{
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+
}
472+
473+
let canonical:Set<GenericConstraint<Symbol.Decl>> = try self.computeConformance(
474+
where: overlapping,
475+
to: target,
476+
of: type)
477+
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
483+
484+
} (&type.conformances[target])
485+
}
400486

487+
// We don’t need this table anymore.
488+
type.conformanceStatements = [:]
489+
}
490+
}
491+
extension SSGC.TypeChecker
492+
{
401493
private mutating
402494
func insert(_ relationship:Symbol.FeatureRelationship, by culture:Symbol.Module) throws
403495
{
@@ -454,37 +546,8 @@ extension SSGC.TypeChecker
454546
""")
455547
}
456548

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-
486549
guard
487-
let conditions:Set<GenericConstraint<Symbol.Decl>>
550+
let conditions:Set<GenericConstraint<Symbol.Decl>> = heir.conformances[conformance]
488551
else
489552
{
490553
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
{

Sources/SymbolGraphs/SymbolGraphABI.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ import SemanticVersions
33
@frozen public
44
enum SymbolGraphABI
55
{
6-
@inlinable public static var version:PatchVersion { .v(0, 10, 3) }
6+
@inlinable public static var version:PatchVersion { .v(0, 11, 0) }
77
}

0 commit comments

Comments
 (0)