Skip to content

Commit

Permalink
Merge pull request #306 from tayloraswift/constraint-spellings
Browse files Browse the repository at this point in the history
partial fix for #273
  • Loading branch information
tayloraswift authored Jul 17, 2024
2 parents 29810ad + 2ac9248 commit 595148e
Show file tree
Hide file tree
Showing 24 changed files with 166 additions and 144 deletions.
7 changes: 1 addition & 6 deletions Sources/Signatures/Generics/GenericConstraint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,8 @@ extension GenericConstraint:Sendable where Scalar:Sendable
extension GenericConstraint
{
@inlinable public
func map<T>(_ transform:(Scalar) throws -> T) rethrows -> GenericConstraint<T>
func map<T>(_ transform:(Scalar) throws -> T?) rethrows -> GenericConstraint<T>
{
.where(self.noun, is: self.what, to: try self.whom.map(transform))
}
@inlinable public
func flatMap<T>(_ transform:(Scalar) throws -> T?) rethrows -> GenericConstraint<T>?
{
try self.whom.flatMap(transform).map { .where(self.noun, is: self.what, to: $0) }
}
}
48 changes: 23 additions & 25 deletions Sources/Signatures/Generics/GenericType.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
@frozen public
enum GenericType<Scalar>
struct GenericType<Scalar>
{
case nominal(Scalar)
case complex(String)
public
let spelling:String
public
let nominal:Scalar?

@inlinable public
init(spelling:String, nominal:Scalar? = nil)
{
self.spelling = spelling
self.nominal = nominal
}
}
extension GenericType:Equatable where Scalar:Equatable
{
Expand All @@ -15,34 +24,23 @@ extension GenericType:Sendable where Scalar:Sendable
}
extension GenericType:Comparable where Scalar:Comparable
{
}
extension GenericType
{
@inlinable public
var nominal:Scalar?
{
switch self
{
case .nominal(let type): type
case .complex: nil
}
}
@inlinable public
func map<T>(_ transform:(Scalar) throws -> T) rethrows -> GenericType<T>
static func < (a:Self, b:Self) -> Bool
{
switch self
switch (a.nominal, b.nominal)
{
case .nominal(let type): .nominal(try transform(type))
case .complex(let type): .complex(type)
case (let i?, let j?): (i, a.spelling) < (j, b.spelling)
case (_?, nil): true
case (nil, _?): false
case (nil, nil): a.spelling < b.spelling
}
}
}
extension GenericType
{
@inlinable public
func flatMap<T>(_ transform:(Scalar) throws -> T?) rethrows -> GenericType<T>?
func map<T>(_ transform:(Scalar) throws -> T?) rethrows -> GenericType<T>
{
switch self
{
case .nominal(let type): try transform(type).map { .nominal($0) }
case .complex(let type): .complex(type)
}
.init(spelling: self.spelling, nominal: try self.nominal.map(transform) ?? nil)
}
}
36 changes: 27 additions & 9 deletions Sources/SymbolGraphPartTests/Main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -381,14 +381,20 @@ enum Main:TestMain, TestBattery
(
["Struct", "internal(_:)"],
[
.where("T", is: .conformer, to: .nominal(.init("s:SQ")!)),
.where("T", is: .conformer, to: .nominal(.init("s:ST")!)),
.where("T", is: .conformer, to: .init(
spelling: "Equatable",
nominal: "sSQ")),
.where("T", is: .conformer, to: .init(
spelling: "Sequence",
nominal: "sST")),
]
),
(
["Protocol", "internal(_:)"],
[
.where("Self.T", is: .conformer, to: .nominal(.init("s:SQ")!)),
.where("Self.T", is: .conformer, to: .init(
spelling: "Equatable",
nominal: "sSQ")),
]
),
]
Expand All @@ -411,27 +417,39 @@ enum Main:TestMain, TestBattery
(
["Struct"],
[
.where("T", is: .conformer, to: .nominal(.init("s:SQ")!)),
.where("T", is: .conformer, to: .nominal(.init("s:ST")!)),
.where("T", is: .conformer, to: .init(
spelling: "Equatable",
nominal: "sSQ")),
.where("T", is: .conformer, to: .init(
spelling: "Sequence",
nominal: "sST")),
]
),
(
["Struct", "external(_:)"],
[
.where("T", is: .conformer, to: .nominal(.init("s:SQ")!)),
.where("T", is: .conformer, to: .nominal(.init("s:ST")!)),
.where("T", is: .conformer, to: .init(
spelling: "Equatable",
nominal: "sSQ")),
.where("T", is: .conformer, to: .init(
spelling: "Sequence",
nominal: "sST")),
]
),
(
["Protocol"],
[
.where("Self.T", is: .conformer, to: .nominal(.init("s:SQ")!)),
.where("Self.T", is: .conformer, to: .init(
spelling: "Equatable",
nominal: "sSQ")),
]
),
(
["Protocol", "external(_:)"],
[
.where("Self.T", is: .conformer, to: .nominal(.init("s:SQ")!)),
.where("Self.T", is: .conformer, to: .init(
spelling: "Equatable",
nominal: "sSQ")),
]
),
]
Expand Down
13 changes: 2 additions & 11 deletions Sources/SymbolGraphParts/Generics/GenericConstraint (ext).swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,9 @@ extension GenericConstraint:JSONObjectDecodable, JSONDecodable where Scalar:JSON
public
init(json:JSON.ObjectDecoder<CodingKey>) throws
{
let type:GenericType<Scalar>
if let scalar:Scalar = try json[.rhsPrecise]?.decode()
{
type = .nominal(scalar)
}
else
{
type = .complex(try json[.rhs].decode())
}

self = .where(try json[.lhs].decode(),
is: try json[.kind].decode(as: Kind.self, with: \.operator),
to: type)
to: .init(spelling: try json[.rhs].decode(),
nominal: try json[.rhsPrecise]?.decode()))
}
}
4 changes: 2 additions & 2 deletions Sources/SymbolGraphTests/Main.Generics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ extension Main.Generics:TestBattery
{
for (name, whom):(String, GenericType<Int32>) in
[
("nominal", .nominal(13)),
("complex", .complex("Dictionary<Int, String>.Index"))
("nominal", .init(spelling: "", nominal: 13)),
("complex", .init(spelling: "Dictionary<Int, String>.Index", nominal: nil))
]
{
guard let tests:TestGroup = tests / name
Expand Down
39 changes: 16 additions & 23 deletions Sources/SymbolGraphs/Generics/GenericConstraint (ext).swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,27 @@ extension GenericConstraint
///
/// The BSON coding schema looks roughly like one of
///
/// `{ G: "0GenericParameterName.AssociatedType", N: 0x1234_5678 }`
/// `{ G: "0GenericParameterName.AssociatedType", C: "Int", N: 0x1234_5678 }`
///
/// or
///
/// `{ G: "0GenericParameterName.AssociatedType", C: "Array<Int>" }` .
///
/// The nominal (`N`) field is usually integer-typed, but its BSON
/// The ``nominal`` (`N`) field is usually integer-typed, but its BSON
/// representation is up to the generic ``Scalar`` parameter.
///
/// The complex (`C`) field is always a string.
/// The ``spelling`` (`C`) field is always a string.
///
/// Prior to 0.9.9, the ``spelling`` was optional.
@frozen public
enum CodingKey:String, Sendable
{
case generic = "G"
case nominal = "N"
case complex = "C"
case spelling = "C"

@available(*, deprecated, renamed: "spelling")
static var complex:Self { .spelling }
}
}
extension GenericConstraint:BSONDocumentEncodable, BSONEncodable
Expand All @@ -41,12 +46,9 @@ extension GenericConstraint:BSONDocumentEncodable, BSONEncodable
}

