2016-06-08 10 views
7

Yakın zamanda TryRoslyn'de C# derleyicisi ile oynuyordum ve bir eşitsizlik kontrolünün birden fazlaya dönüştürüldüğü garip bir soruna rastladım. İşte üreme kodu:Neden Roslyn, burada bir> karşılaştırma yerine> bir karşılaştırma üretiyor?

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

    private int Foo() => 0; 
} 

ve burada decompiler tarafından oluşturulan alır koddur: repro için

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 bağlantı. Roslyn bunu neden yapıyor? bu bir hata mı?

Bazı gözlemler Bir süre koduyla etrafında oynadıktan sonra yaptık:

  • Bu yalnızca durumda son boolean ifade ile olur. Örneğin, başka bir || bildirimi eklerseniz, yalnızca son çağrı Foo() ile gerçekleşir.

  • Ayrıca özellikle 0 ile olur; 1 veya başka bir sayı için değiştirirseniz, bu olmaz.

cevap

11

Decompiled kodu yanlış; Bu derleyici değil, decompiler bir hatadır. Üretilen IL doğrudur. IL'yi dikkatlice okuyunuz. Büyük karşılaştırmaların neden doğru olduğunu ve dekompozisyonun neden yanlış olduğunu görüyor musunuz?

Bu kodgenin neden yalnızca operatörün sağ tarafı için gerçekleştiğine ilişkin olarak, hatırlamıyorum. Eğer kodu jeneratörü mağaracılık gitmek istiyorsanız o burada:

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

yöntemden EmitIsNonNullOrZero istiyorum.

+0

Tamam, sanırım sorunun ne olduğunu görüyorum; opcode unsigned bir karşılaştırma yapar bir 'cgt.un', bu yüzden decompiled kodu gerçekten '(uint) bu olmalıdır.Foo()> (uint) 0' (tüm sıfır olmayan inters için doğru olan). Bunu işaret ettiğin için teşekkürler. –

+0

@JamesKo: Kesinlikle doğru! –