Skip to content

Commit

Permalink
Redesign func templates to allow template args.
Browse files Browse the repository at this point in the history
  • Loading branch information
fubark committed Jun 6, 2024
1 parent 2d432aa commit b87a37c
Show file tree
Hide file tree
Showing 31 changed files with 1,383 additions and 582 deletions.
71 changes: 45 additions & 26 deletions docs/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ There are `29` general keywords. This list categorizes them:
- [Operators](#operators): `or` `and` `not`
- [Variables](#variables): [`var`](#local-variables) [`context`](#context-variables)
- [Functions](#functions): `func` `return`
- [Coroutines](#fibers): `coinit` `coyield` `coresume`
- [Fibers](#fibers): `coinit` `coyield` `coresume`
- [Async](#async): `await`
- [Types](#custom-types): `type` `as`
- [Type embedding](#type-embedding): `use`
Expand Down Expand Up @@ -1004,7 +1004,7 @@ The dynamic type defers type checking to runtime. However, it also tracks its ow
* [Distinct types.](#distinct-types)
* [Traits.](#traits)
* [Union types.](#union-types)
* [Generic types.](#generic-types)
* [Type templates.](#type-templates)
* [Expand type template.](#expand-type-template)
</td>
</tr></table>
Expand Down Expand Up @@ -1311,7 +1311,7 @@ print s.!line --> 20
## Optionals.
Optionals provide **Null Safety** by forcing values to be unwrapped before they can be used.

The generic `Option` type is a choice type that either holds a `none` value or contains `some` value. The option template is defined as:
The `Option` template type is a choice type that either holds a `none` value or contains `some` value. The option template is defined as:
```cy
type Option[T type] enum:
case none
Expand Down Expand Up @@ -1477,8 +1477,8 @@ print s.area() --> 20
## Union types.
> _Planned Feature_
## Generic types.
Templates are used to specialize type declarations:
## Type templates.
Type declarations can include template parameters to create a type template:
```cy
type MyContainer[T type]:
id int
Expand All @@ -1489,7 +1489,7 @@ type MyContainer[T type]:
```

### Expand type template.
When the template is invoked with compile-time argument(s), a specialized version of the type is generated.
When the type template is invoked with template argument(s), a special version of the type is generated.

In this example, `String` can be used as an argument since it satisfies the `type` parameter constraint:
```cy
Expand Down Expand Up @@ -1725,9 +1725,10 @@ The `try catch` statement, `try else` and `try` expressions provide a way to cat
* [Function calls.](#function-calls)
* [Shorthand syntax.](#shorthand-syntax)
* [Call block syntax.](#call-block-syntax)
* [Generic functions.](#generic-functions)
* [Expand function template.](#expand-function-template)
* [Infer template function.](#infer-template-function)
* [Function templates.](#function-templates)
* [Explicit template call.](#explicit-template-call)
* [Expand function.](#expand-function)
* [Infer param type.](#infer-param-type)

[^top](#table-of-contents)

Expand Down Expand Up @@ -1902,27 +1903,45 @@ Button('Count'):
```
Arguments assigned in the call block can be unordered.
## Generic functions.
Templates are used to specialize function declarations:
## Function templates.
Function declarations can include template parameters to create a function template:
```cy
func add[T type](a T, b T) T:
func add(#T type, a T, b T) T:
return a + b
```
Template parameters are prefixed with `#`.
Unlike type templates, function templates allow template parameters to be declared alongside runtime parameters.
### Expand function template.
When the template is invoked with compile-time argument(s), a specialized version of the function is generated:
### Explicit template call.
When the function is invoked with template argument(s), a special version of the function is generated and used:
```cy
print add[int](1, 2) --> 3
print add[float](1, 2) --> 3.0
print add(int, 1, 2) --> 3
print add(float, 1, 2) --> 3.0
```
Note that invoking the template again with the same argument(s) returns the same generated function. In other words, the generated function is always memoized from the input parameters.
Note that invoking the function again with the same argument(s) uses the same generated function. In other words, the generated function is always memoized from the template arguments.
### Infer template function.
When invoking template functions, it is possible to infer the template parameters from the call arguments.
In the following example, `add[int]` and `add[float]` are inferred from the function calls:
### Expand function.
Since functions may contain template and runtime parameters, the index operator is used to expand the function with just template arguments and return the generated function:
```cy
print add(1, 2) --> 3
print add(1.0, 2.0) --> 3.0
var addInt = add[int]
print addInt(1, 2) --> 3
```
### Infer param type.
When a template parameter is declared in the type specifier, it's inferred from the argument's type:
```cy
func add(a #T, b T) T:
return a + b

print add(1, 2) --> 3
print add(1.0, 2.0) --> 3.0
```
In the above example, `add[int]` and `add[float]` were inferred from the function calls:
Nested compile-time parameters can also be inferred:
```cy
func set(m Map[#K, #V], key K, val V):
m.set(key, val)
```
# Modules.
Expand Down Expand Up @@ -3059,7 +3078,7 @@ print str.trim(.left, ' ')
* [Reflection.](#reflection)
* [Attributes.](#attributes)
* [Generics.](#generics)
* [Templates.](#templates)
* [Template specialization.](#template-specialization)
* [Macros.](#macros)
* [Compile-time execution.](#compile-time-execution)
Expand Down Expand Up @@ -3226,10 +3245,10 @@ Attributes start with `@`. They are used as declaration modifiers.
>
>Bind a function, variable, or type to the host. See [libcyber](#libcyber).
## Generics.
Generics enables parametric polymorphism for types and functions. Compile-time arguments are passed to templates to generate specialized code. This facilitates developing container types and algorithms that operate on different types.
## Templates.
Templates enables parametric polymorphism for types and functions. Template arguments are passed to templates to generate specialized code. This facilitates developing container types and algorithms that operate on different types.
See [Custom Types / Generic types](#generic-types) and [Functions / Generic functions](#generic-functions).
See [Custom Types / Type templates](#type-templates) and [Functions / Function templates](#function-templates).
### Template specialization.
> _Planned Feature_
Expand Down
1 change: 1 addition & 0 deletions src/ast.zig
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ pub const FuncParam = struct {
name_pos: u32 align(8),
name_len: u32,
typeSpec: ?*Node,
ct_param: bool,
};

pub const UseAlias = struct {
Expand Down
5 changes: 5 additions & 0 deletions src/bc_gen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ pub fn genAll(c: *cy.Compiler) !void {
log.tracev("prep type: {s}", .{stype.sym.name()});
const sym = stype.sym;

if (stype.info.ct_infer or stype.info.ct_ref) {
continue;
}

switch (sym.type) {
.object_t => {
const obj = sym.cast(.object_t);
Expand All @@ -55,6 +59,7 @@ pub fn genAll(c: *cy.Compiler) !void {
try c.vm.addFieldSym(@intCast(typeId), fieldSymId, @intCast(i), field.type);
}
},
.dummy_t,
.trait_t,
.int_t,
.float_t,
Expand Down
4 changes: 2 additions & 2 deletions src/builtins/builtins.zig
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,9 @@ const vm_types = [_]C.HostTypeEntry{
htype("any", C.CORE_TYPE(bt.Any)),
htype("type", C.CORE_TYPE(bt.Type)),
htype("List", C.CUSTOM_TYPE(null, listGetChildren, listFinalizer)),
htype("ListDyn", C.CORE_TYPE_EXT(bt.ListDyn, listGetChildren, listFinalizer)),
htype("ListDyn", C.CORE_TYPE_EXT(bt.ListDyn, listGetChildren, listFinalizer, true)),
htype("ListIterator", C.CUSTOM_TYPE(null, listIterGetChildren, null)),
htype("ListIterDyn", C.CORE_TYPE_EXT(bt.ListIterDyn, listIterGetChildren, null)),
htype("ListIterDyn", C.CORE_TYPE_EXT(bt.ListIterDyn, listIterGetChildren, null, true)),
htype("Tuple", C.CORE_TYPE(bt.Tuple)),
htype("Table", C.CORE_TYPE_DECL(bt.Table)),
htype("Map", C.CORE_TYPE(bt.Map)),
Expand Down
18 changes: 9 additions & 9 deletions src/builtins/builtins_vm.cy
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ type List[dyn] _

--| Creates a list with initial capacity of `n` and values set to `val`.
--| If the value is an object, it is shallow copied `n` times.
@host func List.fill[](val T, n int) List[T]
@host func List.fill(val #T, n int) List[T]

@host type ListIterator[T type] _:
@host func next() ?T
Expand Down Expand Up @@ -443,21 +443,21 @@ type Option[T type] enum:
@host type Future[T type] _

--| Returns a `Future[T]` that has a completed value.
func Future.complete[](val T) Future[T]:
func Future.complete(val #T) Future[T]:
return Future.complete_(val, (Future[T]).id())

@host -func Future.complete_[](val T, ret_type int) Future[T]
@host -func Future.complete_(val #T, ret_type int) Future[T]

func Future.new[]() Future[T]:
return Future.new_[T]((Future[T]).id())
func Future.new(#T type) Future[T]:
return Future.new_(T, (Future[T]).id())

@host -func Future.new_[](ret_type int) Future[T]
@host -func Future.new_(#T type, ret_type int) Future[T]

@host type FutureResolver[T type] _:
@host func complete(val T) void
@host func future() Future[T]

func FutureResolver.new[]() FutureResolver[T]:
return FutureResolver.new_[T]((Future[T]).id(), (FutureResolver[T]).id())
func FutureResolver.new(#T type) FutureResolver[T]:
return FutureResolver.new_(T, (Future[T]).id(), (FutureResolver[T]).id())

@host -func FutureResolver.new_[](future_t int, ret_t int) FutureResolver[T]
@host -func FutureResolver.new_(#T type, future_t int, ret_t int) FutureResolver[T]
3 changes: 2 additions & 1 deletion src/capi.zig
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,14 @@ pub inline fn CORE_TYPE(type_id: TypeId) HostType {
}},
};
}
pub inline fn CORE_TYPE_EXT(type_id: TypeId, get_children: GetChildrenFn, finalizer: FinalizerFn) HostType {
pub inline fn CORE_TYPE_EXT(type_id: TypeId, get_children: GetChildrenFn, finalizer: FinalizerFn, load_all_methods: bool) HostType {
return HostType{
.type = c.CL_BIND_TYPE_CORE_CUSTOM,
.data = .{ .core_custom = .{
.type_id = type_id,
.get_children = get_children,
.finalizer = finalizer,
.load_all_methods = load_all_methods,
}},
};
}
Expand Down
4 changes: 2 additions & 2 deletions src/cgen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ fn genHead(c: *Compiler, w: std.ArrayListUnmanaged(u8).Writer, chunks: []Chunk)
const params = base.sema.getFuncSig(func.funcSigId).params();
if (params.len > 0) {
for (params) |param| {
try w.print(", {}", .{ try chunk.cTypeName(param) });
try w.print(", {}", .{ try chunk.cTypeName(param.type) });
}
}
try w.writeAll(");\n");
Expand All @@ -719,7 +719,7 @@ fn genHead(c: *Compiler, w: std.ArrayListUnmanaged(u8).Writer, chunks: []Chunk)
const params = funcSig.params();
if (params.len > 0) {
for (params) |param| {
try w.print(", {}", .{ try chunk.cTypeName(param) });
try w.print(", {}", .{ try chunk.cTypeName(param.type) });
}
}
try w.writeAll(");\n");
Expand Down
21 changes: 10 additions & 11 deletions src/chunk.zig
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,8 @@ pub const Chunk = struct {
/// Includes lambdas which are not linked from a named sym.
funcs: std.ArrayListUnmanaged(*cy.Func),

/// This is used for lookups when resolving const expressions.
cur_template_params: std.StringHashMapUnmanaged(cy.Value),
cur_template_ctx: ?sema.TemplateContext,
/// Reference the current resolve context.
resolve_stack: std.ArrayListUnmanaged(sema.ResolveContext),

///
/// Codegen pass
Expand Down Expand Up @@ -277,8 +276,7 @@ pub const Chunk = struct {
},
.syms = .{},
.funcs = .{},
.cur_template_params = .{},
.cur_template_ctx = null,
.resolve_stack = .{},
.in_ct_expr = false,
.host_types = .{},
.host_funcs = .{},
Expand Down Expand Up @@ -328,7 +326,6 @@ pub const Chunk = struct {
self.symInitDeps.deinit(self.alloc);
self.symInitInfos.deinit(self.alloc);
self.curInitingSymDeps.deinit(self.alloc);
self.cur_template_params.deinit(self.alloc);

self.typeStack.deinit(self.alloc);
self.valueStack.deinit(self.alloc);
Expand All @@ -339,6 +336,7 @@ pub const Chunk = struct {
self.listDataStack.deinit(self.alloc);
self.genIrLocalMapStack.deinit(self.alloc);
self.genLocalStack.deinit(self.alloc);
self.resolve_stack.deinit(self.alloc);

if (cy.hasJIT) {
self.tempTypeRefs.deinit(self.alloc);
Expand All @@ -354,17 +352,18 @@ pub const Chunk = struct {
self.sym_cache.deinit(self.alloc);
self.use_alls.deinit(self.alloc);

for (self.funcs.items) |func| {
func.deinit(self.alloc);
self.alloc.destroy(func);
}
self.funcs.deinit(self.alloc);

// Deinit chunk syms.
for (self.syms.items) |sym| {
sym.destroy(self.vm, self.alloc);
}
self.syms.deinit(self.alloc);

for (self.funcs.items) |func| {
self.alloc.destroy(func);
}
self.funcs.deinit(self.alloc);

for (self.variantFuncSyms.items) |func| {
self.alloc.destroy(func);
}
Expand Down
Loading

0 comments on commit b87a37c

Please sign in to comment.