Tutto deriva dall'indecidibilità del problema di arresto. Supponiamo di avere una funzione di codice morto "perfetta", alcune Turing Machine M e alcune stringhe di input x e una procedura simile a questa:
Run M on input x;
print "Finished running input";
Se M funziona per sempre, eliminiamo l'istruzione print, poiché non la raggiungeremo mai. Se M non funziona per sempre, allora dobbiamo conservare la dichiarazione di stampa. Quindi, se abbiamo un dispositivo di rimozione del codice morto, ci consente anche di risolvere il problema di Halting, quindi sappiamo che non può esserci un dispositivo di rimozione del codice morto.
Il modo per aggirare questo è attraverso "l'approssimazione conservatrice". Quindi, nel mio esempio di Turing Machine sopra, possiamo supporre che l'esecuzione di M su x potrebbe finire, quindi la riproduciamo in modo sicuro e non rimuoviamo l'istruzione print. Nel tuo esempio, sappiamo che, indipendentemente dalle funzioni che fanno o non si fermano, che non riusciremo mai a raggiungere quell'istruzione stampata.
Di solito, questo viene fatto costruendo un "grafico del flusso di controllo". Facciamo ipotesi di semplificazione, come "la fine di un ciclo while è collegata all'inizio e l'istruzione dopo", anche se funziona per sempre o funziona solo una volta e non visita entrambi. Allo stesso modo, supponiamo che un'istruzione if possa raggiungere tutti i suoi rami, anche se in realtà alcuni non vengono mai utilizzati. Questo tipo di semplificazioni ci consente di rimuovere "ovviamente codice morto" come nell'esempio che dai, pur rimanendo decidibile.
Per chiarire alcune confusioni dai commenti:
Nitpick: per M fisso, questo è sempre decidibile. M deve essere l'input
Come dice Raphael, nel mio esempio, consideriamo la Turing Machine come un input. L'idea è che, se avessimo un algoritmo DCE perfetto, saremmo in grado di costruire lo snippet di codice che do per qualsiasi macchina di Turing e avere un DCE risolverebbe il problema di arresto.
non convinto. restituire come una dichiarazione contundente in un'esecuzione diretta senza ramo non è difficile da decidere. (e il mio compilatore mi dice che è in grado di capirlo)
Per il problema che njzk2 solleva: hai assolutamente ragione, in questo caso puoi determinare che non è possibile ottenere una dichiarazione dopo il ritorno. Questo perché è abbastanza semplice da poter descrivere la sua irraggiungibilità usando i vincoli del grafico del flusso di controllo (ovvero non ci sono bordi in uscita da un'istruzione return). Ma non esiste un perfetto eliminatore di codice morto, che elimina tutto il codice inutilizzato.
Non prendo una prova dipendente dall'input per una prova. Se esiste un tale tipo di input dell'utente che può consentire al codice di essere finito, è corretto per il compilatore supporre che il seguente ramo non sia morto. Non riesco a vedere a cosa servano tutti questi voti, è sia ovvio (es. Infinito stdin) che sbagliato.
Per TomášZato: in realtà non è una prova dipendente dall'input. Piuttosto, interpretalo come un "forall". Funziona come segue: supponiamo di avere un algoritmo DCE perfetto. Se mi dai una Turing Machine M arbitraria e inserisci x, posso usare il mio algoritmo DCE per determinare se M si ferma, costruendo lo snippet di codice sopra e vedendo se l'istruzione print viene rimossa. Questa tecnica, di lasciare un parametro arbitrario per dimostrare un'istruzione forall, è comune in matematica e logica.
Non capisco fino in fondo il punto di TomášZato sul fatto che il codice sia finito. Sicuramente il codice è finito, ma un algoritmo DCE perfetto deve essere applicato a tutto il codice, che è un set infinte. Allo stesso modo, mentre il codice stesso è finito, i potenziali insiemi di input sono infinte, così come il potenziale tempo di esecuzione del codice.
Per quanto riguarda considerare il ramo finale non morto: è sicuro in termini di "approssimazione conservativa" di cui parlo, ma non è sufficiente rilevare tutte le istanze di codice morto come richiesto dall'OP.
Prendi in considerazione un codice come questo:
while (true)
print "Hello"
print "goodbye"
Chiaramente possiamo rimuovere print "goodbye"
senza cambiare il comportamento del programma. Quindi, è un codice morto. Ma se c'è una chiamata di funzione diversa invece che (true)
nella while
condizione, allora non sappiamo se possiamo rimuoverla o meno, portando all'indecidibilità.
Nota che non me ne occuperò da solo. È un risultato ben noto nella teoria dei compilatori. È discusso in The Tiger Book . (Potresti riuscire a vedere di cosa parlano nei libri di Google .