From 8ae2d111c5c55990204cab8e4c527a3cf2bd266c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 6 May 2026 10:08:01 +0000
Subject: [PATCH 1/8] Initial plan
From 176af12bd97265839c423d152c76058cf5a77128 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 6 May 2026 10:16:19 +0000
Subject: [PATCH 2/8] feat: add structural equality for flat expressions
Agent-Logs-Url: https://github.com/dadhi/FastExpressionCompiler/sessions/8bcf787b-adee-4401-8721-4587fe7cb997
Co-authored-by: dadhi <39516+dadhi@users.noreply.github.com>
---
.../FlatExpression.cs | 377 +++++++++++++++++-
.../LightExpressionTests.cs | 57 ++-
2 files changed, 432 insertions(+), 2 deletions(-)
diff --git a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs
index ad07b3fe..df0f6aa4 100644
--- a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs
+++ b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs
@@ -173,7 +173,7 @@ public LambdaClosureParameterUsage(ushort lambdaIdx, ushort parameterIdx, ushort
}
/// Stores an expression tree as flat nodes plus separate closure constants.
-public struct ExprTree
+public struct ExprTree : IEquatable
{
private static readonly object ClosureConstantMarker = new();
private const byte ParameterByRefFlag = 1;
@@ -709,6 +709,27 @@ public SysExpr ToExpression() =>
[RequiresUnreferencedCode(FastExpressionCompiler.LightExpression.Trimming.Message)]
public LightExpression ToLightExpression() => FastExpressionCompiler.LightExpression.FromSysExpressionConverter.ToLightExpression(ToExpression());
+ /// Structurally compares two flat expression trees.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool Equals(ExprTree other) =>
+ new StructuralComparer().Eq(this, other);
+
+ /// Structurally compares this tree with another object.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override bool Equals(object obj) =>
+ obj is ExprTree other && Equals(other);
+
+ /// Computes a content-addressable hash for the flat expression tree.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override int GetHashCode() =>
+ new StructuralComparer().Hash(this);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator ==(ExprTree left, ExprTree right) => left.Equals(right);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator !=(ExprTree left, ExprTree right) => !left.Equals(right);
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, int child) =>
AddNode(type, obj, nodeType, ExprNodeKind.Expression, 0, CloneChild(child));
@@ -1606,6 +1627,360 @@ private static bool Contains(ref SmallList
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ushort ToStoredUShortIdx(int idx) => checked((ushort)idx);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static object ReadInlineConstantValue(Type type, uint data)
+ {
+ if (type.IsEnum)
+ return Enum.ToObject(type, Type.GetTypeCode(Enum.GetUnderlyingType(type)) switch
+ {
+ TypeCode.Byte => (object)(byte)data,
+ TypeCode.SByte => (object)(sbyte)(byte)data,
+ TypeCode.Char => (object)(char)(ushort)data,
+ TypeCode.Int16 => (object)(short)(ushort)data,
+ TypeCode.UInt16 => (object)(ushort)data,
+ TypeCode.Int32 => (object)(int)data,
+ TypeCode.UInt32 => (object)data,
+ var tc => FlatExpressionThrow.UnsupportedInlineConstantType