public override Type EvaluateType() { var leftType = LeftExpression.EvaluateType(); var rightType = RightExpression.EvaluateType(); var leftTypeUnboxed = TypeHelper.GetUnderlyingTypeIfNullable(leftType); var rightTypeUnboxed = TypeHelper.GetUnderlyingTypeIfNullable(rightType); var anyNullable = TypeHelper.IsSystemNullableType(leftType) || TypeHelper.IsSystemNullableType(rightType); Type resultedTypeRaw; switch (Operator.Type) { case BinaryOperatorType.Logical: // For logical comparison, we ensure that all operands' type are logical already // The return type is always boolean (logical) if (leftType != typeof(bool) || leftType != typeof(bool?) && rightType != typeof(bool) || rightType != typeof(bool?)) { throw new TranspilerNotSupportedException($"Logical binary operator {Operator} operating must operate on bool types. Actual types: {leftType}, {rightType}"); } return(anyNullable ? TypeHelper.MakeNullableIfNotAlready(typeof(bool)) : typeof(bool)); case BinaryOperatorType.Value: // For value type operator, use the value type coercion table if (!TypeCoersionTables.CoersionTableForValueType.TryGetValue((Operator.Name, leftTypeUnboxed, rightTypeUnboxed), out resultedTypeRaw)) { throw new TranspilerInternalErrorException($"Unexpected use of binary operator {Operator.Name} operating between types {leftTypeUnboxed} and {rightTypeUnboxed}"); } if (resultedTypeRaw == default(Type)) { throw new TranspilerNotSupportedException($"Binary operator {Operator.Name} operating between types {leftTypeUnboxed} and {rightTypeUnboxed}"); } return(anyNullable ? TypeHelper.MakeNullableIfNotAlready(resultedTypeRaw) : resultedTypeRaw); case BinaryOperatorType.Comparison: // For comparison operators, use the equality/inequality type coercion table if (Operator.Name == BinaryOperator.EQ || Operator.Name == BinaryOperator.NEQ) { if (!TypeCoersionTables.CoersionTableEqualityComparison.TryGetValue((leftTypeUnboxed, rightTypeUnboxed), out resultedTypeRaw)) { throw new TranspilerInternalErrorException($"Unexpected use of binary operator {Operator.Name} operating between types {leftTypeUnboxed} and {rightTypeUnboxed}"); } } else { if (!TypeCoersionTables.CoersionTableInequalityComparison.TryGetValue((leftTypeUnboxed, rightTypeUnboxed), out resultedTypeRaw)) { throw new TranspilerInternalErrorException($"Unexpected use of binary operator {Operator.Name} operating between types {leftTypeUnboxed} and {rightTypeUnboxed}"); } } if (resultedTypeRaw == default(Type)) { throw new TranspilerNotSupportedException($"Binary operator {Operator.Name} operating between types {leftTypeUnboxed} and {rightTypeUnboxed}"); } return(anyNullable ? TypeHelper.MakeNullableIfNotAlready(resultedTypeRaw) : resultedTypeRaw); case BinaryOperatorType.Invalid: default: throw new TranspilerInternalErrorException($"Unexpected operator type: {Operator.Type}"); } }