provekit_noirc_driver 1.0.0-beta.20-alpha.1

Noir compiler driver.
Documentation
pub mod ctstring;
pub mod expr;
pub mod format_string;
pub mod function_def;
pub mod module;
pub mod op;
pub mod trait_constraint;
pub mod trait_def;
pub mod trait_impl;
pub mod typ;
pub mod type_def;
pub mod typed_expr;
pub mod quoted;
pub mod unresolved_type;

use crate::default::Default;

/// Calling unquote as a macro (via `unquote!(arg)`) will unquote
/// its argument. Since this is the effect `!` already does, `unquote`
/// itself does not need to do anything besides return its argument.
// docs:start:unquote
pub comptime fn unquote(code: Quoted) -> Quoted {
    // docs:end:unquote
    code
}

/// Returns the type of any value
#[builtin(type_of)]
// docs:start:type_of
pub comptime fn type_of<T>(x: T) -> Type {}
// docs:end:type_of

// docs:start:derive_example
// These are needed for the unconstrained hashmap we're using to store derive functions
use crate::collections::umap::UHashMap;
use crate::hash::BuildHasherDefault;
use crate::hash::poseidon2::Poseidon2Hasher;

// A derive function is one that given a type definition can
// create us a quoted trait impl from it.
pub comptime type DeriveFunction = fn(TypeDefinition) -> Quoted;

// We'll keep a global HANDLERS map to keep track of the derive handler for each trait
comptime mut global HANDLERS: UHashMap<TraitDefinition, DeriveFunction, BuildHasherDefault<Poseidon2Hasher>> =
    UHashMap::default();

// Given a type definition and a vector of traits to derive, create trait impls for each.
// This function is as simple as iterating over the vector, checking if we have a trait
// handler registered for the given trait, calling it, and appending the result.
// docs:start:derive
#[varargs]
pub comptime fn derive(s: TypeDefinition, traits: [TraitDefinition]) -> Quoted {
    // docs:end:derive
    let mut result = quote {};

    for trait_to_derive in traits {
        let handler = HANDLERS.get(trait_to_derive);
        assert(handler.is_some(), f"No derive function registered for `{trait_to_derive}`");

        let trait_impl = handler.unwrap()(s);
        result = quote { $result $trait_impl };
    }

    result
}
// docs:end:derive_example

// docs:start:derive_via
// To register a handler for a trait, just add it to our handlers map
// docs:start:derive_via_signature
pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) {
    // docs:end:derive_via_signature
    HANDLERS.insert(t, f);
}
// docs:end:derive_via

/// `make_impl` is a helper function to make a simple impl, usually while deriving a trait.
/// This impl has a couple assumptions:
/// 1. The impl only has one function, with the signature `function_signature`
/// 2. The trait itself does not have any generics.
///
/// While these assumptions are met, `make_impl` will create an impl from a TypeDefinition,
/// automatically filling in the required generics from the type, along with the where clause.
/// The function body is created by mapping each field with `for_each_field` and joining the
/// results with `join_fields_with`. The result of this is passed to the `body` function for
/// any final processing - e.g. wrapping each field in a `StructConstructor { .. }` expression.
///
/// See `derive_eq` and `derive_default` for example usage.
// docs:start:make_trait_impl
pub comptime fn make_trait_impl<Env1, Env2>(
    s: TypeDefinition,
    trait_name: Quoted,
    function_signature: Quoted,
    for_each_field: fn[Env1](Quoted) -> Quoted,
    join_fields_with: Quoted,
    body: fn[Env2](Quoted) -> Quoted,
) -> Quoted {
    // docs:end:make_trait_impl
    let typ = s.as_type();

    let mut impl_generics = [].as_vector();
    let mut where_clause = [].as_vector();
    for g in s.generics() {
        let (typ, numeric_type) = g;
        impl_generics = impl_generics.push_back(quote { $typ });
        if numeric_type.is_none() {
            where_clause = where_clause.push_back(quote { $typ: $trait_name });
        }
    }

    let impl_generics = impl_generics.join(quote {, });
    let where_clause = where_clause.join(quote {, });

    // `for_each_field(field1) $join_fields_with for_each_field(field2) $join_fields_with ...`
    let fields = s.fields_as_written().map(|(name, _, _)| for_each_field(name));
    let body = body(fields.join(join_fields_with));

    quote {
        impl<$impl_generics> $trait_name for $typ where $where_clause {
            $function_signature {
                $body
            }
        }
    }
}

mod tests {
    use crate::meta::ctstring::AsCtString;
    use crate::meta::derive_via;

    // docs:start:quote-example
    comptime fn quote_one() -> Quoted {
        quote { 1 }
    }

    #[test]
    fn returning_versus_macro_insertion() {
        comptime {
            // let _a: Quoted = quote { 1 };
            let _a: Quoted = quote_one();

            // let _b: Field = 1;
            let _b: Field = quote_one!();

            // Since integers default to fields, if we
            // want a different type we have to explicitly cast
            // let _c: i32 = 1 as i32;
            let _c: i32 = quote_one!() as i32;
        }
    }
    // docs:end:quote-example

