¿Por qué los compiladores son tan confiables?

58

Usamos compiladores diariamente como si su corrección fuera cierta, pero los compiladores también son programas y potencialmente pueden contener errores. Siempre me he preguntado por esta infalible robustez. ¿Alguna vez has encontrado un error en el compilador? ¿Qué fue y cómo se dio cuenta de que el problema estaba en el compilador?

... y cómo hacen que hacen que los compiladores sean tan confiables?

    
pregunta EpsilonVector 25.02.2011 - 18:24

19 respuestas

94

Se someten a pruebas exhaustivas mediante el uso de miles o incluso millones de desarrolladores a lo largo del 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, se trata de traducir una entrada de texto en un formato muy específico para enviar en otro tipo de formato bien definido (algún tipo de código de bytec o código de máquina). Por lo tanto, es fácil crear y verificar casos de prueba.

Además, por lo general, los errores también son fáciles de reproducir: aparte de la plataforma exacta y la información de la versión del compilador, por lo general, todo lo que necesita es un código de entrada. Sin mencionar que los usuarios del compilador (siendo ellos mismos desarrolladores) tienden a dar informes de errores mucho más precisos y detallados que cualquier usuario promedio de computadoras :-)

    
respondido por el Péter Török 25.02.2011 - 18:28
57

Además de todas las grandes 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ú. Entonces empecé a escribir compiladores profesionalmente, y déjame decirte que hay muchos errores ahí.

No ves los errores porque escribes un código que es como el 99.999% del 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 sofisticado o extraño, porque usted es un desarrollador normal que resuelve problemas comerciales normales.

No ves ningún error en el compilador porque los errores del compilador no se encuentran en los escenarios de código normal, 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ódigo loco todo el día todos los días, y para mí los compiladores parecen estar llenos de errores.

Si te sentaste con la especificación de lenguaje de cualquier idioma, tomaste cualquier implementación del compilador para ese lenguaje y realmente intentaste determinar si el compilador implementó exactamente la especificación o no, concentrándote en casos oscuros de esquina, muy pronto lo harás. Estar encontrando errores de compilación con bastante frecuencia. Déjame darte un ejemplo, aquí hay un error del compilador de C # que encontré literalmente hace cinco minutos.

static void N(ref int x){}
...
N(ref 123);

El compilador da tres errores.

  • Un argumento de referencia o de salida debe ser una variable asignable.
  • La mejor coincidencia para N (ref int x) tiene argumentos no válidos.
  • Falta "ref" en el argumento 1.

Obviamente, el primer mensaje de error es correcto y el tercero es un error. El algoritmo de generación de errores está tratando de averiguar por qué el primer argumento no fue válido, lo observa, ve que es una constante y no regresa al código fuente para verificar si estaba marcado como "ref"; más bien, asume que nadie sería tan tonto como para marcar una constante como referencia, y decide que falta la referencia.

No está claro cuál es el tercer mensaje de error correcto, pero no lo es. De hecho, tampoco está claro si el mensaje de error second es correcto. ¿Debería fallar la resolución de sobrecarga o debería tratarse "ref 123" como un argumento ref del tipo correcto? Ahora tendré que pensarlo un poco y hablarlo con el equipo de clasificació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 tratar de pasar el 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 intentando romper el compilador. Si lo intentas, también verías los errores.

    
respondido por el Eric Lippert 01.03.2011 - 00:26
49

¿Estás bromeando? Los compiladores tienen errores también, cargas realmente.

GCC es probablemente el compilador de código abierto más famoso del planeta y echa un vistazo a su base de datos de errores: enlace

Entre GCC 3.2 y GCC 3.2.3, observe cuántos errores se corrigieron: enlace

En cuanto a otros como Visual C ++, ni siquiera quiero comenzar.

¿Cómo hacer que los compiladores sean confiables? Bueno, para empezar tienen cargas y cargas de pruebas unitarias. Y todo el planeta los usa, así que no hay escasez de probadores.

