2025-12-05
With Christmas on the horizon, C3 gets another monthly update to 0.7 with 0.7.8. As usual it brings a set of small tweaks and fixes. Let’s see what we got:
0.7.7 added struct initializer splatting, but it was a special case. And while it has been possible to splat an array or slice into both initializers and calls, structs didn’t support that. This has been amended in 0.7.8:
struct Foo{ int a; double b;}
fn void test(int x, double y, int a){ io::printfn("%s %s", x * a, y * a);}
fn int main(){ Foo f = { 42, 3.14 }; test(...f, 2); // prints 84 6.280000 return 0;}While vectors can use names to reference the first four values, e.g. foo.x as well as supporting swizzling: foo.xy, designated initialization has been limited to using the same syntax as arrays: { [0..1] = 1.2, [2] = 3.2 }.
With this improvement, it’s now possible to use the name of the components directly:
float[<3>] x = { .xy = 1.2, .z = 3.3 };// Same as float[<3>] x = { [0..1] = 1.2, [2] = 3.2 };A limitation is that any swizzling syntax, like .xy must indicate a consecutive gapless range, so initializing using for example .zx or .xz would not be allowed.
@return? for simplified fault declarationsBefore we had this:
faultdef BAD_ZERO, BAD_ONE, TOO_BIG;<* @return? BAD_ZERO, BAD_ONE*>fn int? foo(int a){ switch (a) { case 0: return BAD_ZERO?; case 1: return BAD_ONE?; default: return a * 2; }}
// We must repeat the errors of "foo"<* @return? BAD_ZERO, BAD_ONE, TOO_BIG*>fn int? bar(int a){ if (a > 100) return TOO_BIG?; return foo(a) ^ 12;}With this improvement, we can refer to the errors of a function (or function pointer) in the @return? statements:
<* @return? foo!, TOO_BIG*>fn int? bar(int a){ if (a > 100) return TOO_BIG?; return foo(a) ^ 12;}membersof to return associated values.Enums used to only support the type property .associated which returned a list of the associated value types. Enums now instead use .membersof, which works like the same property on structs. .associated has been deprecated as .membersof includes its information.
enum Foo : (String x, int val){ ABC = { "Hello", 3 }, DEF = { "World", -100 },}
fn int main(){ io::printn(Foo.membersof[0].get(Foo.ABC)); // Print "Hello" io::printn(Foo.membersof[1].get(Foo.DEF)); // Print -100 $assert Foo.membersof[0].type == String.typeid; io::printn(Foo.membersof[0].nameof); // prints x Foo f = ABC; io::printn(Foo.membersof[1].get(f)); // prints 3 return 0;}C vaargs on functions were previously not possible to reference using @param. This has been improved, allowing ”…” to be referenced:
<* @param fmt : "the format string" @param ... : "the arguments to print"*>extern fn int printf(ZString fmt, ...);@if evaluates to falseThis change means that if you add imports to a module that isn’t enabled, they are not checked:
module foo @if(false);import non_existing_lib; // Missing module
fn int test(){ return 0;}Prior to 0.7.8 this would be reported as an error due to non_existing_lib not being a valid module, but from 0.7.8 such errors are only reported if the importing module is enabled. In this example, changing @if(false) to @if(true) would make the import reported as an error.
A --linux-libc command line option has been added, supporting gnu and musl options. This is the beginning of official musl support for the C3C compiler, contributed by DylanDoesProgramming.
int $foo... argumentsNamed macro vaargs were incorrectly handled prior to 0.7.7, but the change in 0.7.7 inadvertently prevented typed const vaargs like int $foo.... This is now enabled again.
"foo" "bar" is now much more efficient, handling very long strings easily.$schema was added as a key in project.json.@simd implementation was changed, and @simd is now possible to use directly after the type as needed.--test-nocapture is deprecated in favour of --test-show-output.-DXTENSA_ENABLE to enable it instead.0.7.8 contains around 30 fixes, with the increase compared to 0.7.7 mostly depending on the vector ABI changes which yielded some regressions to clean up in 0.7.8.
The MacOS bindings in std::os::macos nicely got a bunch of additions contributed by Glenn Kirk, and printing typeids now prints the actual underlying id as well. Printing BigInts was optimized and printf now has caching which makes printing on Win32 faster.
Several things are in the pipe: possibly updating the syntax for turning a fault into an optional, going from return io::EOF?; to some syntax that makes the grammar simpler. Inline asm is still waiting for its revision, and there should be a review of the casting rules. Finally, generating proper headers when building static and dynamic is rather overdue.
This release wouldn’t have been possible without the C3 community. I’d like to extend a deep thank you to all who have contributed, both through filed issues, PRs and just plain discussions.
@if evaluates to false #2251."$schema" as key in project.json #2554.@return? for simplified fault declarations. Check @return? eagerly #2340.membersof to return the associated values. #2571SomeEnum.associated in favour of SomeEnum.membersof@simd implementation.Foo{} when Foo is not a generic type #2574.@param directives for ... parameters. #2578--test-nocapture in favour of --test-show-output #2588.-DXTENSA_ENABLE to enable it insteadfloat[<3>] x = { .xy = 1.2, .z = 3.3 } swizzle initialization for vectors. #2599int $foo... arguments. #2601--linux-libc=musl.Foo.is_eq would return false if the type was a typedef and had an overload, but the underlying type was not comparable.a /= 0 and b /= 0f #2558.a %= 0f.Path handling c:\foo and \home parent. #2569c:\ or \ #2569.ZString* it would not properly emit a compilation error, but hit an assert #2573.any would accidentally be permitted. #2575overflow_* vector ops now correctly return a bool vector.defer catch with a (void), would cause an assertion. #2580@wasm would ignore the renaming.*(int*)1 incorrectly yielded an assert in LLVM IR lowering #2584.String.to_integer does not correctly return in some cases where it should #2590.$defined(hashmap.init(mem)) causes compiler segfault #2611.CGFloat CGPoint CGSize CGRect types to core_foundation (macOS).NSStatusItem const enum to ns module (macOS).NSWindowCollectionBehavior NSWindowLevel NSWindowTabbingMode to ns module (macOS).ns::eventmask_from_type function to objc (macOS).event_type_from function in favour of using NSEvent directly, to better align with the objc api (macOS).NSApplicationTerminateReply to ns module (macOS).registerClassPair function to objc module (macOS).Check out the documentation or download it and try it out.
Have questions? Come and chat with us on Discord.
Discuss this article on Reddit and Hacker News.