LLVM 3.0

Posté par  (Mastodon) . Édité par baud123, nazcafan, Manuel Menal et Benoît Sibaud. Modéré par patrick_g. Licence CC By‑SA.
Étiquettes :
55
2
déc.
2011
Technologie

LLVM, pour Low Level Virtual Machine, est une suite de compilation qui commence à se faire une place à côté du vénérable GCC. Sa grande modularité, comparé au monolithique GCC, permet d'écrire facilement un compilateur, en utilisant la représentation intermédiaire de LLVM, et en faisant appel ensuite aux optimisations et à la génération de code de LLVM.

Le projet arrive maintenant à une certaine maturité grâce à cette version 3.0 qui apporte beaucoup de nouveautés. Il est à noter que la numérotation suit le schéma d'OpenBSD, c'est-à-dire que la version 3.0 suit la version 2.9, ce n'est donc pas une version majeure – malgré les modifications plus importantes qu'à l'accoutumée – qui sont détaillées dans la seconde partie.

Sommaire

Les nouveautés dans le cœur de LLVM

Un modèle mémoire

LLVM s'est doté d'un modèle mémoire compatible avec ceux du tout récent C++11, du futur C1X et du vénérable Java. Il introduit une spécification des accès mémoire suivant différents ordres. Les instructions atomiques, comme le bien connu compare-and-swap, qui étaient auparavant accessibles via une fonction intrinsèque sont maintenant directement intégrées dans le langage.

Un nouveau système de gestion des exceptions

Le système de gestion des exceptions a été entièrement revu. Le nouveau système permet de corriger un grand nombre de bugs. Il permet également de produire du code mieux optimisé, grâce à l'utilisation d'instructions du langage plutôt que de fonctions intrinsèques. Il est à noter qu'il est en partie compatible avec le système utilisé par GCC, ce qui permet qu'un programme compilé avec CLang/LLVM puisse interagir avec un programme compilé avec GCC, et inversement.

Un nouvel allocateur de registre

Un nouvel allocateur de registre glouton a été développé. L'allocation de registres est l'étape pendant laquelle le compilateur va assigner au mieux des variables à des registres ou à des zones mémoires, en essayant d'optimiser les opérations effectuées (une opération sur une zone mémoire est plus coûteuse qu'une opération dans un registre). Le problème de l'allocation de registre étant un problème NP-complet, il est nécessaire de passer par des heuristiques.

Le code généré par ce nouvel allocateur est 1 à 2% plus petit en taille et jusqu'à 10% plus rapide que le code généré par l'ancien allocateur. Par conséquent, ce nouvel allocateur est maintenant l'allocateur par défaut dans LLVM.

La refonte des types en interne

Comme annoncé dans la nouvelle de sortie de LLVM 2.9, le système de type a été entièrement revu. L'ancien système était basé sur la structure des types, c'est-à-dire que deux types ayant la même structure étaient représentés par un seul et même objet. Ceci avait pour avantage de pouvoir comparer les types en comparant les pointeurs sur les objets, mais comme énorme inconvénient de vérifier pour chaque nouveau type l'existence d'un type avec la même structure, donc de faire des comparaisons de graphes. En plus de cet inconvénient, les types opaques, une fois résolus, impliquaient un parcours complet de tous les types et valeurs existantes, ce qui provoquait de nombreux bugs. En outre, les types récursifs (comme les listes chaînées) étaient difficiles à construire.

Le nouveau système résout tous ces problèmes en introduisant les types nommés, qui correspondent grosso-modo aux structures en C. Les types nommés sont d'abord créés, puis leur corps est défini. Les anciens types basés sur la structure existent toujours mais sont relégués au second plan. Un autre avantage est qu'une grande partie de la fusion des types est maintenant faite au niveau de l'édition des liens, pour tous les types en même temps, plutôt qu'à la compilation, à chaque nouveau type.

Autres améliorations en vrac

  • Le backend MIPS a fait de gros progrès et est considéré comme mature. Il gère notamment diverses sous-architectures.
  • LLVM gère désormais la prédiction de branche et __builtin_expect sous forme de métadonnées dans la représentation intermédiaire.

Les projets annexes à LLVM

Clang

Clang est le compilateur C/C++/Objective C/Objective C++ attitré du projet LLVM, développé en même temps que LLVM. Comparé à GCC, Clang est modulaire (comme LLVM) et fait un gros effort sur les messages de diagnostic en donnant, quand il le peut, la manière de corriger l'erreur. Clang essaie toutefois d'être compatible avec les options de GCC pour que le remplacement puisse se faire de manière souple.

