Cómo funciona Bagron
Este documento describe la implementación actual del compilador y sus límites. No debe interpretarse como una garantía formal de seguridad ni como una promesa sobre funcionalidades que todavía no estén implementadas.
Qué es Bagron
Bagron es un lenguaje compilado y estáticamente tipado. La implementación
actual recibe archivos .bg, valida su semántica y genera código nativo a
través de LLVM. No hay una máquina virtual de Bagron ni un recolector de basura
integrado.
El control de memoria es explícito en los tipos que lo necesitan: un
buffer se reserva y libera con las operaciones previstas por el lenguaje,
mientras que un puntero crudo no lleva longitud ni ownership implícitos. Los
punteros crudos, extern y el ensamblador son fronteras en las que el programa
puede salir de las comprobaciones estáticas de Bagron.
Flujo de compilación
Para un módulo ejecutable, el flujo es, en términos prácticos, el siguiente:
- Se preprocesan las directivas admitidas, como las condiciones de
compilación,
#prepy#link. - Gram analiza el fuente mediante la interfaz AOT/FFI y produce GIR. Bagron convierte ese resultado en su árbol interno. La ruta actual requiere que la biblioteca nativa de esa interfaz esté disponible; no usa un parser Python alternativo silencioso cuando falta.
- Se cargan los imports, se comprueban las rutas y los targets de los módulos, y se registran sus declaraciones públicas y privadas.
- Se ejecutan las comprobaciones semánticas: tipos, firmas, ámbitos, flujo,
reglas de punteros y arrays, llamadas
@unsafe, ownership y metadatos implícitos comoLen_t,Size_tyRegion_t. - El backend genera LLVM IR y lo verifica. Según el perfil seleccionado, conserva ese IR o aplica optimizaciones; el IR resultante vuelve a verificarse antes de generar el objeto y enlazar el artefacto final.
Un módulo ejecutable debe tener func main() int32:. Su retorno entero es el
código de salida, salvo que el flujo termine mediante una función Never.
Los módulos importados no pueden cambiar el target ni el perfil elegidos por
el módulo de entrada.
La interfaz de línea de comandos actual no expone una opción independiente
strict. Las opciones antiguas con ese nombre no deben documentarse como
parte del flujo vigente.
Qué significa el modo estricto
En la implementación actual, las comprobaciones estrictas forman parte del camino normal del compilador; no hay un modo permisivo paralelo. Entre otras cosas:
- los enteros tienen anchura y signo explícitos; no existe un
intouintgenérico; - no se aceptan conversiones implícitas de signo ni estrechamientos, y los literales se comprueban contra el rango del tipo destino;
- los tipos nominales, las formas de arrays y sus metadatos deben coincidir;
- las condiciones requieren
bool, sin convertir automáticamente un entero; - las funciones no pueden devolver directamente un puntero crudo, un array o
un
buffer; - el código seguro no puede llamar a una función
@unsafe,externo de ensamblador; - los errores semánticos detienen la compilación antes de que el backend produzca el artefacto final.
@unsafe es una excepción localizada por función, no un modo global de
compilación. Permite operaciones que el compilador no puede probar, como
determinados accesos mediante punteros o índices dinámicos de arrays, pero no
elimina las reglas de tipos, formas, firmas, metadatos ni el contrato de
main.
Seguridad real
Lo que sí comprueba el código seguro
- La procedencia conocida de las direcciones que se usan en operaciones seguras.
- Los índices literales de arrays y los índices dinámicos cuando existe una prueba de rango aplicable al array y al camino de control concretos.
- Los límites de un
bufferen tiempo de ejecución; un índice literal fuera de su capacidad se rechaza en compilación. - Que una función segura no llame a código marcado como inseguro.
- Reglas conservadoras de ownership para
buffery handlesopaque, incluyendo estados consumidos o cerrados. - Trampas para casos que el backend detecta en ejecución, como una desreferencia nula, un paso inválido o ciertos límites de destino.
Lo que no garantiza
Bagron no es un lenguaje formalmente probado ni garantiza ausencia de todos los errores de memoria, errores lógicos, carreras, fallos del sistema o comportamientos incorrectos en código extranjero. En particular:
@unsafe,@assembleryexternatraviesan la frontera de las pruebas estáticas. La validez de direcciones externas, lifetime, ABI y efectos del código extranjero queda bajo responsabilidad del programa y de su entorno.- Un puntero crudo no contiene por sí mismo longitud, ownership ni una prueba de que el objeto siga vivo.
@unsafeno vuelve válido un puntero nulo o colgante; solamente permite que el cuerpo realice operaciones que el modo seguro no puede justificar.- Una verificación del compilador no convierte el programa en resistente a entradas maliciosas, fallos de lógica o defectos del propio compilador.
Por tanto, “seguro” en Bagron significa “sujeto a las comprobaciones estáticas y dinámicas implementadas para esa operación”, no “memory-safe en cualquier circunstancia”.
Memoria y ownership
El compilador mantiene estados de ownership durante el análisis; esos estados son metadatos semánticos y no se emiten como campos ocultos en el ABI.
- Un
bufferposee almacenamiento y capacidad. Las variables locales y globales empiezan como propietarias; los parámetros se reciben como prestados.Delconsume un buffer propietario y una segunda utilización o liberación se rechaza. - No hay copia implícita de
buffer. Los merges de ramas y los bucles se tratan conservadoramente cuando no puede probarse que el estado siga siendo válido. - Un
bufferderivado no puede cruzar unexterncomo si fuera un puntero ABI ordinario. - Los handles
opaquetambién tienen estados de propietario, prestado y cerrado; las operacionesclose_*consumen el handle propietario.
Estas reglas no introducen un recolector de basura. La liberación del almacenamiento se realiza mediante las operaciones y hooks definidos por el lenguaje y su backend.
Perfiles de optimización
El perfil se selecciona con clause Optimizations en el módulo de entrada:
| Perfil | Comportamiento actual |
|---|---|
dev |
Conserva el IR emitido por Bagron después de verificarlo; sirve para inspección y comparación. |
prod |
Es el perfil predeterminado. Aplica el pipeline de optimización de producción y expande los inline explícitos admitidos. |
high |
Usa un nivel de generación de código más alto y un pipeline más agresivo. |
Los perfiles cambian el IR y el coste de generación, no las reglas semánticas ni el límite entre código seguro e inseguro. Todos pasan por la verificación de LLVM correspondiente.
Módulos, targets y ABI
Los imports se resuelven dentro de la raíz del proyecto y se rechazan las
rutas absolutas, el traversal y los enlaces que terminen fuera de esa raíz.
Los ciclos de imports son errores. Las cláusulas System, Arch y Boot de
los módulos se contrastan con el target elegido por el módulo de entrada.
extern declara una firma ABI para código fuera de Bagron. Sus restricciones
son deliberadas: no todos los tipos del lenguaje son tipos ABI, y los
descriptores administrados no se pueden tratar como punteros extranjeros sin
una representación compatible declarada. extern, al igual que el
ensamblador, se considera una llamada insegura desde una función segura.
Límites actuales relevantes
- La ruta principal depende de Gram y de su biblioteca AOT/FFI instalada.
- El análisis estricto es el comportamiento normal, pero
@unsafey las fronteras extranjeras siguen existiendo por diseño. - No hay recolector de basura,
intgenérico, retorno directo de punteros crudos, ni conversiones implícitas generales para hacer más permisivo el lenguaje. - Los detalles de targets, perfiles, tipos, arrays, punteros, ownership y ABI deben consultarse en sus documentos específicos; este archivo resume sus contratos y no los sustituye.