Sin embargo, en serio, los desarrolladores de compiladores que me gustan creen que son programadores superiores y, aunque no son infalibles, sí tienen un gran impacto.

    
respondido por el Fanatic23 25.02.2011 - 17:52
19

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 pósters han señalado, creo que la confiabilidad del compilador es a menudo 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% del tiempo lo es) y cambias tu código para solucionar el problema del compilador en lugar de hacerlo al revés. Por ejemplo, el bloqueo de código en una configuración de optimización alta es definitivamente un error del compilador, pero la mayoría de las personas simplemente lo establecen un poco más bajo y continúan sin informar el error.

    
respondido por el Karl Bielefeldt 25.02.2011 - 17:41
13

Los compiladores tienen varias propiedades que llevan a su corrección:

  • El dominio es muy conocido e investigado. El problema está bien definido y las soluciones ofrecidas están bien definidas.
  • Las pruebas automatizadas son suficientes para probar que los compiladores funcionan correctamente
  • Los compiladores tienen pruebas muy extensas, generalmente públicas, automatizadas y de unidad, que se han ido acumulando a lo largo del tiempo para cubrir más espacio de error que para la mayoría de los otros programas.
  • Los compiladores tienen una gran cantidad de globos oculares observando sus resultados
respondido por el blueberryfields 25.02.2011 - 17:26
12
  

Usamos compiladores diariamente

     

... ¿Y cómo hacen que los compiladores sean tan confiables?

No lo hacen. Hacemos. Porque todos los usan todo el tiempo, los errores se encuentran rápidamente.

Es un juego de números. Debido a que los compiladores se utilizan de manera tan generalizada, es muy probable que cualquier error provoque algún error , pero debido a que hay una cantidad tan grande de usuarios, es altamente improbable que eso alguien te será 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 un trozo de código similar antes de que lo hicieras, por lo que si su era un error, los hubiera afectado a ellos, no a ti, por lo que, desde tu individuo punto de vista, parece que el error nunca estuvo allí.

Por supuesto, además de eso, puede agregar todas las demás 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 tienen más cuidado cuando lo hacen. Por lo general, son fáciles de probar y fáciles de realizar pruebas de estrés o fuzz. Los usuarios del compilador tienden a ser expertos programadores, lo que lleva a informes de errores de alta calidad. Y al revés: los escritores de compiladores tienden a ser usuarios de su propio compilador.

    
respondido por el Jörg W Mittag 25.02.2011 - 18:29
11

Además de todas las respuestas, me gustaría agregar:

Yo creo muchas veces, los vendedores están comiendo su propia comida para perros. Es decir, están escribiendo los compiladores en sí mismos.

    
respondido por el DevSolo 25.02.2011 - 17:19
7

Me he encontrado con errores de compilación a menudo.

Puedes encontrarlos en las esquinas más oscuras donde hay menos probadores. Por ejemplo, para encontrar errores en GCC debes intentar:

  • Construye un compilador cruzado. Encontrará literalmente docenas de errores en los scripts de configuración y creación de GCC. Algunos resultan en fallas de compilación durante la compilación de GCC y otros resultarán en fallas en el compilador cruzado para construir ejecutables de trabajo.
  • Cree una versión de GCC de Itanium usando profile-bootstrap. Las últimas dos veces que intenté esto en GCC 4.4 y 4.5 no se pudo producir un controlador de excepciones de C ++ que funcionara. La construcción no optimizada funcionó bien. Nadie parecía interesado en corregir el error que informé y yo dejé de solucionarlo yo mismo después de intentar investigar lo que estaba rompiendo las especificaciones de memoria asm de GCC.
  • Intente crear su propio GCJ operativo a partir de las últimas novedades sin seguir un script de compilación de distro. Te atrevo.
respondido por el Zan Lynx 25.02.2011 - 22:33
5

Varios motivos:

  • Los compiladores del compilador " comen su propia comida para perros ".
  • Los compiladores se basan en principios bien entendidos de CS.
  • Los compiladores están construidos con una clara especificación .
  • Los compiladores se prueban .
  • Los compiladores no siempre son muy confiables .
respondido por el Kramii 25.02.2011 - 18:10
4