    // docs:start:derive-field-count-example
    trait FieldCount {
        fn field_count() -> u32;
    }

    #[derive_field_count]
    struct Bar {
        x: Field,
        y: [Field; 2],
    }

    comptime fn derive_field_count(s: TypeDefinition) -> Quoted {
        let typ = s.as_type();
        let field_count = s.fields_as_written().len();
        quote {
            impl FieldCount for $typ {
                fn field_count() -> u32 {
                    $field_count
                }
            }
        }
    }
    // docs:end:derive-field-count-example

    // docs:start:annotation-arguments-example
    #[assert_field_is_type(quote { i32 }.as_type())]
    struct MyStruct {
        my_field: i32,
    }

    comptime fn assert_field_is_type(s: TypeDefinition, typ: Type) {
        // Assert the first field in `s` has type `typ`
        let fields = s.fields([]);
        assert_eq(fields[0].1, typ);
    }
    // docs:end:annotation-arguments-example

    // docs:start:annotation-varargs-example
    #[assert_three_args(1, 2, 3)]
    struct MyOtherStruct {
        my_other_field: u32,
    }

    #[varargs]
    comptime fn assert_three_args(_s: TypeDefinition, args: [Field]) {
        assert_eq(args.len(), 3);
    }
    // docs:end:annotation-varargs-example

    // docs:start:big-derive-usage-example
    // Finally, to register a handler we call the above function as an annotation
    // with our handler function.
    #[derive_via(derive_do_nothing)]
    trait DoNothing {
        fn do_nothing(self);
    }

    comptime fn derive_do_nothing(s: TypeDefinition) -> Quoted {
        // This is simplified since we don't handle generics or where clauses!
        // In a real example we'd likely also need to introduce each of
        // `s.generics()` as well as a trait constraint for each generic
        // to ensure they also implement the trait.
        let typ = s.as_type();
        quote {
            impl DoNothing for $typ {
                fn do_nothing(self) {
                    // Traits can't tell us what to do
                    println("something");
                }
            }
        }
    }

    // Since `DoNothing` is a simple trait which:
    // 1. Only has one method
    // 2. Does not have any generics on the trait itself
    // We can use `std::meta::make_trait_impl` to help us out.
    // This helper function will generate our impl for us along with any
    // necessary where clauses and still provides a flexible interface
    // for us to work on each field on the struct.
    comptime fn derive_do_nothing_alt(s: TypeDefinition) -> Quoted {
        let trait_name = quote { DoNothing };
        let method_signature = quote { fn do_nothing(self) };

        // Call `do_nothing` recursively on each field in the struct
        let for_each_field = |field_name| quote { self.$field_name.do_nothing(); };

        // Some traits like Eq want to join each field expression with something like `&`.
        // We don't need that here
        let join_fields_with = quote {};

        // The body function is a spot to insert any extra setup/teardown needed.
        // We'll insert our println here. Since we recur on each field, we should see
        // one println for the struct itself, followed by a println for every field (recursively).
        let body = |body| quote {
            println("something");
            $body
        };
        crate::meta::make_trait_impl(
            s,
            trait_name,
            method_signature,
            for_each_field,
            join_fields_with,
            body,
        )
    }
    // docs:end:big-derive-usage-example

    impl DoNothing for Bar {
        fn do_nothing(_: Self) {}
    }

    // docs:start:concatenate-example
    comptime fn concatenate(q1: Quoted, q2: Quoted) -> Quoted {
        assert(q1.tokens().len() <= 1);
        assert(q2.tokens().len() <= 1);

        f"{q1}{q2}".quoted_contents()
    }

    // The CtString type is also useful for a compile-time string of unbounded size
    // so that you can append to it in a loop.
    comptime fn double_spaced(q: Quoted) -> CtString {
        let mut result = "".as_ctstring();

        for token in q.tokens() {
            if result != "".as_ctstring() {
                result = result.append_str("  ");
            }
            result = result.append_fmtstr(f"{token}");
        }

        result
    }

    #[test]
    fn concatenate_test() {
        comptime {
            let result = concatenate(quote {foo}, quote {bar});
            assert_eq(result, quote {foobar});

            let result = double_spaced(quote {foo bar 3}).as_quoted_str!();
            assert_eq(result, "foo  bar  3");
        }
    }
    // docs:end:concatenate-example

    // This function is just to remove unused warnings
    fn remove_unused_warnings() {
        let _: Bar = Bar { x: 1, y: [2, 3] };
        let _: MyStruct = MyStruct { my_field: 1 };
        let _: MyOtherStruct = MyOtherStruct { my_other_field: 2 };
        let _ = derive_do_nothing(crate::panic::panic(""));
        let _ = derive_do_nothing_alt(crate::panic::panic(""));
        if false {
            remove_unused_warnings();
        }
    }
}