Skip to content

Commit 30d12ea

Browse files
authored
Merge pull request #315 from tayloraswift/anneal-namespaces
Anneal namespaces
2 parents 157528e + 35adf1e commit 30d12ea

File tree

9 files changed

+162
-75
lines changed

9 files changed

+162
-75
lines changed

Package.resolved

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/SymbolGraphCompiler/Declarations/SSGC.DeclObject.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ extension SSGC
1313
class DeclObject
1414
{
1515
let conditions:Set<GenericConstraint<Symbol.Decl>>
16-
let namespace:Symbol.Module
16+
var namespace:Symbol.Module
1717
let culture:Symbol.Module
18-
let access:Symbol.ACL
18+
var access:Symbol.ACL
1919

2020
/// The outer set is populated by redundant implicit conformances produced by
2121
/// lib/SymbolGraphGen.

Sources/SymbolGraphCompiler/Declarations/SSGC.Declarations.swift

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ extension SSGC
2424
}
2525
extension SSGC.Declarations
2626
{
27+
/// Returns the declaration object, or nil if it has already been indexed, or does not meet
28+
/// the minimum visibility threshold.
2729
mutating
2830
func include(_ vertex:SymbolGraphPart.Vertex,
2931
namespace:Symbol.Module,
30-
culture:Symbol.Module) -> SSGC.DeclObject
32+
culture:Symbol.Module) -> SSGC.DeclObject?
3133
{
3234
guard
3335
case .scalar(let symbol) = vertex.usr,
@@ -37,11 +39,12 @@ extension SSGC.Declarations
3739
fatalError("vertex is not a decl!")
3840
}
3941

40-
let decl:SSGC.DeclObject =
42+
let decl:SSGC.DeclObject? =
4143
{
42-
if let decl:SSGC.DeclObject = $0
44+
guard case nil = $0
45+
else
4346
{
44-
return decl
47+
return nil
4548
}
4649

4750
var kinks:Phylum.Decl.Kinks = []
@@ -72,7 +75,7 @@ extension SSGC.Declarations
7275
comment: vertex.doccomment.map { .init($0.text, at: $0.start) } ?? nil))
7376

7477
$0 = decl
75-
return decl
78+
return decl.access < self.threshold ? nil : decl
7679

7780
} (&self.decls[symbol])
7881

Sources/SymbolGraphCompiler/SSGC.TypeChecker.swift

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ extension SSGC.TypeChecker
7676
/// We use this to look up protocols by name instead of symbol. This is needed in order
7777
/// to work around some bizarre lib/SymbolGraphGen bugs.
7878
var protocolsByName:[UnqualifiedPath: Symbol.Decl] = [:]
79-
var children:[SSGC.DeclObject] = []
79+
var children:[Int: [SSGC.DeclObject]] = [:]
8080
// Pass I. Gather scalars, extension blocks, and extension relationships.
8181
for part:SSGC.SymbolDump in culture.symbols
8282
{
@@ -91,33 +91,40 @@ extension SSGC.TypeChecker
9191
switch vertex.usr
9292
{
9393
case .block(let symbol):
94-
// We *do* in fact care about extension blocks that only contain
95-
// internal/private members, because they might contain a conformance
96-
// to an internal protocol that inherits from a public protocol.
97-
// SymbolGraphGen probably shouldn’t be marking these extension blocks
98-
// as internal, but SymbolGraphGen doesn’t care what we think.
94+
// We *do* in fact care about extension blocks that only contain
95+
// internal/private members, because they might contain a conformance
96+
// to an internal protocol that inherits from a public protocol.
97+
// SymbolGraphGen probably shouldn’t be marking these extension blocks
98+
// as internal, but SymbolGraphGen doesn’t care what we think.
9999
self.extensions.include(vertex,
100100
extending: try extensions.extendee(of: symbol),
101101
culture: id)
102102

103103
case .scalar(let symbol):
104+
guard
104105
let decl:SSGC.DeclObject = self.declarations.include(vertex,
105106
namespace: namespace,
106107
culture: id)
108+
else
109+
{
110+
// Declaration is private or re-exported.
111+
continue
112+
}
107113

108114
if case .decl(.protocol) = vertex.phylum
109115
{
110116
protocolsByName[vertex.path] = symbol
111117
}
112-
else if !decl.value.path.prefix.isEmpty
118+
119+
let depth:Int = decl.value.path.prefix.count
120+
if depth > 0
113121
{
114-
children.append(decl)
122+
children[depth, default: []].append(decl)
115123
}
116-
117-
if decl.culture == id
124+
else
118125
{
119-
// If this is not a re-exported symbol, make it available for
120-
// link resolution.
126+
// We need to wait until the next pass for the nested declarations,
127+
// because we do not know if their namespaces are correct yet.
121128
self.resolvableLinks[namespace, decl.value.path].append(
122129
.decl(decl.value))
123130
}
@@ -154,15 +161,15 @@ extension SSGC.TypeChecker
154161
try nesting.do { try self.assign($0, by: id) }
155162
}
156163
}
157-
// SymbolGraphGen fails to emit a `memberOf` edge if the member is a default
164+
// lib/SymbolGraphGen fails to emit a `memberOf` edge if the member is a default
158165
// implementation of a protocol requirement. Because the requirement might be a
159166
// requirement from a different protocol than the protocol containing the default
160167
// implementation, this means we need to use lexical name lookup to resolve the true
161168
// parent of the default implementation.
162169
//
163170
// Luckily for us, this lib/SymbolGraphGen bug only seems to affect default
164171
// implementations that implement requirements from protocols in the current module.
165-
for decl:SSGC.DeclObject in children
172+
for decl:SSGC.DeclObject in children.values.joined()
166173
{
167174
guard decl.scopes.isEmpty,
168175
let parent:UnqualifiedPath = .init(decl.value.path.prefix),
@@ -176,6 +183,39 @@ extension SSGC.TypeChecker
176183

177184
try self.assign(inferred, by: id)
178185
}
186+
// lib/SymbolGraphGen will place nested declarations under the wrong namespace if the
187+
// outer type was re-exported from another module. This is a bug in lib/SymbolGraphGen.
188+
// There is an alternative source of this information, in the `extendedModule` field of
189+
// the extension context, but this field is only present for declarations that are
190+
// physically written in an extension block, and also does not specify the correct
191+
// namespace for types under more than one level of nesting.
192+
//
193+
// Therefore, we need to correct the namespaces by actually inspecting the extended
194+
// types of nested declarations. It is most efficient to visit the shallower
195+
// declarations first, as this avoids the need to traverse the entire path hierarchy.
196+
for (_, decls):(Int, [SSGC.DeclObject]) in children.sorted(by: { $0.key < $1.key })
197+
{
198+
for decl:SSGC.DeclObject in decls
199+
{
200+
guard
201+
let scope:Symbol.Decl = decl.scopes.first,
202+
let scope:SSGC.DeclObject = self.declarations[visible: scope]
203+
else
204+
{
205+
// If we can’t find the parent, this symbol is probably a public member of
206+
// a private type, which is a common bug in lib/SymbolGraphGen.
207+
decl.access = .private
208+
continue
209+
}
210+
211+
let namespace:Symbol.Module = scope.namespace
212+
213+
decl.access = min(decl.access, scope.access)
214+
decl.namespace = namespace
215+
216+
self.resolvableLinks[namespace, decl.value.path].append(.decl(decl.value))
217+
}
218+
}
179219

