Skip to content

Commit c6b5390

Browse files
authored
Merge pull request #380 from tayloraswift/disambiguate-existentials
Disambiguate existentials, parameter packs, `some` types, variadics, and `~Copyable` types
2 parents 3316887 + 9188458 commit c6b5390

15 files changed

+393
-121
lines changed

Sources/MarkdownPluginSwift/Signatures/SignatureSyntax.Autographer.swift

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,39 @@ extension SignatureSyntax
2020
}
2121
extension SignatureSyntax.Autographer
2222
{
23+
mutating
24+
func encode(parameter:FunctionParameterSyntax)
25+
{
26+
self.encode(type: parameter.type)
27+
28+
if case _? = parameter.ellipsis
29+
{
30+
self.autograph.append("...")
31+
}
32+
}
33+
2334
mutating
2435
func encode(type:TypeSyntax)
2536
{
2637
if let type:AttributedTypeSyntax = type.as(AttributedTypeSyntax.self)
2738
{
2839
self.encode(type: type.baseType)
2940
}
41+
else if
42+
let type:SomeOrAnyTypeSyntax = type.as(SomeOrAnyTypeSyntax.self)
43+
{
44+
self.encode(type: type.constraint)
45+
}
46+
else if
47+
let type:PackElementTypeSyntax = type.as(PackElementTypeSyntax.self)
48+
{
49+
self.encode(type: type.pack)
50+
}
51+
else if
52+
let type:PackExpansionTypeSyntax = type.as(PackExpansionTypeSyntax.self)
53+
{
54+
self.encode(type: type.repetitionPattern)
55+
}
3056
else if
3157
let type:IdentifierTypeSyntax = type.as(IdentifierTypeSyntax.self)
3258
{
@@ -74,6 +100,12 @@ extension SignatureSyntax.Autographer
74100
self.encode(type: type.baseType)
75101
self.autograph.append(".Type")
76102
}
103+
else if
104+
let suppressed:SuppressedTypeSyntax = type.as(SuppressedTypeSyntax.self)
105+
{
106+
self.autograph.append("~")
107+
self.encode(type: suppressed.type)
108+
}
77109
else if
78110
let type:TupleTypeSyntax = type.as(TupleTypeSyntax.self)
79111
{
@@ -85,6 +117,8 @@ extension SignatureSyntax.Autographer
85117

86118
self.autograph.append("(")
87119

120+
/// We don’t rely on the existence of the trailing comma in the syntax tree, because
121+
/// Swift now allows trailing commas in many places, and we want to normalize them.
88122
var first:Bool = true
89123
for element:TupleTypeElementSyntax in type.elements
90124
{
@@ -124,6 +158,24 @@ extension SignatureSyntax.Autographer
124158
self.autograph.append(")->")
125159
self.encode(type: function.returnClause.type)
126160
}
161+
else if
162+
let type:CompositionTypeSyntax = type.as(CompositionTypeSyntax.self)
163+
{
164+
var first:Bool = true
165+
for element:CompositionTypeElementSyntax in type.elements
166+
{
167+
if first
168+
{
169+
first = false
170+
}
171+
else
172+
{
173+
self.autograph.append("&")
174+
}
175+
176+
self.encode(type: element.type)
177+
}
178+
}
127179
else
128180
{
129181
self.autograph.append("_")

Sources/MarkdownPluginSwift/Signatures/SignatureSyntax.ExpandedVisitor.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ extension SignatureSyntax.ExpandedVisitor:SignatureVisitor
2929
type _:SignatureParameterType) -> SignatureSyntax.ExpandedParameter
3030
{
3131
var autographer:SignatureSyntax.Autographer = .init(sugaring: self.sugarMap)
32-
autographer.encode(type: parameter.type)
32+
autographer.encode(parameter: parameter)
3333
inputs.append(autographer.autograph)
3434
return .init(syntax: parameter)
3535
}

Sources/MarkdownPluginSwiftTests/Autographs.swift

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ struct Autographs
5757
let decl:String = "subscript(a: some Equatable, b: [Float: Bool]!) -> (String)?"
5858
let _:Signature<Never>.Expanded = .init(decl, landmarks: &self.landmarks)
5959

60-
#expect(self.landmarks.inputs == ["_", "[Float:Bool]!"])
60+
#expect(self.landmarks.inputs == ["Equatable", "[Float:Bool]!"])
6161
#expect(self.landmarks.output == ["String?"])
6262
}
6363

@@ -85,6 +85,89 @@ struct Autographs
8585
#expect(self.landmarks.output == ["(Int)->(Int)->Int"])
8686
}
8787

88+
@Test mutating
89+
func SomeAndAnyTypes()
90+
{
91+
let decl:String = """
92+
func f(
93+
a: any Error,
94+
b: some Error & CustomStringConvertible,
95+
c: [some RandomAccessCollection<UInt8> & MutableCollection<UInt8>].Type)
96+
"""
97+
let _:Signature<Never>.Expanded = .init(decl, landmarks: &self.landmarks)
98+
99+
#expect(self.landmarks.inputs == [
100+
"Error",
101+
"Error&CustomStringConvertible",
102+
"[RandomAccessCollection<UInt8>&MutableCollection<UInt8>].Type"
103+
])
104+
#expect(self.landmarks.output == [])
105+
}
106+
107+
/// Note that as of Swift 6.0, it is currently illegal to include primary associated types
108+
/// in an existential protocol composition type, although this is allowed for `some` types.
109+
@Test mutating
110+
func ProtocolCompositions()
111+
{
112+
let decl:String = """
113+
func f(
114+
a: any Error,
115+
b: any Error & CustomStringConvertible,
116+
c: any RandomAccessCollection & MutableCollection)
117+
"""
118+
let _:Signature<Never>.Expanded = .init(decl, landmarks: &self.landmarks)
119+
120+
#expect(self.landmarks.inputs == [
121+
"Error",
122+
"Error&CustomStringConvertible",
123+
"RandomAccessCollection&MutableCollection"
124+
])
125+
#expect(self.landmarks.output == [])
126+
}
127+
128+
@Test mutating
129+
func Variadics()
130+
{
131+
let decl:String = """
132+
func f(
133+
a: String...,
134+
b: [String]...,
135+
c: Set<Int>...)
136+
"""
137+
let _:Signature<Never>.Expanded = .init(decl, landmarks: &self.landmarks)
138+
139+
#expect(self.landmarks.inputs == [
140+
"String...",
141+
"[String]...",
142+
"Set<Int>..."
143+
])
144+
#expect(self.landmarks.output == [])
145+
}
146+
147+
@Test mutating
148+
func Packs()
149+
{
150+
let decl:String = """
151+
func f<each T>(x: repeat [each T])
152+
"""
153+
let _:Signature<Never>.Expanded = .init(decl, landmarks: &self.landmarks)
154+
155+
#expect(self.landmarks.inputs == ["[T]"])
156+
#expect(self.landmarks.output == [])
157+
}
158+
159+
@Test mutating
160+
func Noncopyable()
161+
{
162+
let decl:String = """
163+
func f(a: borrowing some ~Copyable)
164+
"""
165+
let _:Signature<Never>.Expanded = .init(decl, landmarks: &self.landmarks)
166+
167+
#expect(self.landmarks.inputs == ["~Copyable"])
168+
#expect(self.landmarks.output == [])
169+
}
170+
88171
@Test mutating
89172
func Resugaring()
90173
{

Sources/UCF/Codelinks/Grammar/UCF.NominalPatternRule.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ extension UCF
77
{
88
typealias Location = String.Index
99
typealias Terminal = Unicode.Scalar
10-
1110
typealias Construction = [(Range<Location>, [UCF.TypePattern])]
1211

1312
static func parse<Diagnostics>(
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
extension UCF
2+
{
3+
struct TypeElement
4+
{
5+
let prefix:TypeSigil?
6+
let operand:TypeOperand
7+
let suffix:[TypeOperator]
8+
}
9+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Grammar
2+
3+
extension UCF.TypeElementRule
4+
{
5+
enum PostfixMetatype:LiteralRule
6+
{
7+
typealias Location = String.Index
8+
static var literal:[Unicode.Scalar] { ["T","y","p","e"] }
9+
}
10+
}

Sources/UCF/Codelinks/Grammar/UCF.TypePatternRule.PostfixOperator.swift renamed to Sources/UCF/Codelinks/Grammar/UCF.TypeElementRule.PostfixOperator.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import Grammar
22

3-
extension UCF.TypePatternRule
3+
extension UCF.TypeElementRule
44
{
5-
/// PostfixOperator ::= '?' | '!' | '.Type'
5+
/// PostfixOperator ::= '?' | '!' | '.Type' | '...'
66
enum PostfixOperator:ParsingRule
77
{
88
typealias Location = String.Index
@@ -18,15 +18,18 @@ extension UCF.TypePatternRule
1818
{
1919
return codepoint
2020
}
21+
22+
try input.parse(as: UnicodeEncoding<Location, Terminal>.Period.self)
23+
24+
if case ()? = input.parse(as: PostfixMetatype?.self)
25+
{
26+
return .metatype
27+
}
2128
else
2229
{
2330
try input.parse(as: UnicodeEncoding<Location, Terminal>.Period.self)
24-
try input.parse(as: UnicodeEncoding<Location, Terminal>.UppercaseT.self)
25-
try input.parse(as: UnicodeEncoding<Location, Terminal>.LowercaseY.self)
26-
try input.parse(as: UnicodeEncoding<Location, Terminal>.LowercaseP.self)
27-
try input.parse(as: UnicodeEncoding<Location, Terminal>.LowercaseE.self)
28-
29-
return .metatype
31+
try input.parse(as: UnicodeEncoding<Location, Terminal>.Period.self)
32+
return .ellipsis
3033
}
3134
}
3235
}

Sources/UCF/Codelinks/Grammar/UCF.TypePatternRule.PostfixOperatorCodepoint.swift renamed to Sources/UCF/Codelinks/Grammar/UCF.TypeElementRule.PostfixOperatorCodepoint.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Grammar
22

3-
extension UCF.TypePatternRule
3+
extension UCF.TypeElementRule
44
{
55
enum PostfixOperatorCodepoint:TerminalRule
66
{
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import Grammar
2+
3+
extension UCF
4+
{
5+
/// TypeElement ::= '~' ? TypeOperand PostfixOperator *
6+
enum TypeElementRule:ParsingRule
7+
{
8+
typealias Location = String.Index
9+
typealias Terminal = Unicode.Scalar
10+
11+
static func parse<Source>(
12+
_ input:inout ParsingInput<some ParsingDiagnostics<Source>>) throws -> TypeElement
13+
where Source:Collection<Terminal>, Source.Index == Location
14+
{
15+
let sigil:TypeSigil?
16+
if case ()? = input.parse(as: UnicodeEncoding<Location, Terminal>.Tilde?.self)
17+
{
18+
sigil = .tilde
19+
}
20+
else
21+
{
22+
sigil = nil
23+
}
24+
25+
let operand:TypeOperand = try input.parse(as: TypeOperandRule.self)
26+
let suffix:[TypeOperator] = input.parse(as: PostfixOperator.self, in: [_].self)
27+
28+
if suffix.isEmpty,
29+
case nil = sigil,
30+
case .single(let parenthesized?) = operand,
31+
let inhabitant:TypeElement = parenthesized.inhabitant
32+
{
33+
// If this is a bare parenthesized operand with no operators, unwrap it.
34+
return inhabitant
35+
}
36+
else
37+
{
38+
return .init(prefix: sigil, operand: operand, suffix: suffix)
39+
}
40+
}
41+
}
42+
}

Sources/UCF/Codelinks/Grammar/UCF.TypePatternRule.PostfixOperand.swift renamed to Sources/UCF/Codelinks/Grammar/UCF.TypeOperandRule.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
import Grammar
22

3-
extension UCF.TypePatternRule
3+
extension UCF
44
{
5-
/// PostfixOperand ::= NominalPattern | BracketPattern | FunctionPattern
6-
enum PostfixOperand:ParsingRule
5+
/// TypeOperand ::= NominalPattern | BracketPattern | FunctionPattern
6+
enum TypeOperandRule:ParsingRule
77
{
88
typealias Location = String.Index
99
typealias Terminal = Unicode.Scalar
1010

1111
static func parse<Diagnostics>(
12-
_ input:inout ParsingInput<Diagnostics>) throws -> UCF.TypeOperand where
12+
_ input:inout ParsingInput<Diagnostics>) throws -> TypeOperand where
1313
Diagnostics:ParsingDiagnostics,
1414
Diagnostics.Source.Element == Terminal,
1515
Diagnostics.Source.Index == Location
1616
{
17-
if let path:[(Range<Location>, [UCF.TypePattern])] = input.parse(
18-
as: UCF.NominalPatternRule?.self)
17+
if let path:[(Range<Location>, [TypePattern])] = input.parse(
18+
as: NominalPatternRule?.self)
1919
{
20-
if let (range, generics):(Range<Location>, [UCF.TypePattern]) = path.first,
20+
if let (range, generics):(Range<Location>, [TypePattern]) = path.first,
2121
generics.isEmpty,
2222
path.count == 1,
2323
input.source.index(after: range.lowerBound) == range.upperBound,
@@ -29,16 +29,16 @@ extension UCF.TypePatternRule
2929
return .nominal(path)
3030
}
3131
else if
32-
let (first, value):(UCF.TypePattern, UCF.TypePattern?) = input.parse(
33-
as: UCF.BracketPatternRule?.self)
32+
let (first, value):(TypePattern, TypePattern?) = input.parse(
33+
as: BracketPatternRule?.self)
3434
{
3535
return .bracket(first, value)
3636
}
3737

38-
switch try input.parse(as: UCF.FunctionPatternRule.self)
38+
switch try input.parse(as: FunctionPatternRule.self)
3939
{
4040
case (let tuple, nil):
41-
if let first:UCF.TypePattern = tuple.first, tuple.count == 1
41+
if let first:TypePattern = tuple.first, tuple.count == 1
4242
{
4343
return .single(first)
4444
}

0 commit comments

Comments
 (0)