@@ -229,77 +229,18 @@ internal static BinaryExpression MakeBinaryExpression(Address left, Address righ
229229 left = AdjustBooleanConstant ( left , rightType ) ;
230230 right = AdjustBooleanConstant ( right , leftType ) ;
231231
232- // For comparison, bitwise, and arithmetic operations, convert enums to handle type mismatches
233- // This handles cases where an enum with a non-int underlying type (e.g., byte, long) is used
234- // with an int constant loaded from IL
235- if ( IsComparisonOperation ( expressionType ) || IsBitwiseOperation ( expressionType ) || IsArithmeticOperation ( expressionType ) )
236- {
237- // Determine if we're dealing with long enums
238- var leftIsLongEnum = left . Type . IsEnum && ( left . Type . GetEnumUnderlyingType ( ) == typeof ( long ) || left . Type . GetEnumUnderlyingType ( ) == typeof ( ulong ) ) ;
239- var rightIsLongEnum = right . Type . IsEnum && ( right . Type . GetEnumUnderlyingType ( ) == typeof ( long ) || right . Type . GetEnumUnderlyingType ( ) == typeof ( ulong ) ) ;
240-
241- var hasLongEnum = leftIsLongEnum || rightIsLongEnum ;
242-
243- left = ConvertEnumExpressionToInt ( left ) ;
244- right = ConvertEnumExpressionToInt ( right ) ;
245-
246- // Ensure type compatibility after conversions
247- if ( ! hasLongEnum )
248- {
249- // For byte/short enums, ensure both are int
250- if ( left . Type == typeof ( byte ) || left . Type == typeof ( sbyte ) || left . Type == typeof ( short ) || left . Type == typeof ( ushort ) )
251- left = Expression . Convert ( left , typeof ( int ) ) ;
252- if ( right . Type == typeof ( byte ) || right . Type == typeof ( sbyte ) || right . Type == typeof ( short ) || right . Type == typeof ( ushort ) )
253- right = Expression . Convert ( right , typeof ( int ) ) ;
254- }
255- }
256- else
257- {
258- left = ConvertEnumExpressionToUnderlyingType ( left ) ;
259- right = ConvertEnumExpressionToUnderlyingType ( right ) ;
260- }
232+ // Convert enums to their appropriate type for operations
233+ // This uses ConvertEnumExpressionToInt which handles:
234+ // - byte/short enums -> int
235+ // - long/ulong enums -> long/ulong
236+ // - Optimizes Convert(intConstant, long) -> longConstant
237+ // - Handles Convert(Convert(enum, byte), int) -> Convert(enum, int)
238+ left = ConvertEnumExpressionToInt ( left ) ;
239+ right = ConvertEnumExpressionToInt ( right ) ;
261240
262241 return Expression . MakeBinary ( expressionType , left , right ) ;
263242 }
264243
265- static bool IsComparisonOperation ( ExpressionType expressionType )
266- {
267- return expressionType == ExpressionType . Equal ||
268- expressionType == ExpressionType . NotEqual ||
269- expressionType == ExpressionType . LessThan ||
270- expressionType == ExpressionType . LessThanOrEqual ||
271- expressionType == ExpressionType . GreaterThan ||
272- expressionType == ExpressionType . GreaterThanOrEqual ;
273- }
274-
275- static bool IsBitwiseOperation ( ExpressionType expressionType )
276- {
277- return expressionType == ExpressionType . And ||
278- expressionType == ExpressionType . Or ||
279- expressionType == ExpressionType . ExclusiveOr ;
280- }
281-
282- static bool IsArithmeticOperation ( ExpressionType expressionType )
283- {
284- return expressionType == ExpressionType . Add ||
285- expressionType == ExpressionType . Subtract ||
286- expressionType == ExpressionType . Multiply ||
287- expressionType == ExpressionType . Divide ||
288- expressionType == ExpressionType . Modulo ;
289- }
290-
291- static Expression UnwrapConstantConversion ( Expression expression )
292- {
293- // If this is Convert(constant, targetType), unwrap it back to the constant
294- if ( expression is UnaryExpression unary &&
295- unary . NodeType == ExpressionType . Convert &&
296- unary . Operand is ConstantExpression )
297- {
298- return unary . Operand ;
299- }
300- return expression ;
301- }
302-
303244 internal static Expression ConvertEnumExpressionToInt ( Expression expression )
304245 {
305246 // If the expression is already an int, return as-is
@@ -331,6 +272,21 @@ internal static Expression ConvertEnumExpressionToInt(Expression expression)
331272 return Expression . Convert ( operand , typeof ( int ) ) ;
332273 }
333274
275+ // Optimize Convert(Convert(X, byte/short), Y) when going through unnecessary intermediate conversions
276+ // This happens with operations like NOT that return int but IL converts to byte before enum
277+ if ( operand is UnaryExpression innerUnary && innerUnary . NodeType == ExpressionType . Convert )
278+ {
279+ var innerOperand = innerUnary . Operand ;
280+ // If we're converting from int through byte/short, skip the intermediate conversion
281+ if ( innerOperand . Type == typeof ( int ) &&
282+ ( innerUnary . Type == typeof ( byte ) || innerUnary . Type == typeof ( sbyte ) ||
283+ innerUnary . Type == typeof ( short ) || innerUnary . Type == typeof ( ushort ) ) )
284+ {
285+ // Keep just the inner operand, let the outer conversion happen
286+ return Expression . Convert ( innerOperand , expression . Type ) ;
287+ }
288+ }
289+
334290 // Optimize Convert(intConstant, long) to a direct long constant
335291 if ( operand is ConstantExpression constant &&
336292 constant . Type == typeof ( int ) &&
@@ -347,7 +303,14 @@ internal static Expression ConvertEnumExpressionToInt(Expression expression)
347303 internal static Expression ConvertEnumExpressionToUnderlyingType ( Expression expression )
348304 {
349305 if ( expression . Type . IsEnum )
350- return Expression . Convert ( expression , expression . Type . GetEnumUnderlyingType ( ) ) ;
306+ {
307+ var underlyingType = expression . Type . GetEnumUnderlyingType ( ) ;
308+ // C# promotes byte/sbyte/short/ushort enums to int for operations
309+ // Only long/ulong stay as their underlying type
310+ if ( underlyingType == typeof ( long ) || underlyingType == typeof ( ulong ) )
311+ return Expression . Convert ( expression , underlyingType ) ;
312+ return Expression . Convert ( expression , typeof ( int ) ) ;
313+ }
351314
352315 return expression ;
353316 }
0 commit comments