|
1 | 1 | using System; |
| 2 | +using System.Collections.Generic; |
2 | 3 | using System.Linq; |
3 | 4 | using System.Reflection; |
4 | 5 |
|
5 | 6 | #if LIGHT_EXPRESSION |
6 | 7 | using static FastExpressionCompiler.LightExpression.Expression; |
7 | 8 | using FastExpressionCompiler.LightExpression; |
| 9 | +using FastExpressionCompiler.LightExpression.ImTools; |
8 | 10 | namespace FastExpressionCompiler.LightExpression.IssueTests; |
9 | 11 | #else |
10 | 12 | using static System.Linq.Expressions.Expression; |
| 13 | +using FastExpressionCompiler.ImTools; |
11 | 14 | namespace FastExpressionCompiler.IssueTests; |
12 | 15 | #endif |
13 | 16 |
|
@@ -35,6 +38,9 @@ public void Run(TestRun t) |
35 | 38 | #if LIGHT_EXPRESSION |
36 | 39 | Eq_complex_lambda_round_trip(t); |
37 | 40 | #endif |
| 41 | + Hash_equal_expressions_have_equal_hashes(t); |
| 42 | + Hash_used_as_dictionary_key(t); |
| 43 | + Hash_used_as_smallmap_key(t); |
38 | 44 | NotEq_different_constants(t); |
39 | 45 | NotEq_different_types(t); |
40 | 46 | NotEq_different_parameters(t); |
@@ -178,6 +184,64 @@ public void Eq_complex_lambda_round_trip(TestContext t) |
178 | 184 | } |
179 | 185 | #endif |
180 | 186 |
|
| 187 | + public void Hash_equal_expressions_have_equal_hashes(TestContext t) |
| 188 | + { |
| 189 | + // Structural equality implies equal hashes (mandatory contract for use as dictionary key). |
| 190 | + var p1 = Parameter(typeof(int), "x"); |
| 191 | + var p2 = Parameter(typeof(int), "y"); // different name — structurally same |
| 192 | + var e1 = Lambda<Func<int, int>>(Add(p1, Constant(1)), p1); |
| 193 | + var e2 = Lambda<Func<int, int>>(Add(p2, Constant(1)), p2); |
| 194 | + t.IsTrue(e1.EqualsTo(e2)); |
| 195 | + t.AreEqual(ExpressionEqualityComparer.GetHashCode(e1), ExpressionEqualityComparer.GetHashCode(e2)); |
| 196 | + |
| 197 | + // Constants |
| 198 | + t.AreEqual(ExpressionEqualityComparer.GetHashCode(Constant(42)), ExpressionEqualityComparer.GetHashCode(Constant(42))); |
| 199 | + |
| 200 | + // Different constants must have different hashes (not guaranteed in general, but these are obviously distinct) |
| 201 | + t.AreNotEqual(ExpressionEqualityComparer.GetHashCode(Constant(1)), ExpressionEqualityComparer.GetHashCode(Constant(2))); |
| 202 | + } |
| 203 | + |
| 204 | + public void Hash_used_as_dictionary_key(TestContext t) |
| 205 | + { |
| 206 | + // Verify that structurally-equal expressions resolve to the same Dictionary bucket. |
| 207 | + var cmp = default(ExpressionEqualityComparer); |
| 208 | + var dict = new Dictionary< |
| 209 | +#if LIGHT_EXPRESSION |
| 210 | + FastExpressionCompiler.LightExpression.Expression, |
| 211 | +#else |
| 212 | + System.Linq.Expressions.Expression, |
| 213 | +#endif |
| 214 | + string>(cmp); |
| 215 | + |
| 216 | + var p1 = Parameter(typeof(int), "x"); |
| 217 | + var e1 = Lambda<Func<int, int>>(Add(p1, Constant(1)), p1); |
| 218 | + dict[e1] = "found"; |
| 219 | + |
| 220 | + var p2 = Parameter(typeof(int), "y"); // different identity/name |
| 221 | + var e2 = Lambda<Func<int, int>>(Add(p2, Constant(1)), p2); |
| 222 | + t.IsTrue(dict.TryGetValue(e2, out var v)); |
| 223 | + t.AreEqual("found", v); |
| 224 | + } |
| 225 | + |
| 226 | + public void Hash_used_as_smallmap_key(TestContext t) |
| 227 | + { |
| 228 | + // Verify lookup via SmallMap8 which uses GetHashCode + Equals internally. |
| 229 | + var p1 = Parameter(typeof(int), "x"); |
| 230 | + var e1 = Lambda<Func<int, int>>(Add(p1, Constant(1)), p1); |
| 231 | + var h1 = ExpressionEqualityComparer.GetHashCode(e1); |
| 232 | + |
| 233 | + var p2 = Parameter(typeof(int), "y"); |
| 234 | + var e2 = Lambda<Func<int, int>>(Add(p2, Constant(1)), p2); |
| 235 | + var h2 = ExpressionEqualityComparer.GetHashCode(e2); |
| 236 | + |
| 237 | + // Structurally equal ⟹ same hash |
| 238 | + t.AreEqual(h1, h2); |
| 239 | + |
| 240 | + // Structurally different ⟹ different hash (for obviously distinct constants) |
| 241 | + var e3 = Lambda<Func<int, int>>(Add(p1, Constant(99)), p1); |
| 242 | + t.AreNotEqual(h1, ExpressionEqualityComparer.GetHashCode(e3)); |
| 243 | + } |
| 244 | + |
181 | 245 | public void NotEq_different_constants(TestContext t) |
182 | 246 | { |
183 | 247 | t.IsFalse(Constant(42).EqualsTo(Constant(43))); |
|
0 commit comments