bson[.generic] = "\(sigil)\(self.noun)"

switch self.whom
{
case .nominal(let type): bson[.nominal] = type
case .complex(let description): bson[.complex] = description
}
bson[.nominal] = self.whom.nominal
// For roundtripping pre-0.9.9 BSON. After 1.0, we should encode it unconditionally.
bson[.spelling] = self.whom.spelling.isEmpty ? nil : self.whom.spelling
}
}
extension GenericConstraint:BSONDocumentDecodable, BSONDecodable
Expand All @@ -72,19 +74,10 @@ extension GenericConstraint:BSONDocumentDecodable, BSONDecodable
return (sigil, .init(decoding: $0.bytes.dropFirst(), as: Unicode.UTF8.self))
}

let type:GenericType<Scalar>
// If there is a null value (which is allowed if `Scalar` is an ``Optional`` type),
// we don’t want to attempt to decode from ``CodingKey.complex``. If we do not
// specify the decoded type (`Scalar.self`), the compiler will infer it to be
// `Scalar?`, which will cause us to fall through to the else block!
if let scalar:Scalar = try bson[.nominal]?.decode(to: Scalar.self)
{
type = .nominal(scalar)
}
else
{
type = .complex(try bson[.complex].decode())
}
let type:GenericType<Scalar> = .init(
// TODO: deoptionalize this after 1.0.
spelling: try bson[.spelling]?.decode() ?? "",
nominal: try bson[.nominal]?.decode())

