Skip to content

Commit 003fdec

Browse files
committed
Swift SIL: a few small addition to SmallProjectionPath
* rename `matchesAllValueFields` -> `topMatchesAnyValueField` * add `hasSingleClassIndirection` and `hasClassProjection` * add `popLastClassAndValuesFromTail`
1 parent 320e4c6 commit 003fdec

File tree

1 file changed

+110
-6
lines changed

1 file changed

+110
-6
lines changed

SwiftCompilerSources/Sources/SIL/SmallProjectionPath.swift

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public struct SmallProjectionPath : CustomStringConvertible, CustomReflectable,
4242
/// The physical representation of the path. The path components are stored in
4343
/// reverse order: the first path component is stored in the lowest bits (LSB),
4444
/// the last component is stored in the highest bits (MSB).
45-
/// Each pass component consists of zero or more "index-overflow" bytes followed
45+
/// Each path component consists of zero or more "index-overflow" bytes followed
4646
/// by the "index-kind" main byte (from LSB to MSB).
4747
///
4848
/// index overflow byte: bit-nr: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
@@ -70,10 +70,10 @@ public struct SmallProjectionPath : CustomStringConvertible, CustomReflectable,
7070
case classField = 0x4 // A concrete class field: syntax e.g. `c1`
7171
case tailElements = 0x5 // A tail allocated element of a class: syntax `ct`
7272
case anyValueFields = 0x6 // Any number of any value fields (struct, tuple, enum): syntax `v**`
73-
case anyClassField = 0x7 // Any class field, including tail elements: syntax `c*`
74-
73+
7574
// "Large" kinds: starting from here the low 3 bits must be 1.
7675
// This and all following kinds (we'll add in the future) cannot have a field index.
76+
case anyClassField = 0x7 // Any class field, including tail elements: syntax `c*`
7777
case anything = 0xf // Any number of any fields: syntax `**`
7878