Dans cette version 3.0, Clang a été grandement amélioré sur les points suivants :

  • Les diagnostics ont encore été améliorés, permettant notamment de suggérer la correction de petites erreurs de nom (Int à la place de int par exemple).
  • La libclang, une interface en C permettant de parcourir l'arbre de syntaxe abstrait produit par Clang, permet maintenant une meilleure complétion. Les bindings Python ont également été améliorés.
  • Le préprocesseur peut maintenant être instrumenté à l'aide de fonctions de rappel.
  • La prise en charge de Windows, que ce soit via MinGW, Cygwin ou nativement, a été améliorée, notamment par la gestion d'extensions spécifiques au compilateur MSVC.
  • Un système d'annotation permet de faire quelques vérifications de sûreté sur du code multi-threadé, en particulier dans la gestion des verrous.
  • La prise en charge complète de C++11 ainsi que du futur C1X continue.

DragonEgg

DragonEgg est un greffon GCC qui permet de remplacer les modules d'optimisation et de génération de code de GCC par ceux de LLVM.

Il est le successeur du frontend llvm-gcc. DragonEgg a été mis à jour pour gérer pleinement GCC 4.6 et ne nécessite plus aucun patch à GCC. Il permet également d'appliquer les optimisations de LLVM et de GCC en même temps. Le frontend llvm-gcc n'est désormais plus distribué.

