This project is read-only.
1
Vote

Semantic Model does not return symbol for built-in operator && and ||

description

The code below produces the output
Bool symbol: Unknown
User Defined symbol: S.operator |(S, S)
whereas I expected
Bool symbol: bool.operator |(bool, bool)
User Defined symbol: S.operator |(S, S)
Apparently, Roslyn's semantic model does not resolve the built-in logical operators, whereas user-defined ones are resolved correctly. This is in contrast to all other operators that I've checked. Roslyn provides a symbol for all of these built-in operators (such as +, -, &, |, ...). I know that under the hoods, there is no method for && and || for built-in types. But it's an annoying inconsistency because you actually do get symbols for other built-in operators. For instance, when you resolve the symbol for 1 + 1, you get a SynthesizedIntrinsicOperatorSymbol representing Method System.Int32 System.Int32.op_Addition(System.Int32 left, System.Int32 right).

I don't really see a reason why that shouldn't also be the case for operators || and &&. Here's the code that I used to produce the above output (using the latest Nuget packages of Roslyn, version 0.7.4091001-beta, published 9/29/14):
namespace RoslynSymbolTest
{
    using System;
    using System.Linq;
    using Microsoft.CodeAnalysis;
    using Microsoft.CodeAnalysis.CSharp;
    using Microsoft.CodeAnalysis.CSharp.Syntax;

    internal class Program
    {
        private static void Main(string[] args)
        {
            var code = @"
struct S 
{
    public static S operator&(S s1, S s2) { return s2; }
    public static S operator|(S s1, S s2) { return s1; }
    public static bool operator true(S s) { return false; }
    public static bool operator false(S s) { return false; }
}

class X 
{ 
    bool BoolTest() 
    { 
        return true || false;
    }

    S UserDefinedTest() 
    { 
        S s;
        return s || s;
    }
}";

            var tree = SyntaxFactory.ParseSyntaxTree(code);
            var compilation = CSharpCompilation.Create("test", new[] { tree },
                new[] { new MetadataFileReference(typeof(object).Assembly.Location) },
                new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            var model = compilation.GetSemanticModel(tree);

            var boolLogicalAnd = tree.GetRoot().DescendantNodes().OfType<BinaryExpressionSyntax>().First();
            var userDefinedLogicalAnd = tree.GetRoot().DescendantNodes().OfType<BinaryExpressionSyntax>().Skip(1).First();

            var boolSymbol = model.GetSymbolInfo(boolLogicalAnd);
            var userDefinedSymbol = model.GetSymbolInfo(userDefinedLogicalAnd);

            Console.WriteLine("Bool symbol: {0}", boolSymbol.Symbol == null ? "Unknown" : boolSymbol.Symbol.ToDisplayString());
            Console.WriteLine("User Defined symbol: {0}", userDefinedSymbol.Symbol ==null ? "Unknown" : userDefinedSymbol.Symbol.ToDisplayString());
        }
    }
}

comments

nmgafter wrote Oct 27, 2014 at 6:47 PM

According to the language spec (7.12 Conditional logical operators) the operator is selected by using overload resolution. Therefore the Semantic Model should reflect that. You should see something like
Bool symbol: operator |(Boolean, Boolean)
User Defined symbol: S.operator |(S, S)

Expandable wrote Oct 27, 2014 at 7:25 PM

@nmgafter: Thanks for your comment. Of course, I completely messed up the expected output in the original version of my bug report. That has been fixed.

What I actually expect is something like the following, as you correctly noted:
Bool symbol: bool.operator |(bool, bool)
User Defined symbol: S.operator |(S, S)
What I mistakenly wrote previously (note the wrong argument types and the wrong class name):
Bool symbol: Int32.operator |(S, S)
User Defined symbol: S.operator |(S, S)