Por lo general son muy buenos en -O0. De hecho, si sospechamos un error de compilación, comparamos -O0 con cualquier nivel que intentemos usar. Mayores niveles de optimización 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 época), pero últimamente son mucho más raros. Sin embargo, en la búsqueda de buenos números de marcas (u otros puntos de referencia importantes para la comercialización), la tentación de empujar los límites es grande. Tuvimos problemas hace unos años, cuando un proveedor (que no lleva el nombre) decidió hacer una violación del valor predeterminado de paréntesis, en lugar de una opción de compilación especial claramente etiquetada.

Puede ser difícil diagnosticar un error del compilador versus decir una referencia de memoria extraviada, una recompilación con diferentes opciones puede simplemente mezclar la posición relativa de los objetos de datos dentro de la memoria, por lo que no se sabe si es el código fuente de Heisenbug de su código fuente. o un compilador de buggy. Además, muchas optimizaciones realizan cambios legítimos en el orden de las operaciones, o incluso simplificaciones algebraicas en su álgebra, y éstas tendrán diferentes propiedades con respecto al redondeo de punto flotante y al desbordamiento / desbordamiento. Es difícil desentrañar estos efectos de los insectos REALES. La computación de punto flotante del 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 desentrañar.

    
respondido por el Omega Centauri 25.02.2011 - 18:14
4

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 debería aceptarse, o que un compilador acepte un código que debería haber sido rechazado.

    
respondido por el kevin cline 25.02.2011 - 18:45
3

Sí, encontré un error en el compilador de ASP.NET ayer mismo:

Cuando se usan modelos fuertemente tipados en 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 los dos ejemplos a continuación hacen que el compilador sea demasiado difícil de manejar:

ViewUserControl<System.Tuple<type1, type2, type3, type4, type5>>

No se compilaría como está pero lo haría si se elimina type5 .

ViewUserControl<System.Tuple<MyModel, System.Func<type1, type2, type3, type4>>>

Se compilaría si se eliminara type4 .

Tenga en cuenta que System.Tuple tiene muchas sobrecargas y puede tomar hasta 16 parámetros (es una locura, lo sé).

    
respondido por el user8685 25.02.2011 - 17:13
3
  

¿Alguna vez has encontrado un error en el   compilador en sí?   Que fue y como   te diste cuenta que el problema estaba en el   compilador en sí?

Yup!

Los dos más memorables fueron los primeros dos que encontré. Ambos estaban en el compilador Lightspeed C para Macs de 680x0 en 1985-7.

El primero fue cuando, en algunas circunstancias, el operador entero de postincremento no hizo nada; en otras palabras, en un fragmento de código en particular, "i ++" simplemente no le hizo nada a "i". Me estaba sacando el pelo hasta que vi un desmontaje. Luego simplemente 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 pensada que salió mal. Los primeros Mac 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 simplemente generara las instrucciones de operación del disco en el lugar en el código del objeto, el compilador 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 68000 CPU, pero cuando ejecutaste el mismo código en una CPU 68020, a menudo haría cosas extrañas. Resultó que una nueva característica del 68020 era una instrucción primitiva de 256 bytes de caché de instrucciones. Al ser los primeros días con cachés de CPU, no tenía la menor idea de que el caché estuviera "sucio" y necesitara ser rellenado; Supongo que los diseñadores de CPU de Motorola no pensaron en auto modificarse el código. Por lo tanto, si realizó dos operaciones de disco lo suficientemente cerca en su secuencia de ejecución, y el tiempo de ejecución de Lightspeed compiló las instrucciones reales en la misma ubicación en la pila, la CPU consideraría erróneamente que tuvo un acierto de caché de instrucciones y ejecutó la primera operación del disco dos veces.

Nuevamente, darme cuenta de que fue necesario excavar un poco con un desensamblador y muchos pasos simples en un depurador de bajo nivel. Mi solución fue prefijar cada operación de disco con una llamada a una función que realizaba 256 instrucciones "NOP", que inundaron (y por lo tanto borraron) la memoria caché de instrucciones.

Durante los 25 años transcurridos desde entonces, he visto cada vez menos errores de compilación a lo largo del tiempo. Creo que hay un par de razones para eso:

  • Hay un conjunto cada vez mayor de pruebas de validación para compiladores.
  • Los compiladores modernos generalmente se dividen en dos o más partes, una de las cuales genera un código independiente de la plataforma (por ejemplo, la focalización de LLVM en lo que podría considerarse una CPU imaginaria), y otra que lo traduce en instrucciones para su hardware de destino real. En los compiladores multiplataforma, la primera parte se usa en todas partes, por lo que recibe toneladas de pruebas reales.
