mago-allocator 1.40.1

Arena allocation for Mago: a unified `Arena` trait over thread-local and thread-safe bump allocators.
Documentation
#![allow(clippy::inline_trait_bounds)]
#![allow(clippy::missing_assert_message)]
#![allow(clippy::wildcard_imports)]

use std::marker::PhantomData;

use rayon::prelude::*;

use mago_allocator::prelude::*;

struct Ast<'arena> {
    _phantom: PhantomData<&'arena ()>,
}

struct Ir<'arena, T> {
    _phantom: PhantomData<&'arena T>,
}

struct DefinitionTable<'arena> {
    _phantom: PhantomData<&'arena ()>,
}

impl CopyInto for DefinitionTable<'_> {
    type Output<'arena> = DefinitionTable<'arena>;

    fn copy_into<'arena, A>(&self, _arena: &'arena A) -> Self::Output<'arena>
    where
        A: Arena,
    {
        DefinitionTable { _phantom: PhantomData }
    }
}

struct SymbolTable<'arena, A: Arena> {
    _symbols: HashMap<'arena, u32, DefinitionTable<'arena>, A>,
}

struct Type<'arena> {
    _phantom: PhantomData<&'arena ()>,
}

fn parse<'arena, A: Arena>(arena: &'arena A, _source: &str) -> &'arena Ast<'arena> {
    arena.alloc(Ast { _phantom: PhantomData })
}

fn lower<'ast, 'arena, A: Arena>(arena: &'arena A, _ast: &'ast Ast<'ast>) -> &'arena Ir<'arena, ()> {
    arena.alloc(Ir { _phantom: PhantomData })
}

fn scan<'ir, 'arena, A: Arena>(_arena: &'arena A, _ir: &'ir Ir<'ir, ()>) -> DefinitionTable<'arena> {
    DefinitionTable { _phantom: PhantomData }
}

fn link<'defs, 'arena, A: Arena>(
    arena: &'arena A,
    definitions: &'defs [DefinitionTable<'defs>],
) -> SymbolTable<'arena, A> {
    let mut symbols: HashMap<'arena, u32, DefinitionTable<'arena>, A> = HashMap::new_in(arena);
    for (index, definition) in definitions.iter().enumerate() {
        symbols.insert(index as u32, definition.copy_into(arena));
    }

    SymbolTable { _symbols: symbols }
}

fn inference<'arena, A: Arena, S: Arena>(
    arena: &'arena A,
    scratch: &S,
    _symbol_table: &SymbolTable<'arena, A>,
    _ir: &Ir<'_, ()>,
) -> &'arena Ir<'arena, Type<'arena>> {
    let _ = scratch;
    arena.alloc(Ir { _phantom: PhantomData })
}

fn check<'arena>(_ir: &Ir<'arena, Type<'arena>>) -> bool {
    true
}

fn compile(arena: &SharedArena, sources: &[&str]) -> bool {
    let ir_arena = SharedArena::with_chunk_size(2 * 1024 * 1024);
    let definitions_arena = SharedArena::new();

    let parse_and_scan_file = |scratch: &mut ScopedArena<'_>, source: &&str| {
        let ast = parse(&*scratch, source);
        let ir = lower(&ir_arena, ast);
        let definition_table = scan(&definitions_arena, ir);
        scratch.reset();
        (ir, definition_table)
    };

    let ir_and_definitions: Vec<'_, _, SharedArena> =
        sources.par_iter().map_init(|| ir_arena.scoped(), parse_and_scan_file).collect_in(&ir_arena);

    let file_count = ir_and_definitions.len();
    let mut untyped_irs: Vec<'_, &Ir<'_, ()>, SharedArena> = Vec::with_capacity_in(file_count, &ir_arena);
    let mut definition_tables: Vec<'_, DefinitionTable<'_>, SharedArena> = Vec::with_capacity_in(file_count, &ir_arena);
    for (ir, definition_table) in ir_and_definitions {
        untyped_irs.push(ir);
        definition_tables.push(definition_table);
    }

    let symbol_table = link(arena, definition_tables.as_slice());
    drop(definition_tables);
    drop(definitions_arena);

    let infer_and_check_file = |scratch: &mut ScopedArena<'_>, ir: &&Ir<'_, ()>| {
        let typed_ir = inference(arena, &*scratch, &symbol_table, ir);
        scratch.reset();
        check(typed_ir)
    };

    let all_files_passed = untyped_irs
        .as_slice()
        .par_iter()
        .map_init(|| arena.scoped(), infer_and_check_file)
        .all(|file_passed| file_passed);

    drop(untyped_irs);
    drop(ir_arena);

    all_files_passed
}

fn main() {
    let arena = SharedArena::new();
    assert!(compile(&arena, &["<?php echo 1;", "<?php echo 2;"]));
}