@@ -83,15 +83,17 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
8383 // This and all following kinds (we'll add in the future) cannot have a field index.
8484 case tailElements = 0x07 // (0 << 3) | 0x7 A tail allocated element of a class: syntax `ct`
8585 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.
8889 // 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 `**`
9192
9293 public var isValueField : Bool {
9394 switch self {
94- case . anyValueFields, . structField, . tupleField, . enumCase, . indexedElement, . anyIndexedElement, . existential:
95+ case . structField, . tupleField, . enumCase, . indexedElement, . existential, . vectorBase,
96+ . anyValueFields, . anyIndexedElement:
9597 return true
9698 case . root, . anything, . anyClassField, . classField, . tailElements:
9799 return false
@@ -102,7 +104,8 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
102104 switch self {
103105 case . anyClassField, . classField, . tailElements:
104106 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:
106109 return false
107110 }
108111 }
@@ -140,6 +143,7 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
140143 case . classField: s = " c \( idx) "
141144 case . tailElements: s = " ct "
142145 case . existential: s = " x "
146+ case . vectorBase: s = " b "
143147 case . indexedElement: s = " i \( idx) "
144148 case . anyIndexedElement: s = " i* "
145149 case . anything: s = " ** "
@@ -398,7 +402,7 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
398402 return subPath. matches ( pattern: subPattern)
399403 case . anyIndexedElement:
400404 return popIndexedElements ( ) . matches ( pattern: subPattern)
401- case . structField, . tupleField, . enumCase, . classField, . tailElements, . indexedElement, . existential:
405+ case . structField, . tupleField, . enumCase, . classField, . tailElements, . indexedElement, . existential, . vectorBase :
402406 let ( kind, index, subPath) = pop ( )
403407 if kind != patternKind || index != patternIdx { return false }
404408 return subPath. matches ( pattern: subPattern)
@@ -478,8 +482,18 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
478482 }
479483 if ( lhsKind == rhsKind && lhsIdx == rhsIdx) ||
480484 ( 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)
483497 }
484498 return false
485499 }
@@ -496,7 +510,7 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
496510 switch lhsKind {
497511 case . root:
498512 return rhs
499- case . classField, . tailElements, . structField, . tupleField, . enumCase, . existential, . indexedElement:
513+ case . classField, . tailElements, . structField, . tupleField, . enumCase, . existential, . indexedElement, . vectorBase :
500514 let ( rhsKind, rhsIdx, rhsBits) = rhs. top
501515 if lhsKind == rhsKind && lhsIdx == rhsIdx {
502516 return pop ( numBits: lhsBits) . subtract ( from: rhs. pop ( numBits: rhsBits) )
@@ -601,6 +615,8 @@ extension StringParser {
601615 entries. append ( ( . tailElements, 0 ) )
602616 } else if consume ( " x " ) {
603617 entries. append ( ( . existential, 0 ) )
618+ } else if consume ( " b " ) {
619+ entries. append ( ( . vectorBase, 0 ) )
604620 } else if consume ( " c " ) {
605621 guard let idx = consumeInt ( withWhiteSpace: false ) else {
606622 try throwError ( " expected class field index " )
@@ -701,7 +717,8 @@ extension SmallProjectionPath {
701717 . push ( . enumCase, index: 6 )
702718 . push ( . anyClassField)
703719 . push ( . tupleField, index: 2 ) )
704- testParse ( " i3.x.i* " , expect: SmallProjectionPath ( . anyIndexedElement)
720+ testParse ( " i3.x.b.i* " , expect: SmallProjectionPath ( . anyIndexedElement)
721+ . push ( . vectorBase)
705722 . push ( . existential)
706723 . push ( . indexedElement, index: 3 ) )
707724
@@ -739,6 +756,8 @@ extension SmallProjectionPath {
739756 testMerge ( " i* " , " i2 " , expect: " i* " )
740757 testMerge ( " s0.i*.e3 " , " s0.e3 " , expect: " s0.i*.e3 " )
741758 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** " )
742761
743762 testMerge ( " ct.s0.e0.v**.c0 " , " ct.s0.e0.v**.c0 " , expect: " ct.s0.e0.v**.c0 " )
744763 testMerge ( " ct.s0.s0.c0 " , " ct.s0.e0.s0.c0 " , expect: " ct.s0.v**.c0 " )
@@ -813,6 +832,7 @@ extension SmallProjectionPath {
813832 testMatch ( " s1.v** " , " s0.** " , expect: false )
814833 testMatch ( " s0.** " , " s0.v** " , expect: false )
815834 testMatch ( " s0.s1 " , " s0.i*.s1 " , expect: true )
835+ testMatch ( " s0.b.s1 " , " s0.b.i*.s1 " , expect: true )
816836 }
817837
818838 func testMatch( _ lhsStr: String , _ rhsStr: String , expect: Bool ) {
@@ -847,6 +867,13 @@ extension SmallProjectionPath {
847867 testOverlap ( " i1 " , " i* " , expect: true )
848868 testOverlap ( " i1 " , " v** " , expect: true )
849869 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 )
850877 }
851878
852879 func testOverlap( _ lhsStr: String , _ rhsStr: String , expect: Bool ) {
@@ -889,7 +916,7 @@ extension SmallProjectionPath {
889916 }
890917
891918 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 " )
893920 testPath2Path ( " v**.c4.s1 " , { $0. popAllValueFields ( ) } , expect: " c4.s1 " )
894921 testPath2Path ( " ** " , { $0. popAllValueFields ( ) } , expect: " ** " )
895922
0 commit comments