2
3
Fork
You've already forked comath
3

Update for Zig 0.16 #1

Merged
InKryption merged 9 commits from agagniere/comath:zig-master into main 2026年06月10日 23:16:55 +02:00
Contributor
Copy link

I must admit not understanding 100% of what was touched, feedback welcome.

In evalFuncCall: I wasn't able to find a way to keep the previous behavior: there in now a maximum number of arguments that can be passed to a function.

I must admit not understanding 100% of what was touched, feedback welcome. In `evalFuncCall`: I wasn't able to find a way to keep the previous behavior: there in now a maximum number of arguments that can be passed to a function.
`@Struct` creates plain structs; to get a tuple that supports indexing
and can be passed to `@call`, the argument-list type must use `@Tuple`.
Affects: Order, Relation.Associativity (comath.zig), NestType,
ExprNode's tagged union (parse.zig).
First-time contributor
Copy link

Hitting a bug with this in my project in 0.16.0 - I'll see if I can make a minimal repro and figure out what is going on...

zig-pkg/comath-0.1.0-wKZ2n1jdAQAG3Eu-8EXkvyEEKEnFzkcA3KIFIIYrOV-I/src/compiled.zig:334:94: error: unable to resolve comptime value
 break :args try comptime evalExprTupleImpl(fc.args, ctx, inputs);
 ^~~~~~
zig-pkg/comath-0.1.0-wKZ2n1jdAQAG3Eu-8EXkvyEEKEnFzkcA3KIFIIYrOV-I/src/compiled.zig:334:53: note: 'comptime' keyword forces comptime evaluation
 break :args try comptime evalExprTupleImpl(fc.args, ctx, inputs);
 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
zig-pkg/comath-0.1.0-wKZ2n1jdAQAG3Eu-8EXkvyEEKEnFzkcA3KIFIIYrOV-I/src/compiled.zig:352:58: note: called inline here
 const rhs: Rhs = try evalImpl(bin.rhs, ctx, inputs);
 ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~
zig-pkg/comath-0.1.0-wKZ2n1jdAQAG3Eu-8EXkvyEEKEnFzkcA3KIFIIYrOV-I/src/compiled.zig:349:58: note: called inline here (2 times)
 const lhs: Lhs = try evalImpl(bin.lhs, ctx, inputs);
 ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~
zig-pkg/comath-0.1.0-wKZ2n1jdAQAG3Eu-8EXkvyEEKEnFzkcA3KIFIIYrOV-I/src/compiled.zig:190:36: note: called inline here
 return evalImpl(root, ctx, inputs);
 ~~~~~~~~^~~~~~~~~~~~~~~~~~~
zig-pkg/comath-0.1.0-wKZ2n1jdAQAG3Eu-8EXkvyEEKEnFzkcA3KIFIIYrOV-I/src/compiled.zig:137:65: note: called inline here
 return Parameterized(@TypeOf(inputs)).evalUnusedMode(.no_unused, self.ctx, inputs);
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
zig-pkg/comath-0.1.0-wKZ2n1jdAQAG3Eu-8EXkvyEEKEnFzkcA3KIFIIYrOV-I/src/eval.zig:17:30: note: called inline here
 return compiled_expr.eval(inputs);
 ~~~~~~~~~~~~~~~~~~^~~~~~~~
Hitting a bug with this in my project in 0.16.0 - I'll see if I can make a minimal repro and figure out what is going on... ``` zig-pkg/comath-0.1.0-wKZ2n1jdAQAG3Eu-8EXkvyEEKEnFzkcA3KIFIIYrOV-I/src/compiled.zig:334:94: error: unable to resolve comptime value break :args try comptime evalExprTupleImpl(fc.args, ctx, inputs); ^~~~~~ zig-pkg/comath-0.1.0-wKZ2n1jdAQAG3Eu-8EXkvyEEKEnFzkcA3KIFIIYrOV-I/src/compiled.zig:334:53: note: 'comptime' keyword forces comptime evaluation break :args try comptime evalExprTupleImpl(fc.args, ctx, inputs); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ zig-pkg/comath-0.1.0-wKZ2n1jdAQAG3Eu-8EXkvyEEKEnFzkcA3KIFIIYrOV-I/src/compiled.zig:352:58: note: called inline here const rhs: Rhs = try evalImpl(bin.rhs, ctx, inputs); ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~ zig-pkg/comath-0.1.0-wKZ2n1jdAQAG3Eu-8EXkvyEEKEnFzkcA3KIFIIYrOV-I/src/compiled.zig:349:58: note: called inline here (2 times) const lhs: Lhs = try evalImpl(bin.lhs, ctx, inputs); ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~ zig-pkg/comath-0.1.0-wKZ2n1jdAQAG3Eu-8EXkvyEEKEnFzkcA3KIFIIYrOV-I/src/compiled.zig:190:36: note: called inline here return evalImpl(root, ctx, inputs); ~~~~~~~~^~~~~~~~~~~~~~~~~~~ zig-pkg/comath-0.1.0-wKZ2n1jdAQAG3Eu-8EXkvyEEKEnFzkcA3KIFIIYrOV-I/src/compiled.zig:137:65: note: called inline here return Parameterized(@TypeOf(inputs)).evalUnusedMode(.no_unused, self.ctx, inputs); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ zig-pkg/comath-0.1.0-wKZ2n1jdAQAG3Eu-8EXkvyEEKEnFzkcA3KIFIIYrOV-I/src/eval.zig:17:30: note: called inline here return compiled_expr.eval(inputs); ~~~~~~~~~~~~~~~~~~^~~~~~~~ ```
First-time contributor
Copy link

