2016-06-08 17 views
7

Stavo giocando con il compilatore C# su TryRoslyn di recente, e mi sono imbattuto in uno strano problema in cui un controllo di disuguaglianza si stava trasformando in uno più grande di uno. Ecco il codice di Repro:Perché Roslyn genera un confronto> anziché un! = Uno qui?

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

    private int Foo() => 0; 
} 

e qui è il codice che viene generato dal decompilatore:

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 il link al Repro. Perché Roslyn fa questo; E 'un errore?

Alcune osservazioni che ho fatto dopo aver giocato con il codice per un po ':

  • accade questo solo con l'ultima espressione booleana nella condizione. Ad esempio, se aggiungi un'altra istruzione ||, ciò avverrà solo con l'ultima chiamata a Foo().

  • Inoltre verifica solo con 0, in particolare; se lo sostituisci per 1, o qualche altro numero, non succederà.

risposta

11

Il codice decompilato è errato; questo è un bug nel decompilatore, non nel compilatore. L'IL generato è corretto. Leggere attentamente l'IL. Capisci perché il confronto più grande è corretto e la decompilazione è sbagliata?

Per quanto riguarda il motivo per cui questo accade codegen solo per il lato destro dell'operatore, che non ricordo. Se si vuole andare speleologia nel generatore di codice è qui:

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

Volete metodo EmitIsNonNullOrZero.

+0

OK, penso di vedere qual è il problema; l'opcode è un 'cgt.un', che esegue un confronto senza segno, quindi il codice decompilato dovrebbe in realtà essere' (uint) this.Foo()> (uint) 0' (che è vero per tutti gli interi non zero). Grazie per la segnalazione. –

+0

@JamesKo: Esattamente! –

Problemi correlati