Découverte d'un bug avec MSVC

Pour une fois, je peux me plaindre du compilateur !

Aujourd’hui, j’ai découvert un bug dans MSVC, le compilateur de Microsoft fourni par Visual Studio. Et tout a commencé par un commentaire de @BIOoOAG sur le tutoriel C++. Pendant qu’il lisait le chapitre sur les lambdas, il nous a écrit un message signalant que le code suivant produisait chez lui deux warnings.

#include <iostream>

int main()
{
    bool const a_boolean { true };
    int const an_integer { 42 };
    double const a_real { 3.1415 };

    auto lambda = [a_boolean, an_integer, a_real]() -> void
    {
        std::cout << "Boolean is " << std::boolalpha << a_boolean << "." << std::endl;
        std::cout << "Integer is " << an_integer << "." << std::endl;
        std::cout << "Real is " << a_real << "." << std::endl;
    };

    lambda();
    return 0;
}

Avec MinGW, il avait les deux warnings suivants.

warning: lambda capture 'a_boolean' is not required to be capture for this use. [-Wunused-lambda-capture]
warning: lambda capture 'an_integer' is not required to be capture for this use. [-Wunused-lambda-capture]

Bizarre, avec Visual Studio je n’ai jamais eu ce problème. Comme mes recherches ne donnent rien, je me décide à demander sur SO. La réponse tombe : MinGW et GCC ont raison, MSVC ne respecte pas la norme. En effet, le code suivant, valide, ne compile pas avec le compilateur de Microsoft.

#include <iostream>

int main()
{
    bool const a_boolean { true };
    int const an_integer { 42 };
    double const a_real { 3.1415 };

    // Notez que a_boolean et an_integer ne sont plus explicitement capturés.
    auto lambda = [a_real]() -> void
    {
        std::cout << "Boolean is " << std::boolalpha << a_boolean << "." << std::endl;
        std::cout << "Integer is " << an_integer << "." << std::endl;
        std::cout << "Real is " << a_real << "." << std::endl;
    };

    lambda();
    return 0;
}

La deuxième réponse à cette question dévoile les passages de la norme, soit dit en passant assez compliqués à comprendre, qui expliquent la raison.

Pour la première fois de ma vie, j’ai pu blâmer le compilateur si mon code ne compilait pas. :)



11 commentaires

Il s’agit de l’article Finding and Understanding Bugs in C Compilers par Xuejun Yang, Yang Chen, Eric Eide, and John Regehr, 2011. Ils ont trouvé des centaines de bugs dans les compilateurs qu’ils ont testé, y compris GCC et Clang/LLVM — sauf dans le compilateur Compcert ou seulement 2 bugs ont été trouvé, si je me souviens bien. Ils ne parlent pas de MSVC parce qu’ils se sont concentrés sur des compilateurs open source.

MSVC a longtemps été un compilateur de merde parce que Microsoft n’avait aucun intérêt à l’améliorer, et préférait la stratégie consistant à forcer les gens à écrire du code Windows-only pour éviter les problèmes de compatibilité, et/ou préférant se concentrer sur des outils C++. Les standards étaient très mal gérés et les problèmes de compatibilité avec les autres compilateurs très très nombreux. Leur support C99 était inexistant (y compris le fait de déclarer des variables au milieu des blocs… je crois que ça a été rajouté en 2013 ?). Depuis ils ont essayé de faire un effort pour éviter d’avoir la honte, embauché des gens qui s’intéressent à C et à C++, ça a un peu progressé.

Ahah, toujours frustrant quand on trouve un bug dans le compilateur. Ca ne m’est jamais arrivé avec MinGW, mais ça m’est arrivé une fois avec javac en Java 9.

Par contre quelqu’un peut m’expliquer pourquoi il n’y a pas besoin de capturer explicitement le bool et le int, mais le double si ?

Est-ce lié au fait qu’on utilise la lambda uniquement dans son contexte emglobant sans en sortir, i.e. pas de closure ? Ou est-ce juste une histoire de copies inutiles, ou parce que les variables sont reconnues de fait comme des constantes ?

C’est plus simple de laisser le compilateur décider tout seul quoi capturer avec [&] ou [=]

+0 -0

Les explications sont sur SO, mais si j’ai bien compris, int const et bool const sont évalués comme des expressions constantes, du coup utilisables dans des contextes où une expression constante est attendue. Exemple : tableau C.

int const a = 5;
int tableau[a];

Alors que double const non. Par contre, si tu remplaces const par constexpr, ça marche pour les trois variables.

Quant à savoir pourquoi y’a capture implicite d’expressions constantes, il faut regarder la norme.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte