
Comprobador de duraciones y otros tipos de refinamiento
Comprobador de
Lifetimes (vida útil) y otros
Refinement types (tipos de refinamiento)
para Zig
Vídeo: https://www.youtube.com/watch?v=mf0WzTOe-40 Patrocinio: https://buymeacoffee.com/dnautics
discutir en hn: https://news.ycombinator.com/item?id=42923829
discutir en lobste.rs: https://lobste.rs/s/9sitsj/clr_checker_for_lifetimes_other
vídeo de demostración en vivo: https://www.youtube.com/watch?v=ZY_Z-aGbYm8
Este proyecto crea un transpilador Zig para el compilador Zig, que transforma AIR (Intermediate Representation Abstracta) en código fuente Zig que realiza análisis estático en tiempo de compilación. El analizador generado detecta problemas de seguridad de memoria como uso antes de asignación, uso después de liberación, fugas de puntero de pila, así como UB específicos de Zig como aserciones de no nulidad, violaciones de uniones etiquetadas o mal uso de fieldParentPtr.
El objetivo es aportar garantías de seguridad de memoria al nivel de Rust a Zig mediante análisis estático de AIR, sin cambiar el lenguaje en sí.
CLR depende de una versión bifurcada del compilador Zig (incluida como submódulo en zig/) que agrega soporte para enrutar AIR a complementos externos. Cuando se invoca con -ofmt=air -fair-out=<plugin.so>, el compilador carga la biblioteca compartida especificada y le pasa el AIR generado para su procesamiento.
CLR está diseñado para empujar los programas hacia patrones de ciclo de vida que sean explícitos y verificables localmente, no simplemente para reconocer todo programa Zig técnicamente válido. Cuando dos representaciones son posibles, CLR prefiere aquella que hace visible el estado del recurso en el tipo y en la estructura de flujo de control.
Por ejemplo, evite cerrar condicionalmente un descriptor de archivo no opcional:
const file = try std.fs.cwd().openFile(path, .{});
if (should_close) {
file.close(); // Mal: el archivo queda ambiguamente abierto después de esta rama.
}
Prefiera representar la propiedad condicional con un opcional:
var file: ?std.fs.File = null;
if (should_open) {
file = try std.fs.cwd().openFile(path, .{});
}
if (file) |open_file| {
open_file.close();
}
Cerrar condicionalmente un descriptor no opcional deja su ciclo de vida ambiguo después de la rama. La política prevista de CLR es rechazar ese patrón en lugar de llevar un estado permanente de "tal vez cerrado".
El mismo principio se aplica a los punteros asignados. No libere a través de un puntero derivado:
const allocation = try allocator.alloc(u8, size);
const payload = allocation[header_size..];
allocator.free(payload); // Mal: payload no es la base de la asignación.
Mantenga disponible el puntero base de la asignación para la desasignación, y use los punteros derivados solo para acceso:
const allocation = try allocator.alloc(u8, size);
defer allocator.free(allocation);
const payload = allocation[header_size..];
use(payload);
Liberar un puntero de campo, subsegmento o puntero producido por aritmética es rechazado a menos que una regla interna documentada restablezca la procedencia de la base de asignación.
Estas políticas son estrictas por defecto porque producen código con ciclos de vida de recursos
más simples y más revisables. Un mecanismo futuro de anotación unsafe permitirá
que GIDs u operaciones seleccionadas opten por no participar en análisis individuales. Eso
respaldará código que acepta deliberadamente comprobaciones más débiles a cambio de
rendimiento, sin debilitar el modelo predeterminado para el resto del programa.
Esta es una reescritura activa del prototipo original basado en Elixir en Zig. La implementación en Zig se carga como un complemento del compilador y analiza AIR directamente.
Actualmente implementado:
std.mem.Allocator:
create/destroy - asignación de un solo elementoalloc/free - asignación de segmentos (incluyendo alignedAlloc, allocSentinel, etc.)realloc/remap - reasignación de segmentos con seguimiento de segmento anterior liberadodupe/dupeZ - duplicación de segmentosinit/deinit/allocator - ciclo de vida completo del arenastd.process.args,
std.mem.asBytes, std.HashMap y APIs de asignador/archivostd.HashMap con identidad de metadatos canónicos/almacenamiento clave/valor
a través de put, get, getPtr e iteración de valoresposix.open/close/dup/dup2/socket/accept/epoll_create/pipePlaneado (consulte LIMITATIONS.md para más detalles):