@@ -83,15 +83,17 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
83
83
// This and all following kinds (we'll add in the future) cannot have a field index.
84
84
case tailElements = 0x07 // (0 << 3) | 0x7 A tail allocated element of a class: syntax `ct`
85
85
case existential = 0x0f // (1 << 3) | 0x7 A concrete value projected out of an existential: synatx 'x'
86
- case anyClassField = 0x17 // (2 << 3) | 0x7 Any class field, including tail elements: syntax `c*`
87
- case anyIndexedElement = 0x1f // (3 << 3) | 0x7 An unknown offset into an array of elements.
86
+ case vectorBase = 0x17 // (2 << 3) | 0x7 The base element of a vector: synatx 'b'
87
+ case anyClassField = 0x1f // (3 << 3) | 0x7 Any class field, including tail elements: syntax `c*`
88
+ case anyIndexedElement = 0x27 // (4 << 3) | 0x7 An unknown offset into an array of elements.
88
89
// There must not be two successive element indices in the path.
89
- case anyValueFields = 0x27 // (4 << 3) | 0x7 Any number of any value fields (struct, tuple, enum): syntax `v**`
90
- case anything = 0x2f // (5 << 3) | 0x7 Any number of any fields: syntax `**`
90
+ case anyValueFields = 0x2f // (5 << 3) | 0x7 Any number of any value fields (struct, tuple, enum): syntax `v**`
91
+ case anything = 0x37 // (6 << 3) | 0x7 Any number of any fields: syntax `**`
91
92
92
93
public var isValueField : Bool {
93
94
switch self {
94
- case . anyValueFields, . structField, . tupleField, . enumCase, . indexedElement, . anyIndexedElement, . existential:
95
+ case . structField, . tupleField, . enumCase, . indexedElement, . existential, . vectorBase,
96
+ . anyValueFields, . anyIndexedElement:
95
97
return true
96
98
case . root, . anything, . anyClassField, . classField, . tailElements:
97
99
return false
@@ -102,7 +104,8 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
102
104
switch self {
103
105
case . anyClassField, . classField, . tailElements:
104
106
return true
105
- case . root, . anything, . anyValueFields, . structField, . tupleField, . enumCase, . indexedElement, . anyIndexedElement, . existential:
107
+ case . root, . anything, . anyValueFields, . structField, . tupleField, . enumCase, . indexedElement,
108
+ . anyIndexedElement, . existential, . vectorBase:
106
109
return false
107
110
}
108
111
}
@@ -140,6 +143,7 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
140
143
case . classField: s = " c \( idx) "
141
144
case . tailElements: s = " ct "
142
145
case . existential: s = " x "
146
+ case . vectorBase: s = " b "
143
147
case . indexedElement: s = " i \( idx) "
144
148
case . anyIndexedElement: s = " i* "
145
149
case . anything: s = " ** "
@@ -398,7 +402,7 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
398
402
return subPath. matches ( pattern: subPattern)
399
403
case . anyIndexedElement:
400
404
return popIndexedElements ( ) . matches ( pattern: subPattern)
401
- case . structField, . tupleField, . enumCase, . classField, . tailElements, . indexedElement, . existential:
405
+ case . structField, . tupleField, . enumCase, . classField, . tailElements, . indexedElement, . existential, . vectorBase :
402
406
let ( kind, index, subPath) = pop ( )
403
407
if kind != patternKind || index != patternIdx { return false }
404
408
return subPath. matches ( pattern: subPattern)
@@ -478,8 +482,18 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
478
482
}
479
483
if ( lhsKind == rhsKind && lhsIdx == rhsIdx) ||
480
484
( lhsKind == . anyClassField && rhsKind. isClassField) ||
481
- ( lhsKind. isClassField && rhsKind == . anyClassField) {
482
- return pop ( numBits: lhsBits) . mayOverlap ( with: rhs. pop ( numBits: rhsBits) )
485
+ ( lhsKind. isClassField && rhsKind == . anyClassField)
486
+ {
487
+ let poppedPath = pop ( numBits: lhsBits)
488
+ let rhsPoppedPath = rhs. pop ( numBits: rhsBits)
489
+ // Check for the case of overlapping the first element of a vector with another element.
490
+ // Note that the index of `.indexedElement` cannot be 0.
491
+ if ( poppedPath. isEmpty && rhsPoppedPath. pop ( ) . kind == . indexedElement) ||
492
+ ( rhsPoppedPath. isEmpty && poppedPath. pop ( ) . kind == . indexedElement)
493
+ {
494
+ return false
495
+ }
496
+ return poppedPath. mayOverlap ( with: rhsPoppedPath)
483
497
}
484
498
return false
485
499
}
@@ -496,7 +510,7 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
496
510
switch lhsKind {
497
511
case . root:
498
512
return rhs
499
- case . classField, . tailElements, . structField, . tupleField, . enumCase, . existential, . indexedElement:
513
+ case . classField, . tailElements, . structField, . tupleField, . enumCase, . existential, . indexedElement, . vectorBase :
500
514
let ( rhsKind, rhsIdx, rhsBits) = rhs. top
501
515
if lhsKind == rhsKind && lhsIdx == rhsIdx {
502
516
return pop ( numBits: lhsBits) . subtract ( from: rhs. pop ( numBits: rhsBits) )
@@ -601,6 +615,8 @@ extension StringParser {
601
615
entries. append ( ( . tailElements, 0 ) )
602
616
} else if consume ( " x " ) {
603
617
entries. append ( ( . existential, 0 ) )
618
+ } else if consume ( " b " ) {
619
+ entries. append ( ( . vectorBase, 0 ) )
604
620
} else if consume ( " c " ) {
605
621
guard let idx = consumeInt ( withWhiteSpace: false ) else {
606
622
try throwError ( " expected class field index " )
@@ -701,7 +717,8 @@ extension SmallProjectionPath {
701
717
. push ( . enumCase, index: 6 )
702
718
. push ( . anyClassField)
703
719
. push ( . tupleField, index: 2 ) )
704
- testParse ( " i3.x.i* " , expect: SmallProjectionPath ( . anyIndexedElement)
720
+ testParse ( " i3.x.b.i* " , expect: SmallProjectionPath ( . anyIndexedElement)
721
+ . push ( . vectorBase)
705
722
. push ( . existential)
706
723
. push ( . indexedElement, index: 3 ) )
707
724
@@ -739,6 +756,8 @@ extension SmallProjectionPath {
739
756
testMerge ( " i* " , " i2 " , expect: " i* " )
740
757
testMerge ( " s0.i*.e3 " , " s0.e3 " , expect: " s0.i*.e3 " )
741
758
testMerge ( " i* " , " v** " , expect: " v** " )
759
+ testMerge ( " s0.b.i1 " , " s0.b.i0 " , expect: " s0.b.i* " )
760
+ testMerge ( " s0.b " , " s0.1 " , expect: " s0.v** " )
742
761
743
762
testMerge ( " ct.s0.e0.v**.c0 " , " ct.s0.e0.v**.c0 " , expect: " ct.s0.e0.v**.c0 " )
744
763
testMerge ( " ct.s0.s0.c0 " , " ct.s0.e0.s0.c0 " , expect: " ct.s0.v**.c0 " )
@@ -813,6 +832,7 @@ extension SmallProjectionPath {
813
832
testMatch ( " s1.v** " , " s0.** " , expect: false )
814
833
testMatch ( " s0.** " , " s0.v** " , expect: false )
815
834
testMatch ( " s0.s1 " , " s0.i*.s1 " , expect: true )
835
+ testMatch ( " s0.b.s1 " , " s0.b.i*.s1 " , expect: true )
816
836
}
817
837
818
838
func testMatch( _ lhsStr: String , _ rhsStr: String , expect: Bool ) {
@@ -847,6 +867,13 @@ extension SmallProjectionPath {
847
867
testOverlap ( " i1 " , " i* " , expect: true )
848
868
testOverlap ( " i1 " , " v** " , expect: true )
849
869
testOverlap ( " s0.i*.s1 " , " s0.s1 " , expect: true )
870
+ testOverlap ( " s0.b.s1 " , " s0.b.i*.s1 " , expect: true )
871
+ testOverlap ( " s0.b.i0.s1 " , " s0.b.i1.s1 " , expect: false )
872
+ testOverlap ( " s0.b.i2.s1 " , " s0.b.i1.s1 " , expect: false )
873
+ testOverlap ( " s0.b.s1 " , " s0.b.i0.s1 " , expect: true )
874
+ testOverlap ( " s0.b " , " s0.b.i1 " , expect: false )
875
+ testOverlap ( " s0.b.i1 " , " s0.b " , expect: false )
876
+ testOverlap ( " s0.b.i1 " , " s0 " , expect: true )
850
877
}
851
878
852
879
func testOverlap( _ lhsStr: String , _ rhsStr: String , expect: Bool ) {
@@ -889,7 +916,7 @@ extension SmallProjectionPath {
889
916
}
890
917
891
918
func path2path( ) {
892
- testPath2Path ( " s0.e2.3.c4.s1 " , { $0. popAllValueFields ( ) } , expect: " c4.s1 " )
919
+ testPath2Path ( " s0.b. e2.3.c4.s1 " , { $0. popAllValueFields ( ) } , expect: " c4.s1 " )
893
920
testPath2Path ( " v**.c4.s1 " , { $0. popAllValueFields ( ) } , expect: " c4.s1 " )
894
921
testPath2Path ( " ** " , { $0. popAllValueFields ( ) } , expect: " ** " )
895
922
0 commit comments