Test case:

conststd=@import("std");constcomath=@import("comath");constFakeVector=struct{x:f32,y:f32,z:f32,pubfninit(x_:f32,y_:f32,z_:f32,)FakeVector{return.{.x=x_,.y=y_,.z=z_};}pubfnadd(self:@This(),rhs:f32,)FakeVector{return.{.x=self.x+rhs,.y=self.y+rhs,.z=self.z+rhs,};}pubfnformat(self:@This(),writer:*std.Io.Writer,)std.Io.Writer.Error!void{trywriter.print("({d}, {d}, {d})",self);}};/// example context that should allow calling the init function like a/// constructor and the add functionconstFAKE_CTX=(comath.ctx.fn_method.context(comath.ctx.simple.context(comath.ctx.namespace.context(.{.v3=FakeVector.init,},)),.{.@"+"="add",},));test"comath with runtime arguments"{// passes{// note the const hereconstx:i32=12.0;constresult=trycomath.eval("v3(0,0,x) + x",FAKE_CTX,.{.x=x},);trystd.testing.expectEqualDeep(FakeVector.init(15,15,30,),result,);}// broken{// note that this is not comptime constantvarx:i32=12.0;x+=3;constresult=trycomath.eval("v3(0,0,x) + x",FAKE_CTX,.{.x=x},);trystd.testing.expectEqualDeep(FakeVector.init(15,15,30,),result,);}}
Test case: ```zig const std = @import("std"); const comath = @import("comath"); const FakeVector = struct { x: f32, y: f32, z: f32, pub fn init( x_: f32, y_: f32, z_: f32, ) FakeVector { return .{ .x = x_, .y = y_, .z = z_ }; } pub fn add( self: @This(), rhs: f32, ) FakeVector { return .{ .x = self.x + rhs, .y = self.y + rhs, .z = self.z + rhs, }; } pub fn format( self: @This(), writer: *std.Io.Writer, ) std.Io.Writer.Error!void { try writer.print("({d}, {d}, {d})", self); } }; /// example context that should allow calling the init function like a /// constructor and the add function const FAKE_CTX = ( comath.ctx.fn_method.context( comath.ctx.simple.context( comath.ctx.namespace.context( .{ .v3 = FakeVector.init, }, ) ), .{ .@"+" = "add", }, ) ); test "comath with runtime arguments" { // passes { // note the const here const x: i32 = 12.0; const result = try comath.eval( "v3(0,0,x) + x", FAKE_CTX, .{ .x = x }, ); try std.testing.expectEqualDeep( FakeVector.init( 15, 15, 30, ), result, ); } // broken { // note that this is not comptime constant var x: i32 = 12.0; x += 3; const result = try comath.eval( "v3(0,0,x) + x", FAKE_CTX, .{ .x = x }, ); try std.testing.expectEqualDeep( FakeVector.init( 15, 15, 30, ), result, ); } } ```
Author
Contributor
Copy link

@stephan_gfx Good catch !
I added you test case as a test.

similarly to evalFuncCall, I wasn't able to figure out an elegant solution, so my solution limits the number of elements in a tuple.

Better ideas welcome !

@stephan_gfx Good catch ! I added you test case as a test. similarly to `evalFuncCall`, I wasn't able to figure out an elegant solution, so my solution limits the number of elements in a tuple. Better ideas welcome !
First-time contributor
Copy link

