@@ -61,6 +61,7 @@ public struct ExprNode
6161 private const int CountShift = 16 ;
6262 private const ulong IndexMask = 0xFFFF ;
6363 private const ulong KindMask = 0x0F ;
64+ private const byte NextReservedFlag = 0x4 ;
6465 private const byte NextPointsParentFlag = 0x8 ;
6566 private const ulong TagMask = 0xFFUL << TagShift ;
6667 private const ulong NextMask = IndexMask << NextShift ;
@@ -93,7 +94,7 @@ public struct ExprNode
9394
9495 internal bool IsParentLink => ( Flags & NextPointsParentFlag ) != 0 ;
9596
96- internal bool HasNextLink => ( _data & NextMask ) != 0 || IsParentLink ;
97+ internal bool HasNextLink => ( _data & NextMask ) != 0 || ( Flags & ( NextPointsParentFlag | NextReservedFlag ) ) != 0 ;
9798
9899 /// <summary>Gets the number of direct children linked from this node.</summary>
99100 public int ChildCount => ( int ) ( ( _data >> CountShift ) & IndexMask ) ;
@@ -117,16 +118,14 @@ internal ExprNode(Type type, object obj, ExpressionType nodeType, ExprNodeKind k
117118 internal void SetNextSiblingIdx ( int nextIdx )
118119 {
119120 _data = ( _data & KeepWithoutNextMask ) | ( ( ulong ) ( ushort ) nextIdx << NextShift ) ;
120- if ( IsParentLink )
121- SetFlags ( ( byte ) ( Flags & ~ NextPointsParentFlag ) ) ;
121+ SetFlags ( ( byte ) ( Flags & ~ ( NextPointsParentFlag | NextReservedFlag ) ) ) ;
122122 }
123123
124124 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
125125 internal void SetParentIdx ( int parentIdx )
126126 {
127127 _data = ( _data & KeepWithoutNextMask ) | ( ( ulong ) ( ushort ) parentIdx << NextShift ) ;
128- if ( ! IsParentLink )
129- SetFlags ( ( byte ) ( Flags | NextPointsParentFlag ) ) ;
128+ SetFlags ( ( byte ) ( ( Flags | NextPointsParentFlag ) & ~ NextReservedFlag ) ) ;
130129 }
131130
132131 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
@@ -145,7 +144,10 @@ internal bool IsExpression() =>
145144 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
146145 internal bool HasFlag ( byte flag ) => ( Flags & flag ) != 0 ;
147146
148- internal byte CopyableFlags => ( byte ) ( Flags & ~ NextPointsParentFlag ) ;
147+ internal byte CopyableFlags => ( byte ) ( Flags & ~ ( NextPointsParentFlag | NextReservedFlag ) ) ;
148+
149+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
150+ internal void ReserveAsLinked ( ) => SetFlags ( ( byte ) ( Flags | NextReservedFlag ) ) ;
149151
150152 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
151153 private void SetFlags ( byte flags )
@@ -1305,10 +1307,19 @@ private int CloneChild(int index)
13051307 if ( node . HasNextLink )
13061308 return AddNodeReference ( index ) ;
13071309
1308- node . SetParentIdx ( index ) ; // reserve first use so repeated child arguments in the same parent become references
1310+ ReserveChildLinkForReuse ( ref node ) ;
13091311 return index ;
13101312 }
13111313
1314+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
1315+ private static void ReserveChildLinkForReuse ( ref ExprNode node )
1316+ {
1317+ // Mark the node as "already linked" before wiring siblings/parent on the enclosing AddNode call.
1318+ // This ensures that repeated use of the same index in a single parent (e.g. Add(x, x))
1319+ // keeps the first occurrence in-place and emits a reference node for later occurrences.
1320+ node . ReserveAsLinked ( ) ;
1321+ }
1322+
13121323 private ChildList CloneChildren ( int [ ] children )
13131324 {
13141325 ChildList cloned = default ;
0 commit comments