Search code examples
c#compiler-constructionroslynexpression-trees

How does the C# compiler resolve types before applying a binary operator?


I'm working on a typed scripting language backed by C# Expression Trees. I'm stuck on one issue around proper type conversion with binary operators. Here is an example of the behavior I'm trying to mimic: (The conversion rules should be the same C#'s compiler)

var value = "someString" + 10; // yields a string
var value = 5 + "someString"; // also yields a string, I don't know why
var x = 10f + 10; // yields a float
var y = 10 + 10f; // also yields a float, I don't know why

How does the C# compiler know to call ToString() on the integer in the first line and to convert the integers to floats in both directions when adding with a float? Are these conversion rules hard coded?

My compiler basically works like this now for binary operators:

Expression Visit(Type tryToConvertTo, ASTNode node) {
    // details don't matter. If tryToConvertTo is not null
    // the resulting expression is cast to that type if not already of that type
}

// very simplified but this is the gist of it
Expression VisitBinaryOperator(Operator operator) {
    Expression lhs = Visit(null, operator.lhs);
    Expression rhs = Visit(lhs, operator.rhs); // wrong, but mostly works unless we hit one of the example cases or something similar
    switch(operator.opType) {
        case OperatorType.Add: {
            return Expression.Add(lhs, rhs);
        }
        // other operators / error handling etc omitted
    }
}

I know always accepting the left hand side's type is wrong, but I have no idea what the proper approach to resolving the example expressions might be other than hard coding the rules for primitive types.

If anyone can point me in the right direction I'd be very grateful!


Solution

  • This kind of questions can only be answered accurately via the language specification.

    + operator with string and int operands

    https://github.com/dotnet/csharpstandard/blob/draft-v7/standard/expressions.md#1195-addition-operator

    Here, under String concatenation you will see:

    These overloads of the binary + operator perform string concatenation. If an operand of string concatenation is null, an empty string is substituted. Otherwise, any non-string operand is converted to its string representation by invoking the virtual ToString method inherited from type object. If ToString returns null, an empty string is substituted.

    + operator with float and int operands

    The int here is implicitly converted to float, specified here:

    https://github.com/dotnet/csharpstandard/blob/draft-v7/standard/conversions.md#1023-implicit-numeric-conversions