capy-language / capy

🍊 A statically typed, compiled programming language, largely inspired by Jai, Odin, and Zig.
Apache License 2.0
64 stars 4 forks source link

Compile-time Parameters #34

Open NotAFlyingGoose opened 4 months ago

NotAFlyingGoose commented 4 months ago

Problem

Currently, there's no way to reapply comptime blocks to different circumstances. Types can be created at compile-time, but doing it is kind of pointless. Also, generics don't really exist outside of runtime stuff with core.Any.

Compile-time function parameters would solve a lot of these problems by allowing comptime blocks to be "reused" with different constants. This would also give way to compile-time generics on top of the already existing system of runtime generics.

Proposed Solution

Here's some syntax for what this might look like:

do_stuff_ig :: (comptime foo: i32, comptime bar: type) {
  some_array: [foo]bar;

  // .. do other stuff
}

do_stuff_ig(5, f32);

as you can see, the comptime keyword is used here as a tag or modifier of the parameter and not as a type. any given argument to one of these parameters would need to pass through hir_ty::GlobalInferenceCtx::get_const to confirm that it's constant.

But wait! Just like in the above example, comptime arguments might change the types of expressions. Any expression that refers to "some_array" might have one type during one call, and a different type during another call.

To solve this, hir_ty::Inferrable would need to have a variant that stores the Fqn + a ComptimeResult for each comptime argument. FileInference currently only has one map of Idx<hir::Expr> to types, but this would need to change so that each Inferrable gets a personal map for its expressions. This would allow a single Idx<hir::Expr> to have multiple types depending on the given comptime arguments.

After that the codegen crate would have to account for this polymorphism, compiling multiple functions depending on the arguments.

There are probably a few more wrinkles that would need to be ironed out but this is the basis for how it should be done.

Notes

A note on the syntax,

Vec :: (comptime sub_ty: type) -> type {
  // create the generic vector
}

// if you want to use `Vec` as a type, you still have to wrap it in a `comptime` block
My_Vec_i32 :: comptime Vec(i32);

This is cumbersome, and it would be nice if this weren't necessary. Although this is just food for thought for a future issue.