respondido por el Bob Murphy 25.02.2011 - 22:16
3

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 era en absoluto un punto de referencia (solo una llamada que no se usa comúnmente).

En general, ciertamente los compiladores de compiladores comerciales (en lugar de proyectos de pasatiempo) tendrán implementados procedimientos de control de calidad y pruebas muy extensos. Saben que sus compiladores son sus proyectos emblemáticos y que las fallas se verán muy mal en ellos, peor que 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 colegas que bien podrían seguir nuestro ejemplo. ejemplo. En muchas otras industrias, ese no es el caso, por lo que la pérdida potencial para un compilador como resultado de un error grave es mucho mayor que eso para decir un fabricante de software de edición de video.

    
respondido por el jwenting 01.03.2011 - 08:26
2

Cuando el comportamiento de su software es diferente cuando se compila con -O0 y con -O2, entonces ha encontrado un error en el compilador.

Cuando el comportamiento de su software es simplemente diferente al que usted espera, entonces es probable que el error esté en su código.

    
respondido por el mouviciel 25.02.2011 - 17:40
2

Los errores del compilador ocurren, pero tiendes a encontrarlos en esquinas extrañas ...

Hubo un error extraño en el compilador VAX VMS C de Digital Equipment Corporation en la década de 1990

(Estaba usando una cebolla en mi cinturón, como era la moda en ese momento)

Un punto y coma extraño en cualquier lugar que preceda a un bucle for se compilaría como el cuerpo del bucle for.

f(){...}
;
g(){...}

void test(){
  int i;
  for ( i=0; i < 10; i++){
     puts("hello");
  }
}

En el compilador en cuestión, el bucle se ejecuta solo una vez.

ve

f(){...}
g(){...}

void test(){
  int i;
  for ( i=0; i < 10; i++) ;  /* empty statement for fun */

  {
     puts("hello");
  }
}

Eso me costó mucho tiempo.

La versión anterior del compilador PIC C que (solíamos) infligía a la experiencia laboral, los estudiantes no podían generar un código que utilizara la interrupción de alta prioridad correctamente. Tuviste que esperar 2-3 años y actualizar.

El compilador de MSVC 6 tenía un error ingenioso en el enlazador, se produciría un error de segmentación y moriría de vez en cuando sin motivo. Una construcción limpia generalmente lo solucionó (pero suspiro no siempre).

    
respondido por el Tim Williscroft 28.02.2011 - 23:31
2

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. Sobre 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 vienen.

    
respondido por el Axel 23.10.2018 - 03:27
1

He visto varios errores de compilación, informé algunos (yo, 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.

La mayoría de ellos están presumiblemente muy familiarizados con cosas como el cálculo lambda, la verificación formal, la semántica denotativa, etc., cosas que un programador promedio como yo apenas puede comprender.

Además, generalmente hay una asignación bastante sencilla de entrada a salida en los compiladores, por lo que la depuración de un lenguaje de programación es probablemente mucho más fácil que la depuración, por ejemplo, un motor de blog.

    
respondido por el Rei Miyasaka 25.02.2011 - 22:32
1

Hace poco tiempo encontré un error en el compilador de C #, puedes ver cómo Eric Lippert (que está en el equipo de diseño de C #) descubrió cuál era el error here .

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 con compiladores, por lo que es imperativo que el compilador sea de alta calidad. Por lo tanto, es en el mejor interés de las empresas hacer compiladores para poner a su mejor gente en él (o al menos, muy buenos: a los mejores puede que no les guste el diseño del compilador). A Microsoft le gustaría que sus compiladores C y C ++ funcionen correctamente, o el resto de la compañía no puede hacer su trabajo.

Además, si estás construyendo un compilador realmente complejo, no puedes simplemente hackearlo juntos. 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, que tiende a dar como resultado menos errores.

    
respondido por el Alex ten Brink 23.05.2017 - 14:40

Lea otras preguntas en las etiquetas