switch sigil
{
Expand Down
2 changes: 1 addition & 1 deletion Sources/SymbolGraphs/SymbolGraphABI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import SemanticVersions
@frozen public
enum SymbolGraphABI
{
@inlinable public static var version:PatchVersion { .v(0, 9, 8) }
@inlinable public static var version:PatchVersion { .v(0, 9, 9) }
}
8 changes: 4 additions & 4 deletions Sources/UnidocLinker/Sema/ConstraintReductionError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import SourceDiagnostics

struct ConstraintReductionError:Error, Equatable, Sendable
{
let constraints:[[GenericConstraint<Unidoc.Scalar?>]]
let minimal:[GenericConstraint<Unidoc.Scalar?>]
let constraints:[[GenericConstraint<Unidoc.Scalar>]]
let minimal:[GenericConstraint<Unidoc.Scalar>]
let subject:Unidoc.Scalar
let `protocol`:Unidoc.Scalar

init(invalid constraints:[[GenericConstraint<Unidoc.Scalar?>]],
minimal:[GenericConstraint<Unidoc.Scalar?>],
init(invalid constraints:[[GenericConstraint<Unidoc.Scalar>]],
minimal:[GenericConstraint<Unidoc.Scalar>],
subject:Unidoc.Scalar,
`protocol`:Unidoc.Scalar)
{
Expand Down
2 changes: 1 addition & 1 deletion Sources/UnidocLinker/Sema/Unidoc.Conformers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ extension Unidoc.Conformers
{
mutating
func append(conformer type:Unidoc.Scalar,
where constraints:[GenericConstraint<Unidoc.Scalar?>])
where constraints:[GenericConstraint<Unidoc.Scalar>])
{
constraints.isEmpty
? self.unconditional.append(type)
Expand Down
4 changes: 2 additions & 2 deletions Sources/UnidocLinker/Sema/Unidoc.ExtensionConditions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ extension Unidoc
{
struct ExtensionConditions:Equatable, Hashable, Sendable
{
var constraints:[GenericConstraint<Unidoc.Scalar?>]
var constraints:[GenericConstraint<Unidoc.Scalar>]
let culture:Int

init(constraints:[GenericConstraint<Unidoc.Scalar?>], culture:Int)
init(constraints:[GenericConstraint<Unidoc.Scalar>], culture:Int)
{
self.constraints = constraints
self.culture = culture
Expand Down
14 changes: 11 additions & 3 deletions Sources/UnidocLinker/Sema/Unidoc.Linker.Table.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ extension Unidoc.Linker.Table<Unidoc.Extension>
return [:]
}

let universal:Set<GenericConstraint<Unidoc.Scalar?>> =
let universal:Set<GenericConstraint<Unidoc.Scalar>> =
extendedDecl.signature.generics.constraints.reduce(into: [])
{
$0.insert($1.map { extendedSnapshot.scalars.decls[$0] })
Expand All @@ -139,7 +139,7 @@ extension Unidoc.Linker.Table<Unidoc.Extension>
.init(
constraints: $0.conditions.compactMap
{
let constraint:GenericConstraint<Unidoc.Scalar?> = $0.map
let constraint:GenericConstraint<Unidoc.Scalar> = $0.map
{
context.current.scalars.decls[$0]
}
Expand Down Expand Up @@ -174,7 +174,15 @@ extension Unidoc.Linker.Table<Unidoc.Extension>
/// ``RawRepresentable`` in the standard library.
conditions.constraints.removeAll
{
$0 == .where("Self", is: .conformer, to: .nominal(s))
if case .where("Self", is: .conformer, to: let type) = $0,
case s? = type.nominal
{
true
}
else
{
false
}
}
}
// It’s possible for two locally-disjoint extensions to coalesce
Expand Down
Loading

0 comments on commit 595148e

Please sign in to comment.