180220
// Pass II. Populate remaining relationships.
181221
for part:SSGC.SymbolDump in culture.symbols
@@ -453,31 +493,10 @@ extension SSGC.TypeChecker
453493
let conformance:Symbol.Decl = feature.scopes.first
454494
else
455495
{
456-
// We hit this on an extremely unusual edge case where a feature and an heir
457-
// are public, but the protocol the feature is defined on is not. Here is some
458-
// valid Swift code that demonstrates this:
459-
//
460-
/* ```
461-
protocol P
462-
{
463-
}
464-
extension P
465-
{
466-
public
467-
func f()
468-
{
469-
}
470-
}
471-
472-
public
473-
struct S:P
474-
{
475-
}
476-
```
477-
*/
478-
// Ideally, lib/SymbolGraphGen would not emit the feature in this case, but
479-
// it does anyway.
480-
return
496+
throw AssertionError.init(message: """
497+
Declaration '\(feature.value.path)' has '\(feature.access)' access but \
498+
has no known lexical parents
499+
""")
481500
}
482501

483502
guard feature.scopes.count == 1
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import SymbolGraphCompiler
2+
@_spi(testable)
3+
import Symbols
4+
import Testing_
5+
6+
extension Main
7+
{
8+
enum FeatureInheritanceAccessControl
9+
{
10+
}
11+
}
12+
extension Main.FeatureInheritanceAccessControl:CompilerTestBattery
13+
{
14+
static
15+
let inputs:[Symbol.Module] =
16+
[
17+
"FeatureInheritanceAccessControl",
18+
]
19+
20+
static
21+
func run(tests:TestGroup, module:SSGC.ModuleIndex)
22+
{
23+
let declsBySymbol:[Symbol.Decl: SSGC.Decl] = module.declarations.reduce(into: [:])
24+
{
25+
for decl:SSGC.Decl in $1.decls
26+
{
27+
$0[decl.id] = decl
28+
}
29+
}
30+
let features:[Symbol.Decl: [Symbol.Decl]] = module.extensions.reduce(into: [:])
31+
{
32+
$0[$1.extendee.id, default: []] += $1.features
33+
}
34+
35+
36+
if let tests:TestGroup = tests / "S",
37+
let _:SSGC.Decl = tests.expect(
38+
value: declsBySymbol["s31FeatureInheritanceAccessControl1SV"])
39+
{
40+
tests.expect(nil: features["s31FeatureInheritanceAccessControl1SV"])
41+
}
42+
}
43+
}

Sources/SymbolGraphCompilerTests/Main.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ enum Main:TestMain
99
Determinism.self,
1010
DefaultImplementations.self,
1111
FeatureInheritance.self,
12+
FeatureInheritanceAccessControl.self,
1213
ExternalExtensionsWithConformances.self,
1314
ExternalExtensionsWithConstraints.self,
1415
InternalExtensionsWithConformances.self,

Sources/SymbolGraphParts/Vertices/SymbolGraphPart.Vertex.ExtensionContext.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,15 @@ extension SymbolGraphPart.Vertex.ExtensionContext:JSONObjectDecodable
2626
public
2727
enum CodingKey:String, Sendable
2828
{
29-
case conditions = "constraints"
29+
case constraints
30+
31+
@available(*, unavailable, message: "Not useful")
32+
case extendedModule
3033
}
3134

3235
public
3336
init(json:JSON.ObjectDecoder<CodingKey>) throws
3437
{
35-
self.init(conditions: try json[.conditions]?.decode() ?? [])
38+
self.init(conditions: try json[.constraints]?.decode() ?? [])
3639
}
3740
}

0 commit comments

Comments
 (0)