Utilizamos compiladores a diario como si su corrección fuera un hecho, pero los compiladores también son programas y pueden contener errores. Siempre me pregunté acerca de esta robustez infalible. ¿Alguna vez has encontrado un error en el compilador? ¿Qué era y cómo te diste cuenta que el problema estaba en el compilador mismo?
... y cómo hacen que los compiladores sean tan confiables?
testing
bug
compiler
system-reliability
EpsilonVector
fuente
fuente
Respuestas:
Se prueban exhaustivamente a través del uso de miles o incluso millones de desarrolladores con el tiempo.
Además, el problema a resolver está bien definido (por una especificación técnica muy detallada). Y la naturaleza de la tarea se presta fácilmente a las pruebas de unidad / sistema. Es decir, básicamente está traduciendo la entrada de texto en un formato muy específico para generar en otro tipo de formato bien definido (algún tipo de código de bytes o código de máquina). Por lo tanto, es fácil crear y verificar casos de prueba.
Además, generalmente los errores también son fáciles de reproducir: aparte de la plataforma exacta y la información de la versión del compilador, generalmente todo lo que necesita es un código de entrada. Sin mencionar que los usuarios del compilador (siendo los propios desarrolladores) tienden a dar informes de errores mucho más precisos y detallados que cualquier usuario promedio de computadoras :-)
fuente
Además de todas las excelentes respuestas hasta ahora:
Tienes un "sesgo de observador". No observa errores, y por lo tanto asume que no hay ninguno.
Solía pensar como tú. Luego comencé a escribir compiladores profesionalmente, y déjame decirte que hay muchos errores allí.
No ves los errores porque escribes código que es como el 99.999% de todo el resto del código que la gente escribe. Probablemente escriba código perfectamente normal, directo y claramente correcto que llame a métodos y ejecute bucles y no haga nada elegante o extraño, porque es un desarrollador normal que resuelve problemas comerciales normales.
No ve ningún error del compilador porque los errores del compilador no se encuentran en los escenarios de código normales sencillos y fáciles de analizar; los errores están en el análisis de código extraño que no escribes.
Por otro lado, tengo el sesgo de observador opuesto. Veo códigos locos todo el día todos los días, y para mí los compiladores parecen estar llenos de errores.
Si se sentó con la especificación de idioma de cualquier idioma, y tomó cualquier implementación del compilador para ese idioma, y realmente trató de determinar si el compilador implementó exactamente la especificación o no, concentrándose en casos de esquina oscuros, muy pronto encontrará errores del compilador con bastante frecuencia. Déjame darte un ejemplo, aquí hay un error del compilador de C # que encontré literalmente hace cinco minutos.
El compilador da tres errores.
Obviamente, el primer mensaje de error es correcto y el tercero es un error. El algoritmo de generación de errores está tratando de descubrir por qué el primer argumento no es válido, lo mira, ve que es una constante y no vuelve al código fuente para verificar si se marcó como "ref"; más bien, supone que nadie sería tan tonto como para marcar una constante como referencia, y decide que la referencia debe faltar.
No está claro cuál es el tercer mensaje de error correcto, pero este no lo es. De hecho, tampoco está claro si el segundo mensaje de error es correcto. ¿Debería fallar la resolución de sobrecarga o "ref 123" debería tratarse como un argumento de referencia del tipo correcto? Ahora tendré que pensarlo un poco y hablarlo con el equipo de selección para que podamos determinar cuál es el comportamiento correcto.
Nunca has visto este error porque probablemente nunca harías algo tan tonto como para intentar pasar 123 por ref. Y si lo hiciera, probablemente ni siquiera notaría que el tercer mensaje de error no tiene sentido, ya que el primero es correcto y suficiente para diagnosticar el problema. Pero trato de hacer cosas así, porque estoy tratando de romper el compilador. Si lo intentaras, también verías los errores.
fuente
¿Me estás tomando el pelo? Los compiladores también tienen errores, realmente se cargan.
GCC es probablemente el compilador de código abierto más famoso del planeta y eche un vistazo a su base de datos de errores: http://gcc.gnu.org/bugzilla/buglist.cgi?product=gcc&component=c%2B%2B&resolution=-- -
Entre GCC 3.2 y GCC 3.2.3, eche un vistazo a cuántos errores se solucionaron: http://gcc.gnu.org/gcc-3.2/changes.html
En cuanto a otros como Visual C ++, ni siquiera quiero comenzar.
¿Cómo hacer que los compiladores sean confiables? Bueno, para empezar, tienen montones y montones de pruebas unitarias. Y todo el planeta los usa para que no haya escasez de probadores.
Sin embargo, en serio, los desarrolladores de compiladores que me gusta creer son programadores superiores y, aunque no son infalibles, son un gran golpe.
fuente
Me he encontrado con dos o tres en mi día. La única forma real de detectar uno es mirar el código de ensamblaje.
Aunque los compiladores son altamente confiables por razones que otros carteles han señalado, creo que la confiabilidad del compilador a menudo es una evaluación autocumplida. Los programadores tienden a ver el compilador como el estándar. Cuando algo sale mal, asumes que es tu culpa (porque el 99,999% de las veces es así) y cambias tu código para solucionar el problema del compilador en lugar de al revés. Por ejemplo, el bloqueo de código bajo una configuración de alta optimización es definitivamente un error del compilador, pero la mayoría de las personas simplemente lo configuran un poco más bajo y continúan sin informar el error.
fuente
Los compiladores tienen varias propiedades que conducen a su corrección:
fuente
Ellos no. Hacemos. Debido a que todos los usan todo el tiempo, los errores se encuentran rápidamente.
Es un juego de números. Debido a que los compiladores se acostumbran tan penetrante, es altamente probable que cualquier fallo se puede accionar por alguien, sino porque hay un gran número de usuarios, por ejemplo, es muy poco probable que alguien que será usted específicamente.
Entonces, depende de su punto de vista: en todos los usuarios, los compiladores tienen errores. Pero es muy probable que alguien más haya compilado una pieza de código similar antes que usted, por lo que si hubiera sido un error, los habría golpeado a usted, no a usted, por lo que desde su punto de vista individual , parece que el error fue nunca ahí.
Por supuesto, además de eso, puede agregar todas las otras respuestas aquí: los compiladores están bien investigados, bien entendidos. Existe el mito de que son difíciles de escribir, lo que significa que solo los programadores muy inteligentes y muy buenos realmente intentan escribir uno, y son muy cuidadosos cuando lo hacen. Por lo general, son fáciles de probar y fáciles de estresar o realizar pruebas de fuzz. Los usuarios del compilador tienden a ser programadores expertos, lo que genera informes de errores de alta calidad. Y al revés: los escritores de compiladores tienden a ser usuarios de su propio compilador.
fuente
Además de todas las respuestas ya, me gustaría agregar:
Creo que muchas veces, los vendedores están comiendo su propia comida para perros. Es decir, están escribiendo los compiladores en sí mismos.
fuente
Me he encontrado con errores de compilación a menudo.
Puede encontrarlos en los rincones más oscuros donde hay menos probadores. Por ejemplo, para encontrar errores en GCC, debería intentar:
fuente
Muchas rasones:
fuente
Suelen ser muy buenos en -O0. De hecho, si sospechamos un error del compilador, comparamos -O0 versus cualquier nivel que estemos tratando de usar. Los niveles de optimización más altos conllevan un mayor riesgo. Algunos incluso lo son deliberadamente y están etiquetados como tales en la documentación. Me he encontrado con muchos (al menos cien durante mi tiempo), pero recientemente se están volviendo mucho más raros. Sin embargo, en la búsqueda de buenos números de referencia (u otros puntos de referencia importantes para el marketing), la tentación de superar los límites es grande. Hace unos años tuvimos problemas en los que un proveedor (sin nombre) decidió hacer una violación del paréntesis predeterminado, en lugar de alguna opción de compilación especial claramente etiquetada.
Puede ser difícil diagnosticar un error del compilador frente a una referencia de memoria perdida, una recompilación con diferentes opciones puede simplemente codificar el posicionamiento relativo de los objetos de datos dentro de la memoria, por lo que no sabe si es el Heisenbug de su código fuente o un buggy compilador. Además, muchas optimizaciones realizan cambios legítimos en el orden de las operaciones, o incluso simplificaciones algebraicas a su álgebra, y estas tendrán diferentes propiedades con respecto al redondeo de punto flotante y bajo / desbordamiento. Es difícil desenredar estos efectos de errores REALES. La computación de punto flotante de núcleo duro es difícil por esta razón, porque los errores y la sensibilidad numérica a menudo no son fáciles de desenredar.
fuente
Los errores del compilador no son tan raros. El caso más común es que un compilador informe un error en el código que debe ser aceptado, o que un compilador acepte un código que debería haber sido rechazado.
fuente
Sí, ayer encontré un error en el compilador ASP.NET:
Cuando utiliza modelos fuertemente tipados en las vistas, hay un límite en la cantidad de parámetros que pueden contener las plantillas. Obviamente, no puede tomar más de 4 parámetros de plantilla, por lo que ambos ejemplos a continuación hacen que sea demasiado difícil de manejar para el compilador:
No se compilaría tal cual, pero lo hará si
type5
se elimina.Se compilaría si
type4
se elimina.Tenga en cuenta que
System.Tuple
tiene muchas sobrecargas y puede tomar hasta 16 parámetros (es una locura, lo sé).fuente
¡Sip!
Los dos más memorables fueron los dos primeros que encontré. Ambos estaban en el compilador Lightspeed C para Macs 680x0 alrededor de 1985-7.
El primero fue donde, en algunas circunstancias, el operador de postincremento entero no hizo nada; en otras palabras, en un código particular, "i ++" simplemente no le hizo nada a "i". Me estaba arrancando el cabello hasta que vi un desmontaje. Luego hice el incremento de una manera diferente y envié un informe de error.
El segundo fue un poco más complicado, y fue realmente una "característica" mal considerada que salió mal. Los primeros Macs tenían un sistema complicado para realizar operaciones de disco de bajo nivel. Por alguna razón, nunca entendí, probablemente teniendo que ver con la creación de ejecutables más pequeños, en lugar de que el compilador solo generara las instrucciones de operación del disco en el lugar en el código objeto, el compilador de Lightspeed llamaría una función interna, que en tiempo de ejecución generó la operación del disco instrucciones en la pila y saltó allí.
Eso funcionó muy bien en las CPU 68000, pero cuando ejecutaba el mismo código en una CPU 68020, a menudo hacía cosas raras. Resultó que una nueva característica del 68020 era una caché de instrucciones de 256 bytes de instrucciones primitivas. Al ser los primeros días con los cachés de la CPU, no tenía la noción de que el caché estuviera "sucio" y necesitara ser rellenado; Supongo que los diseñadores de CPU de Motorola no pensaron en el código auto modificable. Entonces, si realizó dos operaciones de disco lo suficientemente juntas en su secuencia de ejecución, y el tiempo de ejecución de Lightspeed construyó las instrucciones reales en la misma ubicación en la pila, la CPU pensaría erróneamente que tuvo un golpe de caché de instrucciones y ejecutaría la primera operación de disco dos veces.
Una vez más, descubrir eso requirió un poco de trabajo con un desensamblador y muchos pasos en un depurador de bajo nivel. Mi solución era prefijar cada operación de disco con una llamada a una función que hizo 256 instrucciones "NOP", que inundaron (y borraron) el caché de instrucciones.
En los últimos 25 años desde entonces, he visto cada vez menos errores de compilación con el tiempo. Creo que hay un par de razones para eso:
fuente
Encontró un error evidente en Turbo Pascal hace 5,5 años. Un error presente en la versión anterior (5.0) ni en la siguiente (6.0) del compilador. Y uno que debería haber sido fácil de probar, ya que no se trataba de una caja de esquina (solo una llamada que no se usa con tanta frecuencia).
En general, ciertamente los creadores de compiladores comerciales (en lugar de proyectos de pasatiempo) tendrán procedimientos de prueba y control de calidad muy extensos. Saben que sus compiladores son sus proyectos emblemáticos y que las fallas se verán muy mal en ellos, peor de lo que se verían en otras compañías que fabrican la mayoría de los otros productos. Los desarrolladores de software son un grupo implacable, nuestros proveedores de herramientas nos decepcionan, es probable que busquemos alternativas en lugar de esperar una solución del proveedor, y es muy probable que comuniquemos ese hecho a nuestros pares que bien podrían seguir nuestro ejemplo. En muchas otras industrias ese no es el caso, por lo que la pérdida potencial para un fabricante de compiladores como resultado de un error grave es mucho mayor que eso para decir un fabricante de software de edición de video.
fuente
Cuando el comportamiento de su software es diferente cuando se compila con -O0 y con -O2, entonces ha encontrado un error de compilación.
Cuando el comportamiento de su software es diferente de lo que espera, es probable que el error esté en su código.
fuente
Los errores del compilador ocurren, pero tiendes a encontrarlos en rincones extraños ...
Hubo un error extraño en el compilador VAX VMS C de Digital Equipment Corporation en la década de 1990
(Llevaba una cebolla en mi cinturón, como era la moda en ese momento)
Un punto y coma extraño en cualquier lugar anterior a un bucle for se compilaría como el cuerpo del bucle for.
En el compilador en cuestión, el bucle se ejecuta solo una vez.
ve
Eso me costó mucho tiempo.
La versión anterior del compilador PIC C que (solíamos) infligir a los estudiantes con experiencia laboral no podía generar código que usara la interrupción de alta prioridad correctamente. Tuviste que esperar 2-3 años y actualizar.
El compilador MSVC 6 tenía un error ingenioso en el enlazador, fallaba en la segmentación y moría de vez en cuando sin ninguna razón. Una construcción limpia generalmente lo solucionó (pero no siempre suspira ).
fuente
En algunos dominios, como el software de aviónica, existen requisitos de certificación extremadamente altos, tanto en el código y el hardware, como en el compilador. Acerca de esta última parte, hay un proyecto que tiene como objetivo crear un compilador de C verificado formalmente, llamado Compcert . En teoría, este tipo de compilador es tan confiable como parece.
fuente
He visto varios errores del compilador, reporté algunos yo mismo (específicamente, en F #).
Dicho esto, creo que los errores de compilación son raros porque las personas que escriben compiladores generalmente se sienten muy cómodos con los conceptos rigurosos de la informática que los hacen realmente conscientes de las implicaciones matemáticas del código.
Presumiblemente, la mayoría de ellos están muy familiarizados con cosas como el cálculo lambda, la verificación formal, la semántica denotacional, etc., cosas que un programador promedio como yo apenas puede comprender.
Además, generalmente hay una asignación bastante directa de entrada a salida en compiladores, por lo que depurar un lenguaje de programación es probablemente mucho más fácil que depurar, por ejemplo, un motor de blog.
fuente
Encontré un error en el compilador de C # no hace mucho tiempo, puedes ver cómo Eric Lippert (que está en el equipo de diseño de C #) descubrió cuál era el error aquí .
Además de las respuestas ya dadas, me gustaría agregar algunas cosas más. Los diseñadores de compiladores suelen ser muy buenos programadores. Los compiladores son muy importantes: la mayoría de la programación se realiza utilizando compiladores, por lo que es imperativo que el compilador sea de alta calidad. Por lo tanto, lo mejor para las empresas es hacer compiladores para poner a sus mejores personas en él (o al menos, muy buenos: a los mejores no les gustará el diseño de compiladores). A Microsoft le gustaría mucho que sus compiladores C y C ++ funcionen correctamente, o el resto de la compañía no puede hacer su trabajo.
Además, si está creando un compilador realmente complejo, no puede simplemente hackearlo. La lógica detrás de los compiladores es altamente compleja y fácil de formalizar. Por lo tanto, estos programas a menudo se construirán de una manera muy 'robusta' y genérica, lo que tiende a generar menos errores.
fuente