Considérer:
using System;
public class Test
{
enum State : sbyte { OK = 0, BUG = -1 }
static void Main(string[] args)
{
var s = new State[1, 1];
s[0, 0] = State.BUG;
State a = s[0, 0];
Console.WriteLine(a == s[0, 0]); // False
}
}
Comment peut-on l'expliquer? Il se produit dans les versions de débogage dans Visual Studio 2015 lors de l'exécution dans le JIT x86. Une version publiée ou exécutée dans le JIT x64 affiche True comme prévu.
Pour reproduire à partir de la ligne de commande:
csc Test.cs /platform:x86 /debug
(/debug:pdbonly
, /debug:portable
et /debug:full
reproduit également.)
Vous avez trouvé un bogue de génération de code dans la gigue .NET 4 x86. C'est très inhabituel, il échoue uniquement lorsque le code n'est pas optimisé. Le code machine ressemble à ceci:
State a = s[0, 0];
013F04A9 Push 0 ; index 2 = 0
013F04AB mov ecx,dword ptr [ebp-40h] ; s[] reference
013F04AE xor edx,edx ; index 1 = 0
013F04B0 call 013F0058 ; eax = s[0, 0]
013F04B5 mov dword ptr [ebp-4Ch],eax ; $temp1 = eax
013F04B8 movsx eax,byte ptr [ebp-4Ch] ; convert sbyte to int
013F04BC mov dword ptr [ebp-44h],eax ; a = s[0, 0]
Console.WriteLine(a == s[0, 0]); // False
013F04BF mov eax,dword ptr [ebp-44h] ; a
013F04C2 mov dword ptr [ebp-50h],eax ; $temp2 = a
013F04C5 Push 0 ; index 2 = 0
013F04C7 mov ecx,dword ptr [ebp-40h] ; s[] reference
013F04CA xor edx,edx ; index 1 = 0
013F04CC call 013F0058 ; eax = s[0, 0]
013F04D1 mov dword ptr [ebp-54h],eax ; $temp3 = eax
; <=== Bug here!
013F04D4 mov eax,dword ptr [ebp-50h] ; a == s[0, 0]
013F04D7 cmp eax,dword ptr [ebp-54h]
013F04DA sete cl
013F04DD movzx ecx,cl
013F04E0 call 731C28F4
Une affaire de plodding avec beaucoup de temporaires et de duplication de code, c'est normal pour le code non optimisé. L'instruction à 013F04B8 est notable, c'est là que la conversion nécessaire de sbyte en entier 32 bits se produit. La fonction d'assistance de getter de tableau a renvoyé 0x0000000FF, égal à State.BUG, et cela doit être converti en -1 (0xFFFFFFFF) avant que la valeur puisse être comparée. L'instruction MOVSX est une instruction Sign eXtension.
La même chose se reproduit à 013F04CC, mais cette fois il y a non instruction MOVSX pour effectuer la même conversion. C'est là que les puces tombent, l'instruction CMP compare 0xFFFFFFFF avec 0x000000FF et c'est faux. Il s'agit donc d'une erreur d'omission, le générateur de code n'a pas réussi à émettre à nouveau MOVSX pour effectuer la même conversion de sbyte en int.
Ce qui est particulièrement inhabituel à propos de ce bogue, c'est que cela fonctionne correctement lorsque vous activez l'optimiseur, il sait maintenant utiliser MOVSX dans les deux cas.
La raison probable pour laquelle ce bogue n'a pas été détecté pendant si longtemps est l'utilisation de sbyte comme type de base de l'énumération. Assez rare à faire. L'utilisation d'un tableau multidimensionnel est également essentielle, la combinaison est fatale.
Sinon, je dirais un bug assez critique. Il est difficile de deviner à quel point cela peut être répandu, je n'ai que la gigue 4.6.1 x86 à tester. La gigue x64 et 3.5 x86 génèrent un code très différent et évitent ce bug. La solution de contournement temporaire pour continuer est de supprimer sbyte comme type de base enum et de le laisser par défaut, int , donc aucune extension de signe n'est nécessaire.
Vous pouvez déposer le bogue sur connect.Microsoft.com, un lien vers ce Q + A devrait être suffisant pour leur dire tout ce qu'ils doivent savoir. Faites-moi savoir si vous ne voulez pas prendre le temps et je m'en occuperai.
Prenons la déclaration d'OP:
enum State : sbyte { OK = 0, BUG = -1 }
Comme le bogue ne se produit que lorsque BUG
est négatif (de -128 à -1) et que State est une énumération de octet signé j'ai commencé à supposer qu'il y avait un problème de transtypage quelque part.
Si vous exécutez ceci:
Console.WriteLine((sbyte)s[0, 0]);
Console.WriteLine((sbyte)State.BUG);
Console.WriteLine(s[0, 0]);
unchecked
{
Console.WriteLine((byte) State.BUG);
}
il affichera:
255
-1
PUNAISE
255
Pour une raison que j'ignore(à partir de maintenant) s[0, 0]
est converti en octet avant l'évaluation et c'est pourquoi il prétend que a == s[0,0]
c'est faux.