Et ailleurs

  • libc++, une implémentation de la bibliothèque standard C++ (jusqu'à la version C++11) a été portée sur FreeBSD de manière à devenir l'implémentation par défaut dans FreeBSD 10.
  • Le debugger LLDB est désormais plus fiable et robuste. Un tutoriel et une feuille de triche^Wmémento sont disponibles.

La rencontre des développeurs LLVM

La dernière rencontre des développeurs LLVM a eu lieu il y a quelques jours. Le programme était alléchant mais les supports de présentation ne sont pas encore disponibles. On peut noter une présence importante de Google qui utilise LLVM de différentes manières sur l'ensemble de son code.

Projets externes utilisant LLVM

Voici des exemples de projets utilisant LLVM pris parmi les nombreux projets répertoriés dans les notes de sortie. Cette sélection est nécessairement partielle et partiale.

Le langage Tart

Le langage Tart est un mix entre C, C++, Python, Java, C#, D, Haskell, Ruby, Scala. Les premiers exemples permettent de mieux se rendre compte de la syntaxe. La documentation très bien faite permet de voir les fonctionnalités intéressantes : une différenciation entre variable mutable et immutable, closures, interfaces et protocoles, surcharge des opérateurs via des fonctions, attributs (annotations), réflexion, appels direct à des fonctions C, macros dans le langage, templates, etc.

Le langage Julia

Le langage Julia est un langage à vocation technique, comme MatLabScilab ou GNU_Octave. La documentation très riche montre les multiples possibilités de ce langage : dispatch multiple associé à un système de typage bien pensé tout en étant très efficace, coroutines, nombres complexes et rationnels directement dans le langage, gestion d'Unicode, exécution de commandes du shell, macros intégrés dans le langage, et bien évidemment module d'algèbre linéaire (utilisant LAPACK). Julia offre en outre un outil de session interactive. Et dire que ce logiciel n'est qu'en version 0 pré-release...

Aller plus loin

  • # Merci NT

    Posté par  . Évalué à 2.

    Rien à dire, juste merci!

  • # Suis-je médisant ?

    Posté par  (site web personnel) . Évalué à -4.

    Je dois dire que certains passage de la dépêche m'interpellent. Je fais référence à :

    Comme annoncé dans la nouvelle de sortie de LLVM 2.9, le système de type a été entièrement revu. […] En plus de cet inconvénient, les types opaques, une fois résolus, impliquaient un parcours complet de tous les types et valeurs existantes, ce qui provoquait de nombreux bugs.

    Et :

    Le système de gestion des exceptions a été entièrement revu. Le nouveau système permet de corriger un grand nombre de bugs.

    Tant de bugs ? Sur des éléments aussi importants ? C'est inquiétant. Surtout sur cinq « nouveautés dans le cœur de LLVM » décrites.

    Plus pour chipoter :

    LLVM gère désormais la prédiction de branche et __builtin_expect sous forme de métadonnées dans la représentation intermédiaire.

    Ça semble un peu élémentaire comme fonctionalité non ?

    • [^] # Re: Suis-je médisant ?

      Posté par  . Évalué à 6.

      Tant de bugs ? Sur des éléments aussi importants ? C'est inquiétant. Surtout sur cinq « nouveautés dans le cœur de LLVM » décrites.

      En regardant le blog llvm on trouve :
      - http://blog.llvm.org/2011/11/llvm-30-exception-handling-redesign.html
      //
      One of the biggest IR changes in the LLVM 3.0 release is a redesign and reimplementation of the LLVM IR exception handling model. The old model, while it worked for most cases, fell over in some key situations, leading to obscure miscompilations, missed optimizations, and poor compile time
      //

      • http://blog.llvm.org/2011/11/llvm-30-type-system-rewrite.html // One of the most pervasive IR (and thus compiler API) changes in LLVM 3.0 was a complete reimplementation of the LLVM IR type system. This change was long overdue (the original type system lasted from LLVM 1.0!) and made the compiler faster, greatly simplified a critical subsystem of VMCore, and eliminated some design points of IR that were frequently confusing and inconvenient. //

      Donc je pense que le "grand nombre de bugs" est une exagération de l'article...

    • [^] # Re: Suis-je médisant ?

      Posté par  . Évalué à 1.

      LLVM gère désormais la prédiction de branche et __builtin_expect sous forme de métadonnées dans la représentation intermédiaire.

      Ça semble un peu élémentaire comme fonctionalité non ?

      Je ne sais quoi penser : d'après le sujet suivant, justement, les processeurs sont devenus tellement bons dans ce domaine que ça n'est plus nécessaire... Ou alors c'est pour les architectures qui en ont encore besoin?

      http://linuxfr.org/news/le-noyau-linux-est-disponible-en-version%C2%A030#toc_13

      • [^] # Re: Suis-je médisant ?

        Posté par  . Évalué à 5.

        Itanium vaincra !

        (ou pas)
        (vraissemblablement pas en fait)

        BeOS le faisait il y a 20 ans !

      • [^] # Re: Suis-je médisant ?

        Posté par  (site web personnel) . Évalué à 3.

        Quel est le rapport entre le prefetch et la prédiction de branchement ?

        "La première sécurité est la liberté"

        • [^] # Re: Suis-je médisant ?

          Posté par  . Évalué à 1.

          Je présume qu'il faut savoir quelle instruction chargée pour la charger en avance.

          Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

          • [^] # Re: Suis-je médisant ?

            Posté par  . Évalué à 3.

            Pas certain de ce que j'avance pour les arch intel, mais il me semble que la cache d'instructions et de donnes sont different. Le prefetch n'affecte que le cache de donnees, donc inutile dans le cadre d'un branchement.

            Par contre, une bonne prediction d'un branchement au sein meme du compilateur permet d'isoler les portions de code qui vont etre executees souvent de celles qui ne le sont pas. En regroupant les sequences d'instructions qui vont etre executee frequement, on obtient une meilleure utilisation du cache d'instruction. Dans le meme registre, dans le cas d'optimizations longues, on peut se permettre de limiter ces optimizations a ces meme instructions, et ne pas le faire sur le code infrequement utilise.

        • [^] # Re: Suis-je médisant ?

          Posté par  . Évalué à 1.

          Environ 4 lettres \o/

        • [^] # Re: Suis-je médisant ?

          Posté par  . Évalué à 2.

          Disons que les deux sont des opérations spéculatives que les processeurs font aujourd'hui plus efficacement qu'une directive explicite du programmeur.

          (pour avoir testé le prefetch explicite et __builtin_expect à quelques endroits dans le source de Python, ça ne sert effectivement à rien, voire ça ralentit un peu l'exécution)

          • [^] # Re: Suis-je médisant ?

            Posté par  (site web personnel) . Évalué à 2.

            Pour la prédiction de branchement, je ne comprends pas comment cela peut faire perdre des performances (sauf infos bidon).

            Par contre, un bon expect ne fait que gagner que qq cycles dans le bon sens. Il y a tellement de sources de perte, un accès mémoire non caché prend 100 cycles, donc il y a plein de raisons, pour ne pas voir d'effets.

            As-tu testé les attributs cold/hot sur les fonctions ?

            "La première sécurité est la liberté"

            • [^] # Re: Suis-je médisant ?

              Posté par  . Évalué à 2.

              Pour la prédiction de branchement, je ne comprends pas comment cela peut faire perdre des performances (sauf infos bidon).

              Non, ça c'était pour le prefetch.

              As-tu testé les attributs cold/hot sur les fonctions ?

              Non pas du tout. As-tu des pointeurs là-dessus ?

              • [^] # Re: Suis-je médisant ?

                Posté par  (site web personnel) . Évalué à 2.

                Il y a d'ailleurs beaucoup de chose :

                http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html

                D'un autre coté, la compilation en 2 temps avec profilage du code doit faire tout cela de manière automatique. ( -fprofile-use ). J'imagine qu'il faut faire une 1er compilation, lancer une série de benchmark représentatif (si les jeu de données sont trop petits, cela n'a aucun sens, c'est pourtant courant dans les benchs synthétiques), puis recompiler avec les infos en plus.

                "La première sécurité est la liberté"

              • [^] # Re: Suis-je médisant ?

                Posté par  (site web personnel) . Évalué à 3.

                Si le but est de tirer les trip à son compilateur, il y a à prendre en considération, le mot clef "restrict" pour qualifier les pointeurs et utiliser "const" le plus souvent possible, cela aide en cas de manipulation de pointeur en provenance du dehors du fichier .c.

                "static inline" peut aider pour diminuer la taille du code inutile si la fonction n'a pas vocation a être utiliser en dehors du fichier .c en cours. Même en cas de fonction inline, la fonction doit exister en temps que tel pour être appelé de l'extérieur, donc si il n'y en a pas besoin autant la virer ("static").

                Je n'ai pas encore beaucoup jouer avec les qualifier de fonction comme "pure" ou "const", j'imagine que cela doit jouer dans l'usage de tel fonction au milieu de boucle de calcul.

                Sinon, il y a aussi les fonctionnalités openMP à regarder. Cela permet de faire de la parallélisation avec des threads à pas chère. Il faudrait que je retrouve mes exemples, mais il suffit souvent de mettre le bon pragma, juste devant la boucle la plus externe pour qu'elle soit paralléliser. Par contre, il faut une indépendance totale entre élément traité (comme une fonction map en fonctionnel).

                http://rajorshi.net/blog/2009/05/programming-for-multicore-introduction-openmp-gcc/
                https://en.wikipedia.org/wiki/OpenMP

                "La première sécurité est la liberté"

                • [^] # Re: Suis-je médisant ?

                  Posté par  . Évalué à 1.

                  Sinon, il y a aussi les fonctionnalités openMP à regarder. Cela permet de faire de la parallélisation avec des threads à pas chère. Il faudrait que je retrouve mes exemples, mais il suffit souvent de mettre le bon pragma, juste devant la boucle la plus externe pour qu'elle soit paralléliser. Par contre, il faut une indépendance totale entre élément traité (comme une fonction map en fonctionnel).

                  Pas nécessairement. OpenMP supporte des pattern plus sophistiqués par exemple :

                  int ret = 0;
                  for(int i = 0; i < 10; ++i) {
                      ret += i;
                  }
                  
                  

                  Peut être parallélisé efficacement ainsi :

                  int ret = 0;
                  #pragma omp parallel for reduction(+:ret)
                  for(int i = 0; i < 10; ++i) {
                      ret += i;
                  }
                  
                  

                  http://software.intel.com/en-us/articles/getting-started-with-openmp/

                  Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

                  • [^] # Re: Suis-je médisant ?

                    Posté par  (site web personnel) . Évalué à 2.

                    la reduction est un truc très particuliers et je me demande si seul le + est pris en charge. Cela ne ressemble pas à un vrai fold/reduce.

                    "La première sécurité est la liberté"

  • # ...

    Posté par  . Évalué à -1.

    Merci pour la news.

    Par contre je trouve que la rédaction ne passe pas bien en francais, si je n'avais pas lu certains articles en anglais, je ne suis pas sur que j'aurais pu tout comprendre.

    On retrouve des traductions techniques qui passe pas tres bien ou de l'enrobage.
    Par exemple :

    • utilisation de vénérable pour gcc/java
    • "comme le bien connu"
    • " grâce à l'utilisation d'instructions du langage plutôt que de fonctions intrinsèques."
    • "permet maintenant une meilleure complétion."
    • "vérifications de sûreté"
    • "Le programme était alléchant mais les transparents ne sont pas encore disponibles."
    • [^] # Re: ...

      Posté par  . Évalué à 1.

      Et pour chipoter un peu plus :

      La prise en charge complète de C++11 ainsi que du futur C1X continue.

      Bein alors ? Complète ou non ? C'est encore du Marketing mensonger à la Apple Inc. ça…
      D'ailleurs, en parlant de la pomme, ils sont quand même un peu gonflé je trouve :

      Apple LLVM Compiler

      Bon et les chercheurs de l'université de l'Illinois dans tout ça ? Apple a le chic pour s'approprier des projets… Il faudrait leur rappeler que la BSD ne permet pas de s'attribuer la paternité d'un projet.

      • [^] # Re: ...

        Posté par  (site web personnel) . Évalué à 3.

        Bein alors ? Complète ou non ? C'est encore du Marketing mensonger à la Apple Inc. ça…

        Le rédacteur s'est un peu enflammé personne n'a jamais écrit que le support de C++11 était complet. Et le C1X c'est la future spec C, donc ce n'est pas la même chose, même si ils se sont alignés pour certains choses.

        Apple LLVM Compiler

        Je suppose que c'est la version Apple (distribué avec les nouveaux codes) basé sur llvm (cad llvm + un tas de patch encore / toujours privé par Apple). Le projet llvm reste indépendant de Apple. Evidemment ils auraient pu écrire llvm compiler + Apple patch m'enfin ...

        • [^] # Re: ...

          Posté par  . Évalué à 1. Dernière modification le 02 décembre 2011 à 12:39.

          J'avais tiqué aussi. J'avais voulu corriger ça dans l'espace de rédaction, mais je ne trouvais pas une bonne formulation … et puis j'ai fini par zapper cette histoire,
          On peut proposer :

          La marche vers la prise en charge complète [...] continue

          Un admin dans le coin ?

        • [^] # Re: ...

          Posté par  (Mastodon) . Évalué à 3.

          J'ai rédigé par morceaux et souvent très tard, donc c'est peut-être pas forcément de la grande qualité. D'un autre côté, la news est resté une grosse semaine dans l'espace de rédaction, la participation est la bienvenue ;)

  • # Tart

    Posté par  (site web personnel) . Évalué à 5.

    Tart a l'air intéressant. En gros ça simplifie le C++ avec une syntax plus moderne et des ajouts intéressants comme les closure, les fonctions anonymes, les protocol.

    J'aime aussi bien le fait que ça semble rester très lisible. J'aime aussi bien Scala, mais on peut vraiment écrire des choses incompréhensibles pour le commun des mortels, notamment en abusant le système de type. Donc j'ai toujours été intéressé par un truc entre le Java/C++ et le Scala et Tart a l'air pas mal dans cette niche.

    • [^] # Re: Tart

      Posté par  . Évalué à 3.

      J'aime aussi bien Scala, mais on peut vraiment écrire des choses incompréhensibles pour le commun des mortels, notamment en abusant le système de type.

      On peut également faire la même chose en C++. Il vaut mieux laisser ce travail aux bibliothèques comme Loki, Boost.

      • [^] # Re: Tart

        Posté par  (site web personnel) . Évalué à 4.

        Ouais mais j'ai l'impression que Scala pousse à ça même hors des bibliothèques spécialisés.
        Typiquement, on n'est pas obligé de mettre les parenthèses quand on appelle une méthode à un argument. On n'est pas toujours obligé de mettre un point virgule à la fin d'une expression (mais parfois oui et les règles sont pas super faciles à mémoriser), etc...

        Le problème c'est que ça permet d'avoir deux programmes tout deux écrits en Scala qui n'ont pas forcément du tout la même gueule. Donc on se retrouve à devoir réapprendre une partie de la syntaxe à chaque fois. C'est particulièrement vrai avec la surcharge des opérateurs qui est utilisée à tort et à travers, encore plus qu'en C++ (pour écrire des parser, pour manipuler des structures de données).

        Enfin voilà, c'est un peu dommage. J'aime beaucoup les idées de Scala, mais je rêverais d'une syntaxe un brin plus standardisé pour pouvoir l'utiliser tous les jours.

        • [^] # Re: Tart

          Posté par  . Évalué à 2.

          Ouais mais j'ai l'impression que Scala pousse à ça même hors des bibliothèques spécialisés.

          Voila un exemple avec et sans sugar syntax, la différence est visible en terme de lecture mais pas en terme de comprehension.

          /* with RegexParsers */
          def bin_nb = "0" | "1"
          def addition: Parser[String] = bin_nb ~ "+" ~ bin_nb ^^ {
            case "0" ~ "+" ~ "0" => "0"
            case "1" ~ "+" ~ "0" => "1"
            case "0" ~ "+" ~ "0" => "1"
            case "1" ~ "+" ~ "1" => "10"
          }
          
          

          peut s'écrire:
          def bin_nb = literal("0").|(literal("1"))
          def addition: Parser[String] = bin_nb.~(literal("+")).~(bin_nb).^^({
            case literal("0") ~ literal("+") ~ literal("0") => "0" 
            case literal("1") ~ literal("+") ~ literal("0") => "1"
            case literal("0") ~ literal("+") ~ literal("0") => "1"
            case literal("1") ~ literal("+") ~ literal("1") => "10" 
          })
          
          
          • [^] # Re: Tart

            Posté par  . Évalué à 4.

            N'enpêche que dans les deux cas, il semble y avoir une erreur dans le code :-)

            case literal("0") ~ literal("+") ~ literal("0") => "1"

            ou alors j'ai rien compris....

            • [^] # Re: Tart

              Posté par  . Évalué à -4.

              C'est une addition de deux nombre binaire pas un xor.

              • [^] # Re: Tart

                Posté par  . Évalué à 5.

                Donc c'est normal d'avoir dans le même bloc "case" :

                case "0" ~ "+" ~ "0" => "0"
                et
                case "0" ~ "+" ~ "0" => "1"

                ???

                • [^] # Re: Tart

                  Posté par  . Évalué à 2.

                  Bien vue, pour couper court voici le code complet et fonctionnel:

                  import scala.util.parsing.combinator._
                  
                  
                  object Binaire extends RegexParsers {
                  
                    def bin_nb = "0" | "1"
                    
                    def exp = bin_nb ~ "+" ~ bin_nb ^^ {
                      case "0" ~ "+" ~ "0" => "0"
                      case "1" ~ "+" ~ "0" => "1"
                      case "0" ~ "+" ~ "1" => "1"
                      case "1" ~ "+" ~ "1" => "10"
                    }
                    
                  
                    def main(args: Array[String]) {
                      println(parseAll(exp, args.mkString))
                    }
                  
                  }
                  
                  
    • [^] # Re: Tart

      Posté par  (site web personnel) . Évalué à 3.

      Tart a l'air bien fait, j'espère qu'il aura plus de succès que les langages de la même catégorie (D, ooc, Pike)!

      Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

    • [^] # Re: Tart

      Posté par  . Évalué à 5.

      La première phrase :

      Le langage Tart est un mix entre C, C++, Python, Java, C#, D, Haskell, Ruby, Scala.

      M'a fait peur généralement quand on mélange trop de trucs comme ça ça donne juste envie de vomir. Mais là il semble que ce soit pas si mauvais en regardant les exemples.

      Mais qu'est ce qu'il est dur de percer pour un nouveau langage …

      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

      • [^] # Re: Tart

        Posté par  . Évalué à 7.

        Quand on voit le succès de Perl ou de Javascript, on peut se demander si c'est vraiment lié aux qualités du langage, à vrai dire…
        Ben quoi, on est vendredi, non ?

      • [^] # Re: Tart

        Posté par  (Mastodon) . Évalué à 2.

        La description vient du site du langage Tart lui-même. C'est vrai que ça fait peur mais en fait, c'est plutôt bien foutu. Il y a plein de bonnes idées.

    • [^] # Re: Tart

      Posté par  (site web personnel) . Évalué à 5.

      Tant de langages oubliés, d'essais ratés, de longues heures perdues, alors qu'il suffirait pour de s'habituer à quelques parenthèses pour être heureux...

      (sortir (moi))
      
      
      • [^] # Re: Tart

        Posté par  . Évalué à -1.

        Qui doit sortir de toi ?

      • [^] # Re: Tart

        Posté par  . Évalué à 1.

        moi.sortir()

        • [^] # Re: Tart

          Posté par  . Évalué à 7.

          Vous savez pas faire du code propre dans un langage magnifique ?

          moi = null;
          System.gc();
          
          

          Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

          • [^] # Re: Tart

            Posté par  . Évalué à 4.

            Warning: explicit call to garbage collector

    • [^] # Re: Tart

      Posté par  . Évalué à 3.

      Bah, pour moi ça reste surtout un changement de syntaxe c'est bien mais dommage que ça n'aille pas plus loin avec par exemples des int avec un comportements "à la Ada" plutôt que les comportements bizarre des C/C++ ou Java.

  • # LLVM & Debian

    Posté par  (site web personnel) . Évalué à 7.

    Pour les Debianeux, LLVM 3.0 vient d'être uploadé dans Debian Unstable.
    Clang et Dragonegg devraient bientôt arriver.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.