Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

'&&' and '||' operators incompatible between operand type 'bool' and type with implicit conversation to 'bool' #626

Closed
n-shay opened this issue Sep 1, 2022 · 3 comments
Assignees
Labels

Comments

@n-shay
Copy link

n-shay commented Sep 1, 2022

Here is what to include in your request to make sure we implement a solution as quickly as possible.

1. Description

In order to explain the use case, I will track back to how we got there.
I needed a way to evaluate this type of expression SomeBool == 1 or SomeBool != 0. To solve that, we create in runtime a new type that swaps bool properties with BooleanVariable type. That BooleanVariable type has operator overloads to support comparison with int hence the expression above works.

Now, even when the expression is a bit more complex, for example SomeBool1 == 1 && SomeBool2 == 1, it works well. But in doing so it broke the standard usage of logical boolean operators and we run into a parsing exception in an expression like SomeBool1 && SomeBool2.

2. Exception

Exception message:

System.Linq.Dynamic.Core.Exceptions.ParseException
Operator '&&' incompatible with operand types 'BooleanVariable' and 'Boolean'

Stack trace:

   at System.Linq.Dynamic.Core.Parser.ExpressionParser.CheckAndPromoteOperands(Type signatures, TokenId opId, String opName, Expression& left, Expression& right, Int32 errorPos) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 2082
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseAndOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 289
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseOrOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 265
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseLambdaOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 245
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseNullCoalescingOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 232
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseConditionalOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 216
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.Parse(Type resultType, Boolean createParameterCtor) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 154
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(Type delegateType, ParsingConfig parsingConfig, Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicExpressionParser.cs:line 122
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(ParsingConfig parsingConfig, Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicExpressionParser.cs:line 97
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicExpressionParser.cs:line 450
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(Boolean createParameterCtor, Type itType, Type resultType, String expression, Object[] values) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicExpressionParser.cs:line 201
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(Type itType, Type resultType, String expression, Object[] values) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicExpressionParser.cs:line 305
   at Program.Main()

3. Fiddle or Project

Fiddle: https://dotnetfiddle.net/1UNldU

Or code for reference:

using System;
using System.Linq.Dynamic.Core;
					
public class Program
{
	public static void Main()
	{
		var model = new Model{
			SomeBool1 = new BooleanVariable(true),
			SomeBool2 = true
		};
		
		Console.WriteLine(model.SomeBool1 && model.SomeBool2);
		
		var expr = DynamicExpressionParser.ParseLambda(typeof(Model), null, "SomeBool1 && SomeBool2");
		var compiled = expr.Compile();
		var result = compiled.DynamicInvoke(model);
		
		Console.WriteLine(result);
	}
}
			
public class Model 
{
	public BooleanVariable SomeBool1 {get; set;}

	public bool SomeBool2 {get; set;}
}

public readonly struct BooleanVariable : IEquatable<bool>, IEquatable<BooleanVariable>, IConvertible
{
        private readonly bool innerValue;

        public BooleanVariable(bool innerValue)
        {
            this.innerValue = innerValue;
        }

        public override bool Equals(object obj)
        {
            return obj is bool val && this.Equals(val);
        }

        public bool Equals(bool other)
        {
            return this.innerValue == other;
        }

        public bool Equals(BooleanVariable other)
        {
            return this.Equals(other.innerValue);
        }

        public override int GetHashCode()
        {
            return this.innerValue.GetHashCode();
        }

        public static implicit operator bool(BooleanVariable v) => v.innerValue;

        public static implicit operator BooleanVariable(bool b) => new BooleanVariable(b);

        public static bool operator true(BooleanVariable v) => v.innerValue;

        public static bool operator false(BooleanVariable v) => !v.innerValue;

        // comparison to bool
        public static bool operator ==(BooleanVariable a, bool b) => a.innerValue == b;

        public static bool operator !=(BooleanVariable a, bool b) => a.innerValue != b;

        // comparison to int
        public static bool operator ==(BooleanVariable a, int b) => a.innerValue == (b != 0);

        public static bool operator !=(BooleanVariable a, int b) => a.innerValue != (b != 0);

        // comparison to decimal
        public static bool operator ==(BooleanVariable a, decimal b) => a.innerValue == (b != 0);

        public static bool operator !=(BooleanVariable a, decimal b) => a.innerValue != (b != 0);

        // To/from self
        public static bool operator ==(BooleanVariable a, BooleanVariable b) => a.innerValue == b.innerValue;

        public static bool operator !=(BooleanVariable a, BooleanVariable b) => a.innerValue != b.innerValue;

        // see remaining code in Fiddle...
}

4. Any further technical details

Of course, our usage is more complex than that with several types replaced in the same pattern, such as enums for comparison with strings (for example, Color == 'Red').

@StefH StefH added the bug label Sep 3, 2022
@StefH StefH self-assigned this Sep 3, 2022
@StefH
Copy link
Collaborator

StefH commented Apr 27, 2024

Hi @n-shay ,

I've implemented this, see PR #806

@StefH
Copy link
Collaborator

StefH commented Apr 27, 2024

@n-shay
if you have time, can you review the PR?

@n-shay
Copy link
Author

n-shay commented Apr 28, 2024

@n-shay if you have time, can you review the PR?

@StefH Looks great! Thank you for fixing the bug.

@StefH StefH closed this as completed Apr 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

2 participants