2016-06-08 16 views
7

Ostatnio grałem z kompilatorem C# na TryRoslyn, a natknąłem się na dziwny problem, w którym kontrola nierówności została przekształcona w większą niż jedną. Oto kod repro:Dlaczego Roslyn generuje> porównanie zamiast a! = Tutaj?

using System; 
public class C { 
    public void M() { 
     if (Foo() != 0 || Foo() != 0) 
     { 
      Console.WriteLine("Hi!"); 
     } 
    } 

    private int Foo() => 0; 
} 

i tutaj jest kod, który zostanie wygenerowany przez dekompilator:

using System; 
using System.Diagnostics; 
using System.Reflection; 
using System.Runtime.CompilerServices; 
using System.Security; 
using System.Security.Permissions; 
[assembly: AssemblyVersion("0.0.0.0")] 
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] 
[assembly: CompilationRelaxations(8)] 
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] 
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] 
[module: UnverifiableCode] 
public class C 
{ 
    public void M() 
    { 
     bool flag = this.Foo() != 0 || this.Foo() > 0; // this should be an != check 
     if (flag) 
     { 
      Console.WriteLine("Hi!"); 
     } 
    } 
    private int Foo() 
    { 
     return 0; 
    } 
} 

Here's link do Repro. Dlaczego Roslyn to robi; czy to błąd?

Niektóre obserwacje zrobiłem po zabawy z kodem na chwilę:

  • Dzieje się tak tylko z ostatniego logicznej wypowiedzi w stanie. Na przykład, jeśli dodasz inne oświadczenie ||, stanie się to tylko przy ostatnim połączeniu z Foo().

  • To samo dzieje się tylko z 0, konkretnie; jeśli zastąpisz go 1 lub inną liczbą, to się nie stanie.

Odpowiedz

11

Dekompilowany kod jest niepoprawny; to jest błąd w dekompilatorze, a nie w kompilatorze. Wygenerowana IL jest poprawna. Przeczytaj uważnie IL pod numerem. Czy widzisz, dlaczego lepsze niż porównanie jest poprawne, a dekompilacja jest zła?

Co do tego, dlaczego ten kodek występuje tylko po prawej stronie operatora, którego nie pamiętam. Jeśli chcesz iść spelunking w generator kodu jest tutaj:

https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/CodeGen/EmitOperators.cs

Chcesz metodę EmitIsNonNullOrZero.

+0

W porządku, wydaje mi się, że widzę problem; opkodem jest 'cgt.un', który wykonuje unsigned porównania, więc dekompilowany kod powinien naprawdę być' (uint) this.Foo()> (uint) 0' (co jest prawdziwe dla wszystkich niezerowych int). Dziękuję za wskazanie tego. –

+0

@JamesKo: Dokładnie tak! –

Powiązane problemy