Tout cela vient de l'indécidabilité du problème de l'arrêt. Supposons que nous ayons une fonction de code mort "parfait", une machine de Turing M et une chaîne d'entrée x, et une procédure qui ressemble à ceci:
Run M on input x;
print "Finished running input";
Si M s'exécute pour toujours, nous supprimons l'instruction print, car nous ne l'atteindrons jamais. Si M ne s'exécute pas indéfiniment, nous devons conserver l'instruction print. Ainsi, si nous avons un suppresseur de code mort, il nous permet également de résoudre le problème d'arrêt, nous savons donc qu'il ne peut pas y avoir un tel suppresseur de code mort.
Nous pouvons contourner ce problème par «approximation conservatrice». Donc, dans mon exemple de Turing Machine ci-dessus, nous pouvons supposer que l'exécution de M sur x peut se terminer, donc nous le faisons en toute sécurité et ne supprimons pas l'instruction d'impression. Dans votre exemple, nous savons que, quelles que soient les fonctions qui s'arrêtent ou non, il n'y a aucun moyen d'atteindre cette instruction d'impression.
Habituellement, cela se fait en construisant un "graphique de flux de contrôle". Nous faisons des hypothèses simplificatrices, telles que "la fin d'une boucle while est connectée au début et à l'instruction après", même si elle s'exécute pour toujours ou ne s'exécute qu'une seule fois et ne visite pas les deux. De même, nous supposons qu'une instruction if peut atteindre toutes ses branches, même si en réalité certaines ne sont jamais utilisées. Ces types de simplifications nous permettent de supprimer le "code manifestement mort" comme l'exemple que vous donnez, tout en restant décidable.
Pour clarifier quelques confusions des commentaires:
Nitpick: pour M fixe, c'est toujours décidable. M doit être l'entrée
Comme le dit Raphaël, dans mon exemple, nous considérons la machine de Turing comme une entrée. L'idée est que, si nous avions un algorithme DCE parfait, nous serions en mesure de construire l'extrait de code que je donne pour n'importe quelle machine de Turing , et avoir un DCE résoudrait le problème d'arrêt.
pas convaincu. retourner comme une instruction franche dans une exécution directe sans branche n'est pas difficile à décider. (et mon compilateur me dit qu'il est capable de comprendre cela)
Pour le problème soulevé par njzk2: vous avez absolument raison, dans ce cas, vous pouvez déterminer qu'il n'y a aucun moyen d'obtenir une déclaration après le retour. Cela est dû au fait qu'il est suffisamment simple pour décrire son inaccessibilité à l'aide de contraintes de graphique de flux de contrôle (c'est-à-dire qu'il n'y a pas de fronts sortants d'une déclaration de retour). Mais il n'y a pas d'éliminateur de code mort parfait, ce qui élimine tout le code inutilisé.
Je ne prends pas de preuve dépendante de l'entrée pour une preuve. S'il existe un tel type d'entrée utilisateur qui peut permettre au code d'être fini, il est correct pour le compilateur de supposer que la branche suivante n'est pas morte. Je ne vois pas à quoi servent tous ces votes positifs, c'est à la fois évident (par exemple stdin sans fin) et faux.
Pour TomášZato: ce n'est pas vraiment une preuve dépendante de l'entrée. Interprétez-le plutôt comme un "forall". Cela fonctionne comme suit: supposons que nous avons un algorithme DCE parfait. Si vous me donnez une machine de Turing M arbitraire et saisissez x, je peux utiliser mon algorithme DCE pour déterminer si M s'arrête, en construisant l'extrait de code ci-dessus et en voyant si l'instruction d'impression est supprimée. Cette technique, consistant à laisser un paramètre arbitraire pour prouver une instruction forall, est courante en mathématiques et en logique.
Je ne comprends pas bien le point de TomášZato sur le code étant fini. Certes, le code est fini, mais un algorithme DCE parfait doit s'appliquer à tout le code, qui est un ensemble infini. De même, alors que le code lui-même est fini, les ensembles potentiels d'entrée sont infinis, tout comme le temps d'exécution potentiel du code.
Quant à considérer que la branche finale n'est pas morte: elle est sûre en termes d '"approximation conservatrice" dont je parle, mais ce n'est pas suffisant pour détecter toutes les instances de code mort comme l'OP le demande.
Considérez le code comme ceci:
while (true)
print "Hello"
print "goodbye"
De toute évidence, nous pouvons supprimer print "goodbye"
sans modifier le comportement du programme. C'est donc du code mort. Mais s'il y a un appel de fonction différent au lieu de (true)
dans la while
condition, alors nous ne savons pas si nous pouvons le supprimer ou non, conduisant à l'indécidabilité.
Notez que je ne propose pas cela tout seul. C'est un résultat bien connu dans la théorie des compilateurs. Il en est question dans The Tiger Book . (Vous pourrez peut-être voir de quoi ils parlent dans Google Books .