Don't know if this is any better:

stephan_gfx/comath@209838ee34

Don't know if this is any better: https://codeberg.org/stephan_gfx/comath/commit/209838ee34d43ed2ca5c1e0405dd726594368abe
Author
Contributor
Copy link

It is indeed nicer to read !

However it is my understanding that it prevents comptime evaluation of functions (as illustrated by the failing test, where the comptime_int arguments are coerced into i32, which, if I understand correctly, prevents the function from being evaluated at comptime)

I'd like to be proven wrong though because I'm not satisfied with my current solution

It is indeed nicer to read ! However it is my understanding that it prevents comptime evaluation of functions (as illustrated by the failing test, where the `comptime_int` arguments are coerced into `i32`, which, if I understand correctly, prevents the function from being evaluated at comptime) I'd like to be proven wrong though because I'm not satisfied with my current solution
Author
Contributor
Copy link

Code snippet to better illustrate the difference I'm referring to:

conststd=@import("std");inlinefnsub(a:i32,b:i32)i32{returna-b;}pubfnmain()void{// OLD-style: comptime-only tuple, has to be comptimeconstargs_old:@Tuple(&.{comptime_int,comptime_int})=.{19,2};constr_old=@call(.auto,sub,args_old);std.debug.print("OLD comptime_int tuple result: {} (type {s})\n",.{r_old,@typeName(@TypeOf(r_old))});// NEW-style: coerced to param typesconstTupleNew=@Tuple(&.{i32,i32});varargs_new:TupleNew=undefined;args_new[0]=19;args_new[1]=2;constr_new=@call(.auto,sub,args_new);std.debug.print("NEW i32 tuple result: {} (type {s})\n",.{r_new,@typeName(@TypeOf(r_new))});// Check whether the result is comptime-known in NEW pathconstis_comptime_old=@typeInfo(@TypeOf(.{r_old})).@"struct".fields[0].is_comptime;constis_comptime_new=@typeInfo(@TypeOf(.{r_new})).@"struct".fields[0].is_comptime;std.debug.print("OLD result is_comptime in literal: {}\n",.{is_comptime_old});std.debug.print("NEW result is_comptime in literal: {}\n",.{is_comptime_new});}
$ zig run snippet.zig
OLD comptime_int tuple result: 17 (type i32)
NEW i32 tuple result: 17 (type i32)
OLD result is_comptime in literal: true
NEW result is_comptime in literal: false
Code snippet to better illustrate the difference I'm referring to: ```zig const std = @import("std"); inline fn sub(a: i32, b: i32) i32 { return a - b; } pub fn main() void { // OLD-style: comptime-only tuple, has to be comptime const args_old: @Tuple(&.{ comptime_int, comptime_int }) = .{ 19, 2 }; const r_old = @call(.auto, sub, args_old); std.debug.print("OLD comptime_int tuple result: {} (type {s})\n", .{ r_old, @typeName(@TypeOf(r_old)) }); // NEW-style: coerced to param types const TupleNew = @Tuple(&.{ i32, i32 }); var args_new: TupleNew = undefined; args_new[0] = 19; args_new[1] = 2; const r_new = @call(.auto, sub, args_new); std.debug.print("NEW i32 tuple result: {} (type {s})\n", .{ r_new, @typeName(@TypeOf(r_new)) }); // Check whether the result is comptime-known in NEW path const is_comptime_old = @typeInfo(@TypeOf(.{r_old})).@"struct".fields[0].is_comptime; const is_comptime_new = @typeInfo(@TypeOf(.{r_new})).@"struct".fields[0].is_comptime; std.debug.print("OLD result is_comptime in literal: {}\n", .{is_comptime_old}); std.debug.print("NEW result is_comptime in literal: {}\n", .{is_comptime_new}); } ``` ```console $ zig run snippet.zig OLD comptime_int tuple result: 17 (type i32) NEW i32 tuple result: 17 (type i32) OLD result is_comptime in literal: true NEW result is_comptime in literal: false ```

I apologize for the long wait on this. Kept punting this down the list of things I have to do with life getting complicated. I appreciate the contribution.

I apologize for the long wait on this. Kept punting this down the list of things I have to do with life getting complicated. I appreciate the contribution.
Sign in to join this conversation.
No reviewers
Labels
Clear labels
No items
No labels
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
InKryption/comath!1
Reference in a new issue
InKryption/comath
No description provided.
Delete branch "agagniere/comath:zig-master"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?