7979
public var isValueField: Bool {
@@ -180,7 +180,10 @@ public struct SmallProjectionPath : CustomStringConvertible, CustomReflectable,
180180
assert(kind != .anything || bytes == 0, "'anything' only allowed in last path component")
181181
var idx = index
182182
var b = bytes
183-
if (b >> 56) != 0 { return Self(.anything) }
183+
if (b >> 56) != 0 {
184+
// Overflow
185+
return Self(.anything)
186+
}
184187
b = (b << 8) | UInt64(((idx & 0xf) << 4) | (kind.rawValue << 1))
185188
idx >>= 4
186189
while idx != 0 {
@@ -242,13 +245,37 @@ public struct SmallProjectionPath : CustomStringConvertible, CustomReflectable,
242245
/// returns true for `v**.c3`
243246
/// returns true for `**`
244247
/// returns false for `s0.c3` (because e.g. `s1` would not match)
245-
public var matchesAllValueFields: Bool {
248+
public var topMatchesAnyValueField: Bool {
246249
switch top.kind {
247250
case .anyValueFields, .anything: return true
248251
default: return false
249252
}
250253
}
251254

255+
/// Returns true if the path does not have any class projections.
256+
/// For example:
257+
/// returns true for `v**`
258+
/// returns false for `c0`
259+
/// returns false for `**` (because '**' can have any number of class projections)
260+
public var hasNoClassProjection: Bool {
261+
return matches(pattern: Self(.anyValueFields))
262+
}
263+
264+
/// Returns true if the path has at least one class projection.
265+
/// For example:
266+
/// returns false for `v**`
267+
/// returns true for `v**.c0.s1.v**`
268+
/// returns false for `**` (because '**' can have zero class projections)
269+
public var hasClassProjection: Bool {
270+
var p = self
271+
while true {
272+
let (k, _, numBits) = p.top
273+
if k == .root { return false }
274+
if k.isClassField { return true }
275+
p = p.pop(numBits: numBits)
276+
}
277+
}
278+
252279
/// Pops all value field components from the beginning of the path.
253280
/// For example:
254281
/// `s0.e2.3.c4.s1` -> `c4.s1`
@@ -262,7 +289,35 @@ public struct SmallProjectionPath : CustomStringConvertible, CustomReflectable,
262289
p = p.pop(numBits: numBits)
263290
}
264291
}
265-
292+
293+
/// Pops the last class projection and all following value fields from the tail of the path.
294+
/// For example:
295+
/// `s0.e2.3.c4.s1` -> `s0.e2.3`
296+
/// `v**.c1.c4.s1` -> `v**.c1`
297+
/// `c1.**` -> `c1.**` (because it's unknown how many class projections are in `**`)
298+
public func popLastClassAndValuesFromTail() -> SmallProjectionPath {
299+
var p = self
300+
var totalBits = 0
301+
var neededBits = 0
302+
while true {
303+
let (k, _, numBits) = p.top
304+
if k == .root { break }
305+
if k.isClassField {
306+
neededBits = totalBits
307+
totalBits += numBits
308+
} else {
309+
totalBits += numBits
310+
if !k.isValueField {
311+
// k is `anything`
312+
neededBits = totalBits
313+
}
314+
}
315+
p = p.pop(numBits: numBits)
316+
}
317+
if neededBits == 64 { return self }
318+
return SmallProjectionPath(bytes: bytes & ((1 << neededBits) - 1))
319+
}
320+
266321
/// Returns true if this path matches a pattern path.
267322
///
268323
/// Formally speaking:
@@ -460,6 +515,8 @@ extension SmallProjectionPath {
460515
parsing()
461516
merging()
462517
matching()
518+
predicates()
519+
path2path()
463520

464521
func basicPushPop() {
465522
let p1 = SmallProjectionPath(.structField, index: 3)
@@ -569,5 +626,52 @@ extension SmallProjectionPath {
569626
let result = lhs.matches(pattern: rhs)
570627
precondition(result == expect)
571628
}
629+
630+
func predicates() {
631+
testPredicate("v**.c3", \.topMatchesAnyValueField, expect: true)
632+
testPredicate("**", \.topMatchesAnyValueField, expect: true)
633+
testPredicate("s0.c3", \.topMatchesAnyValueField, expect: false)
634+
635+
testPredicate("v**", \.hasNoClassProjection, expect: true)
636+
testPredicate("c0", \.hasNoClassProjection, expect: false)
637+
testPredicate("1", \.hasNoClassProjection, expect: true)
638+
testPredicate("**", \.hasNoClassProjection, expect: false)
639+
640+
testPredicate("v**", \.hasClassProjection, expect: false)
641+
testPredicate("v**.c0.s1.v**", \.hasClassProjection, expect: true)
642+
testPredicate("c0.**", \.hasClassProjection, expect: true)
643+
testPredicate("c0.c1", \.hasClassProjection, expect: true)
644+
testPredicate("ct", \.hasClassProjection, expect: true)
645+
testPredicate("s0", \.hasClassProjection, expect: false)
646+
}
647+
648+
func testPredicate(_ pathStr: String, _ property: (SmallProjectionPath) -> Bool, expect: Bool) {
649+
var parser = StringParser(pathStr)
650+
let path = try! parser.parseProjectionPathFromSIL()
651+
let result = property(path)
652+
precondition(result == expect)
653+
}
654+
655+
func path2path() {
656+
testPath2Path("s0.e2.3.c4.s1", { $0.popAllValueFields() }, expect: "c4.s1")
657+
testPath2Path("v**.c4.s1", { $0.popAllValueFields() }, expect: "c4.s1")
658+
testPath2Path("**", { $0.popAllValueFields() }, expect: "**")
659+
660+
testPath2Path("s0.e2.3.c4.s1.e2.v**.**", { $0.popLastClassAndValuesFromTail() }, expect: "s0.e2.3.c4.s1.e2.v**.**")
661+
testPath2Path("s0.c2.3.c4.s1", { $0.popLastClassAndValuesFromTail() }, expect: "s0.c2.3")
662+
testPath2Path("v**.c*.s1", { $0.popLastClassAndValuesFromTail() }, expect: "v**")
663+
testPath2Path("s1.ct.v**", { $0.popLastClassAndValuesFromTail() }, expect: "s1")
664+
testPath2Path("c0.c1.c2", { $0.popLastClassAndValuesFromTail() }, expect: "c0.c1")
665+
testPath2Path("**", { $0.popLastClassAndValuesFromTail() }, expect: "**")
666+
}
667+
668+
func testPath2Path(_ pathStr: String, _ transform: (SmallProjectionPath) -> SmallProjectionPath, expect: String) {
669+
var parser = StringParser(pathStr)
670+
let path = try! parser.parseProjectionPathFromSIL()
671+
var expectParser = StringParser(expect)
672+
let expectPath = try! expectParser.parseProjectionPathFromSIL()
673+
let result = transform(path)
674+
precondition(result == expectPath)
675+
}
572676
}
573677
}

0 commit comments

Comments
 (0)