Skip to content

The C3 Blog

An early 0.6.8 release

Originally from: https://c3.handmade.network/blog/p/8996-an_early_0.6.8_release

Release 0.6.8

0.6.8 is to be the last release before 0.7.0 and had a short cycle of development - only two weeks. As usual it has fixes, but more importantly, it prepares for 0.7.0.

So let's dive into what's new in this release!

"New generics" - List{int}

0.6.8 has the flag --enable-new-generics, which implements the new syntax for generics, from, for example, List(<int>) a; to List{int} a;. With this flag the compound literal syntax of Foo { 1, 2 } is disallowed, instead it uses C-style (Foo) { 1, 2 }.

Expression blocks and @operator(construct) deprecated

The expression blocks {| |} helps you turn any sequence of statements into an expression. This would be immensely useful in C-like macros, but with semantic macros in C3 it's much less so, to the point that it's being removed in 0.7.0. The feature isn't bad, it just hasn't been useful enough to keep it.

The @operator(construct) which allowed static method-like invocations on types is deprecated. It was introduced in 0.6.6 as an experiment, but will ultimately be removed in the next version.

Changes to the stdlib

After a lot of testing during the last year, we're retiring the new_* and temp_ functions. The old pattern of foo.new_init becomes foo.init(mem) with "mem" being the default heap allocator, and initializers with temp allocators usin foo.tinit().

Tooling changes

The compiler now places temporaries in .build by default when using compile. There is a --suppress-run for benchmark and test targets to not run them immediately. There is also a --build-env option to get some information about the build environment.

What's next:

The release of 0.7.0 is planned for early April, with the main feature being the new generics and and standard library "standardization" aroud "init(mem)". The other big thing is of course the removal of deprecated functionality.

There are some long standing issues, like completing the asm functionality and resolving some of the semantics around #hash arguments that hopefully will be fixed in 0.7.0 as well.

Even though focus is on 0.7.0, a 0.6.9 version might appear if there is an urgent need for bug fixes in 0.6.x.

Here is the full change list:

0.6.8 Change list

Changes / improvements

  • Increase precedence of (Foo) { 1, 2 }
  • Add --enable-new-generics to enable Foo{int} generic syntax.
  • {| |} expression blocks deprecated.
  • c3c --test-leak-report flag for displaying full memory lead report if any
  • Output into /.build/obj/ by default.
  • Output llvm/asm into llvm/ and asm/ by default.
  • Add flag --suppress-run. For commands which may run executable after building, skip the run step. #1931
  • Add --build-env for build environment information.
  • Deprecation of @operator(construct).

Fixes

  • Bug appearing when ?? was combined with boolean in some cases.
  • Test runner --test-disable-sort didn't work, c3c was expecting --test-nosort
  • Test runner with tracking allocator assertion at failed test #1963
  • Test runner with tracking allocator didn't properly handle teardown_fn
  • Correctly give an error if a character literal contains a line break.
  • Implicitly unwrapped optional value in defer incorrectly copied #1982.
  • Crash when trying to define a method macro that isn't @construct but has no arguments.
  • Regression, .gitkeep files were generated incorrectly.
  • Aliases are now correctly handled as if they were variables/functions in regards to namespacing and accept @builtin.
  • Correctly handle in/out when interacting with inout.
  • Don't delete .o files not produced by the compiler.
  • Fix optional jumps in expression lists, #1942.
  • Several fixes for .o files and -o output, improving handling and naming.
  • Fix bug casting bool to int to other int #1995.
  • @if declarations were missing from -P output #1973.
  • Check exe and lib output so -o works with directories.
  • Swizzling an inline vector in a struct would cause a crash.
  • Fixed error and poor error message when using an invalid target name.

If you want to read more about C3, check out the documentation: https://c3-lang.org or download it and try it out: https://github.com/c3lang/c3c

Comments


Comment by Christoffer Lernö

Release 0.6.8

0.6.8 is to be the last release before 0.7.0 and had a short cycle of development - only two weeks. As usual it has fixes, but more importantly, it prepares for 0.7.0.

So let's dive into what's new in this release!

"New generics" - List{int}

0.6.8 has the flag --enable-new-generics, which implements the new syntax for generics, from, for example, List(<int>) a; to List{int} a;. With this flag the compound literal syntax of Foo { 1, 2 } is disallowed, instead it uses C-style (Foo) { 1, 2 }.

Expression blocks and @operator(construct) deprecated

The expression blocks {| |} helps you turn any sequence of statements into an expression. This would be immensely useful in C-like macros, but with semantic macros in C3 it's much less so, to the point that it's being removed in 0.7.0. The feature isn't bad, it just hasn't been useful enough to keep it.

The @operator(construct) which allowed static method-like invocations on types is deprecated. It was introduced in 0.6.6 as an experiment, but will ultimately be removed in the next version.

Changes to the stdlib

After a lot of testing during the last year, we're retiring the new_* and temp_ functions. The old pattern of foo.new_init becomes foo.init(mem) with "mem" being the default heap allocator, and initializers with temp allocators usin foo.tinit().

Tooling changes

The compiler now places temporaries in .build by default when using compile. There is a --suppress-run for benchmark and test targets to not run them immediately. There is also a --build-env option to get some information about the build environment.

What's next:

The release of 0.7.0 is planned for early April, with the main feature being the new generics and and standard library "standardization" aroud "init(mem)". The other big thing is of course the removal of deprecated functionality.

There are some long standing issues, like completing the asm functionality and resolving some of the semantics around #hash arguments that hopefully will be fixed in 0.7.0 as well.

Even though focus is on 0.7.0, a 0.6.9 version might appear if there is an urgent need for bug fixes in 0.6.x.

Here is the full change list:

0.6.8 Change list

Changes / improvements

  • Increase precedence of (Foo) { 1, 2 }
  • Add --enable-new-generics to enable Foo{int} generic syntax.
  • {| |} expression blocks deprecated.
  • c3c --test-leak-report flag for displaying full memory lead report if any
  • Output into /.build/obj/ by default.
  • Output llvm/asm into llvm/ and asm/ by default.
  • Add flag --suppress-run. For commands which may run executable after building, skip the run step. #1931
  • Add --build-env for build environment information.
  • Deprecation of @operator(construct).

Fixes

  • Bug appearing when ?? was combined with boolean in some cases.
  • Test runner --test-disable-sort didn't work, c3c was expecting --test-nosort
  • Test runner with tracking allocator assertion at failed test #1963
  • Test runner with tracking allocator didn't properly handle teardown_fn
  • Correctly give an error if a character literal contains a line break.
  • Implicitly unwrapped optional value in defer incorrectly copied #1982.
  • Crash when trying to define a method macro that isn't @construct but has no arguments.
  • Regression, .gitkeep files were generated incorrectly.
  • Aliases are now correctly handled as if they were variables/functions in regards to namespacing and accept @builtin.
  • Correctly handle in/out when interacting with inout.
  • Don't delete .o files not produced by the compiler.
  • Fix optional jumps in expression lists, #1942.
  • Several fixes for .o files and -o output, improving handling and naming.
  • Fix bug casting bool to int to other int #1995.
  • @if declarations were missing from -P output #1973.
  • Check exe and lib output so -o works with directories.
  • Swizzling an inline vector in a struct would cause a crash.
  • Fixed error and poor error message when using an invalid target name.

If you want to read more about C3, check out the documentation: https://c3-lang.org or download it and try it out: https://github.com/c3lang/c3c

It's February and time for a new C3 release: 0.6.7

Originally from: https://c3.handmade.network/blog/p/8994-it%2527s_february_and_time_for_a_new_c3_release__0.6.7

0.6.7 has been interesting. There has been some significant additions to the language that opens for new solutions. Also because it's now finally possible to manipulate compile time arrays in a simple way, there are some simple but useful compile time tricks that now is available. For example, string manipulation at compile time.

So let's have look at the biggest changes.

Compile time improvements

As previously mentioned, compile time arrays can now be mutated. This allows things like $arr[$i] = 123. And at this point, the only thing still not possible to mutate at compile time are struct fields.

In addition to this the concatenation operator, +++, now works on all types of arrays, even ones defined with gaps.

"Inline" enums

It's now possible to set enums to have its ordinal or an associated value marked "inline".

The feature allows an enum value to implicitly convert to that value:

enum Foo : int (inline String name, int y, int z)
{
  ABC = { "Hello", 1, 2 },
  DEF = { "World", 2, 3 },
}

fn void main()
{
  String hello = Foo.ABC;
  io::printn(hello); // Prints "Hello"
}

Short function syntax combined with macros

The short function syntax handles macros with trailing bodies in a special way, allowing the macro's trailing body to work as the body of the function, which simplifies the code when a function starts with a macro with a body:

// 0.6.6
fn Path! new_cwd(Allocator allocator = allocator::heap())
{
  @pool(allocator)
  {
    return new(os::getcwd(allocator::temp()), allocator);
  };
}

// 0.6.7
fn Path! new_cwd(Allocator allocator = allocator::heap()) => @pool()
{
  return new(os::getcwd(allocator::temp()), allocator);
}

C style compound literals

C style compound literals is making a comeback, it's now possible to do (int[2]) { 1, 2 } as an alternative to int[2] { 1, 2 }.

This is in preparation for possibly changing the syntax for generic modules. Regardless, this style is probably here to stay.

Improvements to runtime and unit test error checking

Unaligned loads will now be detected in safe mode, and the test runner has been improved to check for memory leaks, and the test runner has been greatly updated.

A new module test in the standard library adds various test utilities for unit testing.

Type inference on ??

Previously the expression MyEnum f = foo() ?? MyEnum.ABC; would require the full enum name as in this example but this has been improved so that in 0.6.7 MyEnum f = foo() ? ABC; is sufficient. This also affects all other types of inference on the right hand side of ??

Better bytes printing in the stdlib

0.6.7 adds %h and %H for printing binary data in hexadecimal.

Other stdlib improvements

  1. Channels for use with threaded code were added.
  2. @select to perform constant a ? x : y at compile time yielding a constant result.
  3. HashMap is now implementing Printable
  4. allocator::wrap allows quickly creating an arena allocator from a collection of bytes.
  5. Nolibc math code was expanded with some additional functions.

Bug fixes

0.6.7, like 0.6.6, contains about 50 bug fixes of various severity.

What's next?

There is an ongoing discussion in regards to generic syntax. (< >) works, but it not particularly lightweight. Some other alternatives, such as < > ( ) and [ ] suffer from ambiguities, so other options are investigated, such as $() and {}

Also in a quest to simplify the language, it's an open question whether {| |} should be removed or not. The expression blocks have their uses, but significantly less in C3 with semantic macros than it would have in C.

Other than that, expansion of the standard library is a priority.

With 0.7.0 scheduled for April, there is pretty much only room for one more 0.6.x release: 0.6.8.

I'm looking forward to cleaning out deprecated code when 0.7.0 finally comes around.

Here is the full change list:

Changes / improvements

  • Contracts @require/@ensure are no longer treated as conditionals, but must be explicitly bool.
  • Add win-debug setting to be able to pick dwarf for output #1855.
  • Error on switch case fallthough if there is more than one newline #1849.
  • Added flags to c3c project view to filter displayed properties
  • Compile time array assignment #1806.
  • Allow +++ to work on all types of arrays.
  • Allow (int[*]) { 1, 2 } cast style initialization.
  • Experimental change from [*] to [?]
  • Warn on if-catch with just a default case.
  • Compile time array inc/dec.
  • Improve error message when using ',' in struct declarations. #1920
  • Compile time array assign ops, e.g. $c[1] += 3 #1890.
  • Add inline to enums #1819.
  • Cleaner error message when missing comma in struct initializer #1941.
  • Distinct inline void causes unexpected error if used in slice #1946.
  • Allow fn int test() => @pool() { return 1; } short function syntax usage #1906.
  • Test runner will also check for leaks.
  • Improve inference on ?? #1943.
  • Detect unaligned loads #1951.

Fixes

  • Fix issue requiring prefix on a generic interface declaration.
  • Fix bug in SHA1 for longer blocks #1854.
  • Fix lack of location for reporting lambdas with missing return statement #1857.
  • Compiler allows a generic module to be declared with different parameters #1856.
  • Fix issue with @const where the statement $foo = 1; was not considered constant.
  • Const strings and bytes were not properly converted to compile time bools.
  • Concatenating a const empty slice with another array caused a null pointer access.
  • Fix linux-crt and linux-crtbegin not getting recognized as a project paramater
  • Fix dues to crash when converting a const vector to another vector #1864.
  • Filter $exec output from \r, which otherwise would cause a compiler assert #1867.
  • Fixes to `"exec" use, including issue when compiling with MinGW.
  • Correctly check jump table size and be generous when compiling it #1877.
  • Fix bug where .min/.max would fail on a distinct int #1888.
  • Fix issue where compile time declarations in expression list would not be handled properly.
  • Issue where trailing body argument was allowed without type even though the definition specified it #1879.
  • Fix issues with @jump on empty default or only default #1893 #1894
  • Fixes miscompilation of nested @jump #1896.
  • Fixed STB_WEAK errors when using consts in macros in the stdlib #1871.
  • Missing error when placing a single statement for-body on a new row #1892.
  • Fix bug where in dead code, only the first statement would be turned into a nop.
  • Remove unused $inline argument to mem::copy.
  • Defer is broken when placed before a $foreach #1912.
  • Usage of @noreturn macro is type-checked as if it returns #1913.
  • Bug when indexing into a constant array at compile time.
  • Fixing various issues around shifts, like z <<= { 1, 2 }.
  • return (any)&foo would not be reported as an escaping variable if foo was a pointer or slice.
  • Incorrect error message when providing too many associated values for enum #1934.
  • Allow function types to have a calling convention. #1938
  • Issue with defer copying when triggered by break or continue #1936.
  • Assert when using optional as init or inc part in a for loop #1942.
  • Fix bigint hex parsing #1945.
  • bigint::from_int(0) throws assertion #1944.
  • write of qoi would leak memory.
  • Issue when having an empty Path or just "."
  • set_env would leak memory.
  • Fix issue where aligned bitstructs did not store/load with the given alignment.
  • Fix issue in GrowableBitSet with sanitizers.
  • Fix issue in List with sanitizers.
  • Circumvent Aarch64 miscompilations of atomics.
  • Fixes to ByteBuffer allocation/free.
  • Fix issue where compiling both for asm and object file would corrupt the obj file output.
  • Fix poll and POLL_FOREVER.
  • Missing end padding when including a packed struct #1966.
  • Issue when scalar expanding a boolean from a conditional to a bool vector #1954.
  • Fix issue when parsing bitstructs, preventing them from implementing interfaces.
  • Regression String! a; char* b = a.ptr; would incorrectly be allowed.
  • Fix issue where target was ignored for projects.
  • Fix issue when dereferencing a constant string.
  • Fix problem where a line break in a literal was allowed.

Stdlib changes

  • Added '%h' and '%H' for printing out binary data in hexadecimal using the formatter.
  • Added weakly linked __powidf2
  • Added channels for threads.
  • New std::core::test module for unit testing machinery.
  • New unit test default runner.
  • Added weakly linked fmodf.
  • Add @select to perform the equivalent of a ? x : y at compile time.
  • HashMap is now Printable.
  • Add allocator::wrap to create an arena allocator on the stack from bytes.

If you want to read more about C3, check out the documentation: https://c3-lang.org or download it and try it out: https://github.com/c3lang/c3c

Comments


Comment by Christoffer Lernö

0.6.7 has been interesting. There has been some significant additions to the language that opens for new solutions. Also because it's now finally possible to manipulate compile time arrays in a simple way, there are some simple but useful compile time tricks that now is available. For example, string manipulation at compile time.

So let's have look at the biggest changes.

Compile time improvements

As previously mentioned, compile time arrays can now be mutated. This allows things like $arr[$i] = 123. And at this point, the only thing still not possible to mutate at compile time are struct fields.

In addition to this the concatenation operator, +++, now works on all types of arrays, even ones defined with gaps.

"Inline" enums

It's now possible to set enums to have its ordinal or an associated value marked "inline".

The feature allows an enum value to implicitly convert to that value:

enum Foo : int (inline String name, int y, int z)
{
  ABC = { "Hello", 1, 2 },
  DEF = { "World", 2, 3 },
}

fn void main()
{
  String hello = Foo.ABC;
  io::printn(hello); // Prints "Hello"
}

Short function syntax combined with macros

The short function syntax handles macros with trailing bodies in a special way, allowing the macro's trailing body to work as the body of the function, which simplifies the code when a function starts with a macro with a body:

// 0.6.6
fn Path! new_cwd(Allocator allocator = allocator::heap())
{
  @pool(allocator)
  {
    return new(os::getcwd(allocator::temp()), allocator);
  };
}

// 0.6.7
fn Path! new_cwd(Allocator allocator = allocator::heap()) => @pool()
{
  return new(os::getcwd(allocator::temp()), allocator);
}

C style compound literals

C style compound literals is making a comeback, it's now possible to do (int[2]) { 1, 2 } as an alternative to int[2] { 1, 2 }.

This is in preparation for possibly changing the syntax for generic modules. Regardless, this style is probably here to stay.

Improvements to runtime and unit test error checking

Unaligned loads will now be detected in safe mode, and the test runner has been improved to check for memory leaks, and the test runner has been greatly updated.

A new module test in the standard library adds various test utilities for unit testing.

Type inference on ??

Previously the expression MyEnum f = foo() ?? MyEnum.ABC; would require the full enum name as in this example but this has been improved so that in 0.6.7 MyEnum f = foo() ? ABC; is sufficient. This also affects all other types of inference on the right hand side of ??

Better bytes printing in the stdlib

0.6.7 adds %h and %H for printing binary data in hexadecimal.

Other stdlib improvements

  1. Channels for use with threaded code were added.
  2. @select to perform constant a ? x : y at compile time yielding a constant result.
  3. HashMap is now implementing Printable
  4. allocator::wrap allows quickly creating an arena allocator from a collection of bytes.
  5. Nolibc math code was expanded with some additional functions.

Bug fixes

0.6.7, like 0.6.6, contains about 50 bug fixes of various severity.

What's next?

There is an ongoing discussion in regards to generic syntax. (< >) works, but it not particularly lightweight. Some other alternatives, such as < > ( ) and [ ] suffer from ambiguities, so other options are investigated, such as $() and {}

Also in a quest to simplify the language, it's an open question whether {| |} should be removed or not. The expression blocks have their uses, but significantly less in C3 with semantic macros than it would have in C.

Other than that, expansion of the standard library is a priority.

With 0.7.0 scheduled for April, there is pretty much only room for one more 0.6.x release: 0.6.8.

I'm looking forward to cleaning out deprecated code when 0.7.0 finally comes around.

Here is the full change list:

Changes / improvements

  • Contracts @require/@ensure are no longer treated as conditionals, but must be explicitly bool.
  • Add win-debug setting to be able to pick dwarf for output #1855.
  • Error on switch case fallthough if there is more than one newline #1849.
  • Added flags to c3c project view to filter displayed properties
  • Compile time array assignment #1806.
  • Allow +++ to work on all types of arrays.
  • Allow (int[*]) { 1, 2 } cast style initialization.
  • Experimental change from [*] to [?]
  • Warn on if-catch with just a default case.
  • Compile time array inc/dec.
  • Improve error message when using ',' in struct declarations. #1920
  • Compile time array assign ops, e.g. $c[1] += 3 #1890.
  • Add inline to enums #1819.
  • Cleaner error message when missing comma in struct initializer #1941.
  • Distinct inline void causes unexpected error if used in slice #1946.
  • Allow fn int test() => @pool() { return 1; } short function syntax usage #1906.
  • Test runner will also check for leaks.
  • Improve inference on ?? #1943.
  • Detect unaligned loads #1951.

Fixes

  • Fix issue requiring prefix on a generic interface declaration.
  • Fix bug in SHA1 for longer blocks #1854.
  • Fix lack of location for reporting lambdas with missing return statement #1857.
  • Compiler allows a generic module to be declared with different parameters #1856.
  • Fix issue with @const where the statement $foo = 1; was not considered constant.
  • Const strings and bytes were not properly converted to compile time bools.
  • Concatenating a const empty slice with another array caused a null pointer access.
  • Fix linux-crt and linux-crtbegin not getting recognized as a project paramater
  • Fix dues to crash when converting a const vector to another vector #1864.
  • Filter $exec output from \r, which otherwise would cause a compiler assert #1867.
  • Fixes to `"exec" use, including issue when compiling with MinGW.
  • Correctly check jump table size and be generous when compiling it #1877.
  • Fix bug where .min/.max would fail on a distinct int #1888.
  • Fix issue where compile time declarations in expression list would not be handled properly.
  • Issue where trailing body argument was allowed without type even though the definition specified it #1879.
  • Fix issues with @jump on empty default or only default #1893 #1894
  • Fixes miscompilation of nested @jump #1896.
  • Fixed STB_WEAK errors when using consts in macros in the stdlib #1871.
  • Missing error when placing a single statement for-body on a new row #1892.
  • Fix bug where in dead code, only the first statement would be turned into a nop.
  • Remove unused $inline argument to mem::copy.
  • Defer is broken when placed before a $foreach #1912.
  • Usage of @noreturn macro is type-checked as if it returns #1913.
  • Bug when indexing into a constant array at compile time.
  • Fixing various issues around shifts, like z <<= { 1, 2 }.
  • return (any)&foo would not be reported as an escaping variable if foo was a pointer or slice.
  • Incorrect error message when providing too many associated values for enum #1934.
  • Allow function types to have a calling convention. #1938
  • Issue with defer copying when triggered by break or continue #1936.
  • Assert when using optional as init or inc part in a for loop #1942.
  • Fix bigint hex parsing #1945.
  • bigint::from_int(0) throws assertion #1944.
  • write of qoi would leak memory.
  • Issue when having an empty Path or just "."
  • set_env would leak memory.
  • Fix issue where aligned bitstructs did not store/load with the given alignment.
  • Fix issue in GrowableBitSet with sanitizers.
  • Fix issue in List with sanitizers.
  • Circumvent Aarch64 miscompilations of atomics.
  • Fixes to ByteBuffer allocation/free.
  • Fix issue where compiling both for asm and object file would corrupt the obj file output.
  • Fix poll and POLL_FOREVER.
  • Missing end padding when including a packed struct #1966.
  • Issue when scalar expanding a boolean from a conditional to a bool vector #1954.
  • Fix issue when parsing bitstructs, preventing them from implementing interfaces.
  • Regression String! a; char* b = a.ptr; would incorrectly be allowed.
  • Fix issue where target was ignored for projects.
  • Fix issue when dereferencing a constant string.
  • Fix problem where a line break in a literal was allowed.

Stdlib changes

  • Added '%h' and '%H' for printing out binary data in hexadecimal using the formatter.
  • Added weakly linked __powidf2
  • Added channels for threads.
  • New std::core::test module for unit testing machinery.
  • New unit test default runner.
  • Added weakly linked fmodf.
  • Add @select to perform the equivalent of a ? x : y at compile time.
  • HashMap is now Printable.
  • Add allocator::wrap to create an arena allocator on the stack from bytes.

If you want to read more about C3, check out the documentation: https://c3-lang.org or download it and try it out: https://github.com/c3lang/c3c

Another monthly release: C3 0.6.6 is here

Originally from: https://c3.handmade.network/blog/p/8983-another_monthly_release__c3_0.6.6_is_here

The 0.6.6 release is surprisingly on time despite (or perhaps thanks to?) the Christmas holidays. It's a new year, and this summer C3 will turn 6. By April, version 0.7.0 will be released, removing deprecated code. The plan is to have one "dot one" release each year until 1.0 is reached (and if everything goes according to plan, the version after 0.9 will be 1.0).

But let's dive into what's new in this release!

Enum from_ordinal / ordinal changes

A longstanding pain point with enums has been modeling C enums with "gaps," as in this example:

typedef enum
{
    ABC = 42,
    GHJ = 101
} MyEnum;

A workaround in C3 using associated values like this doesn't feel ideal:

enum MyEnum : (int val)
{
    ABC = 42,
    GHJ = 101
}

This is because you can get the ordinal of the enum with a cast, e.g., (int)MyEnum.ABC, whereas retrieving the an associated value requires access, like MyEnum.ABC.val.

There are some ideas to bridge this gap, but the fact that the enums would cast to their ordinal by default made it hard to create a reasonable feature for it.

To address this, casts are now deprecated and replaced by MyEnum.from_ordinal(value). This type method is essentially a no-op, as is converting an enum to a number with .ordinal (e.g. MyEnum.ABC.ordinal).

This puts something like MyEnum.ABC.val and MyEnum.ABC.ordinal on, more equal footing which will make it easier to implement possible solutions to the gap enum problem.

No more & macro arguments

Macros differ from functions by allowing 5 (!) additional types of arguments:

  1. Compile-time constant arguments ($foo)
  2. Compile-time type arguments ($Type)
  3. Unevaluated expression arguments (#expr)
  4. And, finally, reference arguments (&ref)

With 0.6.6 &ref arguments become deprecated. With #expr arguments actually already being able to do what &ref does, this is not a loss in functionality. It does put somewhat more effort in using an #expr argument in terms of checking, but with the fairly marginal use of &ref in the standard library it didn't seem like a loss.

The main advantage for dropping &ref is that it no longer needs to be explained or motivated. It also simplifies the compiler in places where it ended up adding surprising complexities – but those simplifications can't be done until 0.7 when &ref is removed (rather than deprecated).

More important than the change is that the removal of & (which has been around for as long as the macros), marks the beginning of consolidating the language: removing things that have proven over time to be superfluous.

void rather than void! as default for main, tests and benchmarks

Another consolidation is the removal of void! as a return type for main.

Previously, it was possible to return an optional Empty value from main and have the compiler turn it into a 1 exit code.

This was problematic because it effectively discarded the Optional’s excuse. It also encouraged bad code which would just rethrow Optionals from functions called by main

void! was removed from @test functions for a similar reason. Actually panicking (with !! rather than !) would yield much better error messages for free. So again the choice between void and void! for test functions were not really helpful.

Starting from version 0.6.6, use void or int as the return type for main functions, and void for @test and @benchmark functions.

$foreach iterating over Strings

An oversight previously made it impossible to iterate over Strings and bytes. With 0.6.6, this now works correctly.

var declaring lambdas

Using var has been limited to macros in C3, but now they're also allowed in functions when declaring lambdas.

@operator(construct)

An experimental feature called construct is available from 0.6.6.

This allows a limited form of static methods on types. For example:

fn Foo Foo.new_with_bar(Bar b) @operator(construct)
{
    return { .b = b }; // Same as Foo { .b = b }
}

The limitation is that such a method must either return the type or a pointer to the type with the method. (So in the above example, either returning Foo or Foo* would be possible).

Consider this an experimental preview for now!

Standard Library Improvements The standard library has seen useful additions, including foreach-compatible iterators for HashMap and an URL parser.

The default hash functions have been improved, enhancing HashMap performance. There have also been other minor additions and changes.

Bug fixes

This version contains over 50 bug fixes, which is about twice from 0.6.5, but less than the record setting 0.6.2.

Refactoring

0.6.6 includes initial steps toward refactoring parts of the frontend representation to facilitate the addition of new backends. A C backend is planned for this year, making these improvements essential.

What's next?

The enums might finally get the "gap enum" usecase filled (pun intended), but it's not clear whether this will happen in 0.6.7 or will be pushed to later.

There is also a glaring hole in compile time evaluation where $i[1] = 2; isn't allowed, but $a = $i[1]; is. This will need to change, but to do so there needs to be some refactoring, which ties into the overall changes in the frontend representation to help the backend lowering, but also static analysis.

Users, despite work under the hood, should see very little of these changes, except maybe that some corner cases (like compile time subscript above) starts working.

There are some syntax tweaks coming up, but they should be very minor and probably not even be visible to most users.

More importantly though, is that C3 will see the beginning of work to prune unused features from the language, which will then eventually be removed with 0.7.0.

0.6.6 Change list

Here is the full change list

Changes / improvements

  • Split help into normal and "full" help, #1703
  • Removed 'headers' command line option.
  • Add enum.from_ordinal and fault.from_ordinal
  • Deprecate cast-style conversion from integer <-> enum.
  • Make deprecation an error in test mode.
  • Add --win-vs-dirs to override VS detection dirs.
  • Add "name" project property to override the name of the resulting binary. #1719
  • Improved add-project to take arguments.
  • Improve error reporting when using type names as the function argument #1750.
  • Improve ordering of method registration to support adding methods to generic modules with method constraints #1746
  • Support experimental @operator(construct) operator overload.
  • Allow using 'var' to declare lambdas in functions.
  • Add 'validation' setting and make dead code a warning.
  • Allow compile time $foreach iteration over constant Strings and bytes.
  • Improved error message when accessing @private from other modules #1769.
  • Include @name when searching for possible matches to name in the error message. #1779
  • Improve @param parse errors #1777
  • Improved #foo resolution inside of the compiler.
  • Deprecated '&' macro arguments.
  • Deprecate `fn void! main() type main functions.
  • Deprecate old void! @benchmark and @test functions.
  • Allow test runners to take String[] arguments.
  • Added --lsp output.
  • Improve the error message when running out of memory.
  • Allowed passing arguments to @test / @benchmark runners via c3c test[benchmark] -- -o --opt1 <arg1>
  • Handle bytes and string literals the same way in terms of zero termination.
  • Function comments are stored and displayed with -P.
  • Prevent #hash arguments from taking code that modifies ct variables. #1794
  • Make stringify to recursively enter #hash expressions #1834.

Fixes

  • Fix case trying to initialize a char[*]* from a String.
  • Fix Map & HashMap put_all_for_create not copying all elements, causing init_from_map to create incomplete copy.
  • Fix bug when a macro calling an extern function was called in another module also declaring and calling the same function. #1690
  • static-lib and dynamic-lib options from the command line now produces headers.
  • Fix bug outputting exported functions without predefined extname.
  • Fix problem where crt1 was linked for dynamic libraries on Linux and BSD. #1710
  • Fix CRT detection on Arch Linux.
  • Fix lexer allowing a trailing underscore (_) with hex and binary literals.
  • Fix --list-operators CLI command printing underscore (_) and hash (#).
  • Fix bug in temp allocator when temp memory is exhausted and allocation needs overaligned mem. #1715
  • Incorrectly handles distinct enums and pointers with '+=' and '-=' #1717.
  • Prevent DString from being initialized with "".
  • Fix bug in OnStackAllocator when freeing overallocated data. #1720
  • Use weak_odr rather than weak on Windows which seems to prevent issues such as #1704.
  • Use weak on dyn-symbols on Linux.
  • Fix crash on project.json not having an empty set of targets.
  • Miscompile when indexing an array with small unsigned types for enums.
  • Change CBool to be 1 byte.
  • any_to_int checks value to be int and no longer works with enum.
  • Add check in formatter printing "%c".
  • Fix bug where !! and ! was not recognized to jump out of the current scope.
  • Fix bug when including compile time parameters in trailing body more than once.
  • Fix issue with compiling a constant struct containing a string array in a local context.
  • Fix error where panic would not properly stop the program when stacktrace couldn't be printed #1751.
  • Macros with default arguments to &, # and type parameters didn't work as expected. #1754.
  • net::poll() with negative timeout behaved incorrectly.
  • Return type inference bugs with macros #1757
  • $defined in a global scope should accept testing normal macros.
  • Assert on add to uninitialized ct variable #1765.
  • Dynamic function lookup fails after changing type without dummy anycast #1761
  • $vasplat was allowed inside of a function when passed as an argument to a function.
  • Prohibit raw vaargs in regular functions with a function body.
  • Assert on certain slice to slice casts. #1768.
  • Fix vector float -> bool conversion.
  • Fix +a = 1 erronously being accepted.
  • Fix not freeing a zero length String
  • Macros with trailing bodys aren't allowed as the single statement after a while loop with no body #1772.
  • Deref subscripts as needed for macro ref method arguments. #1789
  • Change ordering to simplify adding methods to type in conditional modules.
  • #foo style arguments were not type checked when given a type. #1790
  • Bug when using +++ on value build a slice or array: the rhs cast was not done.
  • Fix bug preventing compile time slices from being iterated over with $foreach.
  • Fix bug with defer assignment in macro #1807.
  • Fix regression with swizzle references for vectors #1810.
  • Assert when partially initializing a constant struct containing a slice #1812.
  • Assert concatenating constant slices #1805.
  • Do not link "ld" on Linux with no libc.
  • Fix bug when multiple $else clauses followed an $if #1824.
  • Report the correct type as not having a method when access fails #1828.
  • Prevent temp arena scribbling from causing an asan warning. #1825
  • Fix bug where &i[0] = null was not detected to be an error #1833.

Stdlib changes

  • Increase BitWriter.write_bits limit up to 32 bits.
  • Updates to Slice2d, like get_xy and others.
  • Added iter() value_iter() and key_iter() to HashMap.
  • Add "tokenizer" to String.
  • Add "skip_empty" to split methods. Add split_to_buffer method.
  • Add @enum_from_value.
  • Updated hash function.
  • Added URL parser.
  • Added convenience functions to Maybe.
  • Added String.trim_left() and .trim_right().
  • Deprecation of several & macros.
  • Format functions for timedates.
  • Add @assert_leak() to assert on memory leaks in the scope.
  • Added double.set_high_word(), double.set_low_word(), and float.set_word().

If you want to read more about C3, check out the documentation: https://c3-lang.org or download it and try it out: https://github.com/c3lang/c3c

Comments


Comment by Christoffer Lernö

The 0.6.6 release is surprisingly on time despite (or perhaps thanks to?) the Christmas holidays. It's a new year, and this summer C3 will turn 6. By April, version 0.7.0 will be released, removing deprecated code. The plan is to have one "dot one" release each year until 1.0 is reached (and if everything goes according to plan, the version after 0.9 will be 1.0).

But let's dive into what's new in this release!

Enum from_ordinal / ordinal changes

A longstanding pain point with enums has been modeling C enums with "gaps," as in this example:

typedef enum
{
    ABC = 42,
    GHJ = 101
} MyEnum;

A workaround in C3 using associated values like this doesn't feel ideal:

enum MyEnum : (int val)
{
    ABC = 42,
    GHJ = 101
}

This is because you can get the ordinal of the enum with a cast, e.g., (int)MyEnum.ABC, whereas retrieving the an associated value requires access, like MyEnum.ABC.val.

There are some ideas to bridge this gap, but the fact that the enums would cast to their ordinal by default made it hard to create a reasonable feature for it.

To address this, casts are now deprecated and replaced by MyEnum.from_ordinal(value). This type method is essentially a no-op, as is converting an enum to a number with .ordinal (e.g. MyEnum.ABC.ordinal).

This puts something like MyEnum.ABC.val and MyEnum.ABC.ordinal on, more equal footing which will make it easier to implement possible solutions to the gap enum problem.

No more & macro arguments

Macros differ from functions by allowing 5 (!) additional types of arguments:

  1. Compile-time constant arguments ($foo)
  2. Compile-time type arguments ($Type)
  3. Unevaluated expression arguments (#expr)
  4. And, finally, reference arguments (&ref)

With 0.6.6 &ref arguments become deprecated. With #expr arguments actually already being able to do what &ref does, this is not a loss in functionality. It does put somewhat more effort in using an #expr argument in terms of checking, but with the fairly marginal use of &ref in the standard library it didn't seem like a loss.

The main advantage for dropping &ref is that it no longer needs to be explained or motivated. It also simplifies the compiler in places where it ended up adding surprising complexities – but those simplifications can't be done until 0.7 when &ref is removed (rather than deprecated).

More important than the change is that the removal of & (which has been around for as long as the macros), marks the beginning of consolidating the language: removing things that have proven over time to be superfluous.

void rather than void! as default for main, tests and benchmarks

Another consolidation is the removal of void! as a return type for main.

Previously, it was possible to return an optional Empty value from main and have the compiler turn it into a 1 exit code.

This was problematic because it effectively discarded the Optional’s excuse. It also encouraged bad code which would just rethrow Optionals from functions called by main

void! was removed from @test functions for a similar reason. Actually panicking (with !! rather than !) would yield much better error messages for free. So again the choice between void and void! for test functions were not really helpful.

Starting from version 0.6.6, use void or int as the return type for main functions, and void for @test and @benchmark functions.

$foreach iterating over Strings

An oversight previously made it impossible to iterate over Strings and bytes. With 0.6.6, this now works correctly.

var declaring lambdas

Using var has been limited to macros in C3, but now they're also allowed in functions when declaring lambdas.

@operator(construct)

An experimental feature called construct is available from 0.6.6.

This allows a limited form of static methods on types. For example:

fn Foo Foo.new_with_bar(Bar b) @operator(construct)
{
    return { .b = b }; // Same as Foo { .b = b }
}

The limitation is that such a method must either return the type or a pointer to the type with the method. (So in the above example, either returning Foo or Foo* would be possible).

Consider this an experimental preview for now!

Standard Library Improvements The standard library has seen useful additions, including foreach-compatible iterators for HashMap and an URL parser.

The default hash functions have been improved, enhancing HashMap performance. There have also been other minor additions and changes.

Bug fixes

This version contains over 50 bug fixes, which is about twice from 0.6.5, but less than the record setting 0.6.2.

Refactoring

0.6.6 includes initial steps toward refactoring parts of the frontend representation to facilitate the addition of new backends. A C backend is planned for this year, making these improvements essential.

What's next?

The enums might finally get the "gap enum" usecase filled (pun intended), but it's not clear whether this will happen in 0.6.7 or will be pushed to later.

There is also a glaring hole in compile time evaluation where $i[1] = 2; isn't allowed, but $a = $i[1]; is. This will need to change, but to do so there needs to be some refactoring, which ties into the overall changes in the frontend representation to help the backend lowering, but also static analysis.

Users, despite work under the hood, should see very little of these changes, except maybe that some corner cases (like compile time subscript above) starts working.

There are some syntax tweaks coming up, but they should be very minor and probably not even be visible to most users.

More importantly though, is that C3 will see the beginning of work to prune unused features from the language, which will then eventually be removed with 0.7.0.

0.6.6 Change list

Here is the full change list

Changes / improvements

  • Split help into normal and "full" help, #1703
  • Removed 'headers' command line option.
  • Add enum.from_ordinal and fault.from_ordinal
  • Deprecate cast-style conversion from integer <-> enum.
  • Make deprecation an error in test mode.
  • Add --win-vs-dirs to override VS detection dirs.
  • Add "name" project property to override the name of the resulting binary. #1719
  • Improved add-project to take arguments.
  • Improve error reporting when using type names as the function argument #1750.
  • Improve ordering of method registration to support adding methods to generic modules with method constraints #1746
  • Support experimental @operator(construct) operator overload.
  • Allow using 'var' to declare lambdas in functions.
  • Add 'validation' setting and make dead code a warning.
  • Allow compile time $foreach iteration over constant Strings and bytes.
  • Improved error message when accessing @private from other modules #1769.
  • Include @name when searching for possible matches to name in the error message. #1779
  • Improve @param parse errors #1777
  • Improved #foo resolution inside of the compiler.
  • Deprecated '&' macro arguments.
  • Deprecate `fn void! main() type main functions.
  • Deprecate old void! @benchmark and @test functions.
  • Allow test runners to take String[] arguments.
  • Added --lsp output.
  • Improve the error message when running out of memory.
  • Allowed passing arguments to @test / @benchmark runners via c3c test[benchmark] -- -o --opt1 <arg1>
  • Handle bytes and string literals the same way in terms of zero termination.
  • Function comments are stored and displayed with -P.
  • Prevent #hash arguments from taking code that modifies ct variables. #1794
  • Make stringify to recursively enter #hash expressions #1834.

Fixes

  • Fix case trying to initialize a char[*]* from a String.
  • Fix Map & HashMap put_all_for_create not copying all elements, causing init_from_map to create incomplete copy.
  • Fix bug when a macro calling an extern function was called in another module also declaring and calling the same function. #1690
  • static-lib and dynamic-lib options from the command line now produces headers.
  • Fix bug outputting exported functions without predefined extname.
  • Fix problem where crt1 was linked for dynamic libraries on Linux and BSD. #1710
  • Fix CRT detection on Arch Linux.
  • Fix lexer allowing a trailing underscore (_) with hex and binary literals.
  • Fix --list-operators CLI command printing underscore (_) and hash (#).
  • Fix bug in temp allocator when temp memory is exhausted and allocation needs overaligned mem. #1715
  • Incorrectly handles distinct enums and pointers with '+=' and '-=' #1717.
  • Prevent DString from being initialized with "".
  • Fix bug in OnStackAllocator when freeing overallocated data. #1720
  • Use weak_odr rather than weak on Windows which seems to prevent issues such as #1704.
  • Use weak on dyn-symbols on Linux.
  • Fix crash on project.json not having an empty set of targets.
  • Miscompile when indexing an array with small unsigned types for enums.
  • Change CBool to be 1 byte.
  • any_to_int checks value to be int and no longer works with enum.
  • Add check in formatter printing "%c".
  • Fix bug where !! and ! was not recognized to jump out of the current scope.
  • Fix bug when including compile time parameters in trailing body more than once.
  • Fix issue with compiling a constant struct containing a string array in a local context.
  • Fix error where panic would not properly stop the program when stacktrace couldn't be printed #1751.
  • Macros with default arguments to &, # and type parameters didn't work as expected. #1754.
  • net::poll() with negative timeout behaved incorrectly.
  • Return type inference bugs with macros #1757
  • $defined in a global scope should accept testing normal macros.
  • Assert on add to uninitialized ct variable #1765.
  • Dynamic function lookup fails after changing type without dummy anycast #1761
  • $vasplat was allowed inside of a function when passed as an argument to a function.
  • Prohibit raw vaargs in regular functions with a function body.
  • Assert on certain slice to slice casts. #1768.
  • Fix vector float -> bool conversion.
  • Fix +a = 1 erronously being accepted.
  • Fix not freeing a zero length String
  • Macros with trailing bodys aren't allowed as the single statement after a while loop with no body #1772.
  • Deref subscripts as needed for macro ref method arguments. #1789
  • Change ordering to simplify adding methods to type in conditional modules.
  • #foo style arguments were not type checked when given a type. #1790
  • Bug when using +++ on value build a slice or array: the rhs cast was not done.
  • Fix bug preventing compile time slices from being iterated over with $foreach.
  • Fix bug with defer assignment in macro #1807.
  • Fix regression with swizzle references for vectors #1810.
  • Assert when partially initializing a constant struct containing a slice #1812.
  • Assert concatenating constant slices #1805.
  • Do not link "ld" on Linux with no libc.
  • Fix bug when multiple $else clauses followed an $if #1824.
  • Report the correct type as not having a method when access fails #1828.
  • Prevent temp arena scribbling from causing an asan warning. #1825
  • Fix bug where &i[0] = null was not detected to be an error #1833.

Stdlib changes

  • Increase BitWriter.write_bits limit up to 32 bits.
  • Updates to Slice2d, like get_xy and others.
  • Added iter() value_iter() and key_iter() to HashMap.
  • Add "tokenizer" to String.
  • Add "skip_empty" to split methods. Add split_to_buffer method.
  • Add @enum_from_value.
  • Updated hash function.
  • Added URL parser.
  • Added convenience functions to Maybe.
  • Added String.trim_left() and .trim_right().
  • Deprecation of several & macros.
  • Format functions for timedates.
  • Add @assert_leak() to assert on memory leaks in the scope.
  • Added double.set_high_word(), double.set_low_word(), and float.set_word().

If you want to read more about C3, check out the documentation: https://c3-lang.org or download it and try it out: https://github.com/c3lang/c3c

Why doesn't C3 do import aliasing?

Originally from: https://c3.handmade.network/blog/p/8982-why_doesn%2527t_c3_do_import_aliasing

Why doesn't C3 allow aliasing of namespaces? Wouldn't import raylib as rl; be a good idea? Why am I so hell-bent on not allowing it?

So, first off, C2 has aliasing. It offers three levels:

  1. Importing with the full name.
  2. Importing with an alias.
  3. Folding the imports (so everything can be used without prefix)

Why didn't I keep this?

C2's flat module namespace

First off, it's important to realize that C2 has a flat module namespace. There are no std::core::mem submodules, everything is bundled up into fairly large groups of functions and types. For example the stdio module which would have all the io functions and so on.

My concern was that it would be very likely for libraries to collide if they were flat.

EVERYONE would want their particular name to be the name for something. So now you can't use library net from Foo, because you also want to use the library net from Bar.

Aliasing won't help you because import net as foonet only works if you can disambiguate net to begin with.

It's possible people would start gravitating towards "prefix+name", but then everything would be superheavy name-wise: import somecompany_networking; and you'd have to use aliasing because the prefixes are so long.

Ok, hierarchical - now what?

Obviously no one would want to repeatedly prefix things like somecompany::networking::open(...) (except Zig programmers), so it would seem that import somecompany::networking as net would need to be the default?

And then we could all use net.Socket socket; and net.open("localhost") (skip to the end if you want to know why C3 uses :: instead of .).

Except what happens now that we started to have hierarchical module names is that we go from having to rename maybe 2-3 BIG packages to coming up with short names for 20 or 30 modules.

The naming problem is going from fairly trivial to painful.

The best C naming convention

Let's make a short detour: the best naming convention for library functions in C, is in my opinion: <category or type>_name(...) So for example if you look for something relating to the Expr type in the C3 compiler it would be expr_<some name> for the semantic checking, all such functions are sema_<name> and so on.

Wouldn't it be nice to have that kind of formalized? Like expr.copy_single(expr) and sema.resolve_desc(d)?

Is there a way to avoid renames?

I was racking my brains for a way to make it work without the need for renaming or namespace folding. The latter was never really recommended in C2, but was left in – probably when writing your own code and you just wanted everything to feel like a homogeneous code base.

My solution was to match on the minimum necessary prefix to disambiguate two things. So let's say we had std::io::File and foo::io::File then we need the full path to know which File was intended – so it would need the full path, but if it was std::io::File and foo::net::File then net::File and io::File would be enough to do the disambiguation.

This would allow people to use terse submodule names while still avoiding the need for explicit renaming.

Formalizing the C coding convention

With submodule names now rarely being used aside from when one was disambiguating functions, it was now possible to encode the C coding convention and say that functions need at least one level of module path.

Something like io::printn("Hello, world") in C3 should be thought of as io_printn("Hello, world").

And this is why under this new scheme using import renaming went from "needed" to "destructive". Because if you're used to seeing io::printn() and suddenly see io2::printn() you're going to think this is something completely new, unless it's code you wrote yourself 15 minutes ago.

The effect on generic modules

Originally generic modules were instantiated with import, so something like:

import std::collections::list(<int>) as intlist;
import std::collections::list(<doublelist>) as doublelist;

This actually mirrors better what is happening in the compiler, but then using things like doublelist::List l isn't really a user-friendly syntax to use.

With the new style you just imported the generic module and then made defines:

import std::collections::list;

def IntList = List(<int>);
def DoubleList = List(<double>);

This would have been possible before too, but it wasn't until the aliasing was removed this style became the "obvious" solution.

Recursive imports

Another effect of the change was allowing recursive imports. Already I've mentioned how the number of submodules made it more of a problem with naming imports, a key reason it has little place in C3 today is because C3 went for recursive imports.

So in C3, if you import std::collections you will get the module but also any submodule (and their submodules) directly available. (In fact, C3 experimented with no import statements at all!)

Let's say you want to import and add an alias. Fine, but this only affects part of what you import. Say you'd do import std::collections as cl.

Okay, but std::collections itself has zero functions and types. This comes back to the fact that when you start separating things into submodules you have so many more names if you want to rename them.

Big modules vs granular modules

In short, what we see is that rename is quite fine when we have big modules containing a lot of functions. This typically happens in two cases:

  1. You have a flat module namespace – like C2
  2. You are using a C library which is modelled as a single flat namespace.

A good experience in C3 without renaming?

There are good and bad ways to use modules in C3. Unfortunately, unlike in many other languages, if the library author is sloppy, you will suffer.

  1. You have to think about the usage. The raylib 4 bindings (by yours truly) infamously use module raylib;. This meant that everyone needed to type raylib:: in front of all the calls. The raylib5 bindings instead use module raylib5::rl. Not only did that make all functions now prefix with
    the more reasonable rl prefix, but there is room to add raymath as module raylib5::rm and so on.
  2. Coming from other languages, people often use more namespacing than they need to. It's just more to type, worse readability and makes the language look like C++. It's File f;, not std::io::File f;. The latter is the antithesis of good style. – And same with functions, one level, no more.
  3. If you're making up names of types, you need to ensure it looks unique because if it has to be namespaced then it has failed. So for example SdlMutex, not Mutex. There is an idea that using the module name is good enough to disambiguate types. It isn't, because C3 isn't built to accommodate C++ style.

Final Word

C3 doesn't have aliasing anymore because of a rather complex dynamic, and all the organization of the standard library and such does depend on each other.

Allowing some external mechanism to "hack" a broken module name and allowing it to be swapped for another at the command line is one thing (e.g. you have to use that library which has the raylib prefix then you might use something like --namespace-patch raylib rl at the command line to make it alias to rl instead) – This is a fairly straightforward enhancement.

To allow C2 aliasing though, that would mean rethinking everything about C3 modules, so that would require quite strong reasons for it to happen. But I prefer to never say never, I just want the language to be as good as possible.

Bonus: why doesn't C3 use .?

The short answer is that :: makes it possible to do the path shortening. Otherwise figuring out where the path starts would need to have a very complicated heuristic. Another thing is that makes sense if your names are things that won't collide with local variables. Now, in C3 we have std::io::file. If C3 used . then something like File file = file.open("foo.txt", "rb") would be ambiguous, whereas with :: we're always fine: File file = file::open("foo.txt", "rb").

(It also saves the grammar from being complicated in some other places, but these are the main reasons).

Comments


Comment by Christoffer Lernö

Why doesn't C3 allow aliasing of namespaces? Wouldn't import raylib as rl; be a good idea? Why am I so hell-bent on not allowing it?

So, first off, C2 has aliasing. It offers three levels:

  1. Importing with the full name.
  2. Importing with an alias.
  3. Folding the imports (so everything can be used without prefix)

Why didn't I keep this?

C2's flat module namespace

First off, it's important to realize that C2 has a flat module namespace. There are no std::core::mem submodules, everything is bundled up into fairly large groups of functions and types. For example the stdio module which would have all the io functions and so on.

My concern was that it would be very likely for libraries to collide if they were flat.

EVERYONE would want their particular name to be the name for something. So now you can't use library net from Foo, because you also want to use the library net from Bar.

Aliasing won't help you because import net as foonet only works if you can disambiguate net to begin with.

It's possible people would start gravitating towards "prefix+name", but then everything would be superheavy name-wise: import somecompany_networking; and you'd have to use aliasing because the prefixes are so long.

Ok, hierarchical - now what?

Obviously no one would want to repeatedly prefix things like somecompany::networking::open(...) (except Zig programmers), so it would seem that import somecompany::networking as net would need to be the default?

And then we could all use net.Socket socket; and net.open("localhost") (skip to the end if you want to know why C3 uses :: instead of .).

Except what happens now that we started to have hierarchical module names is that we go from having to rename maybe 2-3 BIG packages to coming up with short names for 20 or 30 modules.

The naming problem is going from fairly trivial to painful.

The best C naming convention

Let's make a short detour: the best naming convention for library functions in C, is in my opinion: <category or type>_name(...) So for example if you look for something relating to the Expr type in the C3 compiler it would be expr_<some name> for the semantic checking, all such functions are sema_<name> and so on.

Wouldn't it be nice to have that kind of formalized? Like expr.copy_single(expr) and sema.resolve_desc(d)?

Is there a way to avoid renames?

I was racking my brains for a way to make it work without the need for renaming or namespace folding. The latter was never really recommended in C2, but was left in – probably when writing your own code and you just wanted everything to feel like a homogeneous code base.

My solution was to match on the minimum necessary prefix to disambiguate two things. So let's say we had std::io::File and foo::io::File then we need the full path to know which File was intended – so it would need the full path, but if it was std::io::File and foo::net::File then net::File and io::File would be enough to do the disambiguation.

This would allow people to use terse submodule names while still avoiding the need for explicit renaming.

Formalizing the C coding convention

With submodule names now rarely being used aside from when one was disambiguating functions, it was now possible to encode the C coding convention and say that functions need at least one level of module path.

Something like io::printn("Hello, world") in C3 should be thought of as io_printn("Hello, world").

And this is why under this new scheme using import renaming went from "needed" to "destructive". Because if you're used to seeing io::printn() and suddenly see io2::printn() you're going to think this is something completely new, unless it's code you wrote yourself 15 minutes ago.

The effect on generic modules

Originally generic modules were instantiated with import, so something like:

import std::collections::list(<int>) as intlist;
import std::collections::list(<doublelist>) as doublelist;

This actually mirrors better what is happening in the compiler, but then using things like doublelist::List l isn't really a user-friendly syntax to use.

With the new style you just imported the generic module and then made defines:

import std::collections::list;

def IntList = List(<int>);
def DoubleList = List(<double>);

This would have been possible before too, but it wasn't until the aliasing was removed this style became the "obvious" solution.

Recursive imports

Another effect of the change was allowing recursive imports. Already I've mentioned how the number of submodules made it more of a problem with naming imports, a key reason it has little place in C3 today is because C3 went for recursive imports.

So in C3, if you import std::collections you will get the module but also any submodule (and their submodules) directly available. (In fact, C3 experimented with no import statements at all!)

Let's say you want to import and add an alias. Fine, but this only affects part of what you import. Say you'd do import std::collections as cl.

Okay, but std::collections itself has zero functions and types. This comes back to the fact that when you start separating things into submodules you have so many more names if you want to rename them.

Big modules vs granular modules

In short, what we see is that rename is quite fine when we have big modules containing a lot of functions. This typically happens in two cases:

  1. You have a flat module namespace – like C2
  2. You are using a C library which is modelled as a single flat namespace.

A good experience in C3 without renaming?

There are good and bad ways to use modules in C3. Unfortunately, unlike in many other languages, if the library author is sloppy, you will suffer.

  1. You have to think about the usage. The raylib 4 bindings (by yours truly) infamously use module raylib;. This meant that everyone needed to type raylib:: in front of all the calls. The raylib5 bindings instead use module raylib5::rl. Not only did that make all functions now prefix with
    the more reasonable rl prefix, but there is room to add raymath as module raylib5::rm and so on.
  2. Coming from other languages, people often use more namespacing than they need to. It's just more to type, worse readability and makes the language look like C++. It's File f;, not std::io::File f;. The latter is the antithesis of good style. – And same with functions, one level, no more.
  3. If you're making up names of types, you need to ensure it looks unique because if it has to be namespaced then it has failed. So for example SdlMutex, not Mutex. There is an idea that using the module name is good enough to disambiguate types. It isn't, because C3 isn't built to accommodate C++ style.

Final Word

C3 doesn't have aliasing anymore because of a rather complex dynamic, and all the organization of the standard library and such does depend on each other.

Allowing some external mechanism to "hack" a broken module name and allowing it to be swapped for another at the command line is one thing (e.g. you have to use that library which has the raylib prefix then you might use something like --namespace-patch raylib rl at the command line to make it alias to rl instead) – This is a fairly straightforward enhancement.

To allow C2 aliasing though, that would mean rethinking everything about C3 modules, so that would require quite strong reasons for it to happen. But I prefer to never say never, I just want the language to be as good as possible.

Bonus: why doesn't C3 use .?

The short answer is that :: makes it possible to do the path shortening. Otherwise figuring out where the path starts would need to have a very complicated heuristic. Another thing is that makes sense if your names are things that won't collide with local variables. Now, in C3 we have std::io::file. If C3 used . then something like File file = file.open("foo.txt", "rb") would be ambiguous, whereas with :: we're always fine: File file = file::open("foo.txt", "rb").

(It also saves the grammar from being complicated in some other places, but these are the main reasons).


Comment by Christoffer Lernö

The need to rename a namespace comes from the fact that there are irresolvable namespace collisions in the first place. This solution prevents that from happening.

What would you prefer? That namespace aliases are consistent over all code bases, or that they are only limited by the imagination of the user?

C3 prefers the former while also making sure that there are no irresolvable collisions despite that.

In C3, you cannot have two modules that have the full name net without them being the same module.

Therefore, disambiguation is not a problem the aliasing will resolve.

The way C3 looks for files is different from Odin. C3 takes a list of files, or directories. ALL C3 files in those directories and in those lists of files will be read by the compiler.

If you have libraries, which are basically directories with a small manifest + c3 files + possibly dynamic/static libraries, these are loaded (again completely) if a dependency is declared on the library when you compile.

So for example, you wish to use raylib with a single file you want to compile and run from the command line: c3c compile-run my_program.c3 --lib raylib

Now in addition to that in your program you need to write something like import raylib; to make the functions available to the code in that module.

So the import statements do not control what is compiled, they simply control what modules are visible.


Comment by Christoffer Lernö

The full name being the full module name, such as mylib::net. It is not possible to have two of those from different sources and still consider them distinct. C3 allows extending modules, so they would be seen as different parts of the same module, even if they are placed in different folders.

For C3, it takes a bunch of files of indata. Each of those files may have one or more module sections in them. A module section being a part of a module. To make it more understandable, thing about how C++ namespaces work. You can open the same namespace multiple times.

Similarly in C3 you can open the same module and append functions and types to it multiple times and from multiple files.


Comment by Christoffer Lernö

Well yes. If you had two libraries called io, how would you even be able to tell them apart? What would import io; mean? Importing both?

The only case when it's possible to have two libraries with the same name, is when imports are file based, so you do something like import "foo/io.lib" and import "bar/io.lib".

This is not how the module model works in C3, so the case doesn't even arise. While you could certainly have a foo/io.c3l and bar/io.c3l that you depend on, the way modules work in C3 would merge these modules if you use those libraries. And the reason why they are merged is because C3 has a very open model. It's possible to extend any module and any type.

In the file version we usually HAVE a longer module name in the sense that the path acts as this module name to differentiate it. It's just that it's informal whereas in C3 or something like Java it's formalized.

More splat and bug fixes: C3 0.6.5 is released

Originally from: https://c3.handmade.network/blog/p/8979-more_splat_and_bug_fixes__c3_0.6.5_is_released

0.6.5 is out, and it brings some improvements and a bunch of bug fixes.

Splat in initializers

Splat has gained more and more features the last releases, and this is likely the last one, allowing you to splat into initializers. This means something like this now works:

struct Foo { int a; int b; }

fn void test(int[2] x)
{
  Foo f = { ...x };
}

Improved [] overloading

Using list[i] *= 2; or list[i]++ where "list" was a type with subscript overloading (such as List) was not possible. This now works fine.

Improved Optionals

Previously using a++ where "a" was an optional, had required using the optional result. This is now no longer the case, and

int! a = foo();
a++;

Works fine.

Updated command line options

A "quiet" option using -q is now available to suppress all non-error output. Furthermore the --debug-log and --debug-stats have been replaced by -v, -vv and -vvv for increasing verbosity.

Stdlib changes

Various fixes are in the 0.6.5, as well as a more convenient API when doing base32/64 encoding. Other small quality-of-life additions such as file::save have found its way into the standard library as well.

What's next

Looking towards the 0.7.0 release, there might be a breaking change in how pointer subscripting works.

Currently, given int[5]* a; doing a[0] will subscript the pointer, returning an int[5] value. This is often not what one desires and creates a fair amount of confusion. For this reason, subscripting pointers might be disallowed, making pointers to arrays implicitly dereference the pointer (so a[0] in our example would implicitly be (*a)[0]) with *(pointer + i) still working as usual. The future will tell how this should be tackled in C3.

Here is the full change list:

0.6.5 Change list

Changes / improvements

  • Allow splat in initializers.
  • Init command will now add test-sources to project.json #1520
  • a++ may be discarded if a is optional and ++/-- works for overloaded operators.
  • Improve support for Windows cross compilation on targets with case sensitive file systems.
  • Add "sources" support to library manifest.json, defaults to root folder if unspecified.
  • Add char_at method in DString and operators [], len, []= and &[].
  • Add -q option, make --run-once implicitly -q.
  • Add -v, -vv and -vvv options for increasing verbosity, replacing debug-log and debug-stats options.

Fixes

  • Fix bug where a > 0 ? f() : g() could cause a compiler crash if both returned void!.
  • @builtin was not respected for generic modules #1617.
  • Fix issue writing a single byte in the WriteBuffer
  • A distinct inline pointer type can now participate in pointer arithmetics.
  • Support &a[0] returning the distinct type when applying it to a distinct of a pointer.
  • Fix error when calling HashMap.remove on uninitialized HashMap.
  • Fix issue with resolved try-unwrap in defer.
  • Fix issue with overloaded subscript and ++/-- and assign ops (e.g. *=)
  • Fix issue with properties in different targets not being respected #1633.
  • Indexing an Optional slice would crash in codegen #1636.
  • SimpleHeapAllocator bug when splitting blocks allowed memory overrun.
  • Not possible to alias or take reference for extension methods on non-user defined types. #1637
  • Prevent methods from using names of properties or fields. #1638
  • b64 / hex data strings can now be used with ` as well.
  • Contracts on generic modules would evaluate too late, sometimes not catching the error until it already occurred elsewhere.
  • Fix bug preventing optionals from being used in ranges or as indices.
  • Crash compiling for arm64 when returning 16 byte and smaller structs by value not a power of 2 #1649.
  • Enforce single module compilation for static libraries to make constructors run properly.
  • Crash when using --no-obj without compile-only. #1653
  • Do not produce expression locations for windows.
  • Issue where multiple methods were accepted for the same type.
  • Issue where a method was linked to a type alias instead of the underlying type.
  • Fix Fnv1a encoding.
  • Fix issue with accessing arrays in access-overloaded types, e.g. list[1][2] #1665.
  • Cast removing arbitrary array indices and converting them to pointers should always be fine #1664
  • Incorrect "no-libc" definition of cos, making it unavailable for wasm.
  • Fix issue with the adjoint and inverse calculations for Matrix2x2.
  • It was possible to create 0 length arrays using byte literals. #1678
  • Crash when a constant null typeid is checked for properties. #1679

Stdlib changes

  • Add io::MultiReader, io::MultiWriter, and io::TeeReader structs.
  • Updated Base32 API.
  • Add file::save.
  • Add memcpy / memset / memcmp to nolibc.
  • Add sort::quickselect to find the k-th smallest element in an unordered list.
  • Add sort::is_sorted to determine if a list is sorted.

If you want to read more about C3, check out the documentation: https://c3-lang.org or download it and try it out: https://github.com/c3lang/c3c

Comments


Comment by Christoffer Lernö

0.6.5 is out, and it brings some improvements and a bunch of bug fixes.

Splat in initializers

Splat has gained more and more features the last releases, and this is likely the last one, allowing you to splat into initializers. This means something like this now works:

struct Foo { int a; int b; }

fn void test(int[2] x)
{
  Foo f = { ...x };
}

Improved [] overloading

Using list[i] *= 2; or list[i]++ where "list" was a type with subscript overloading (such as List) was not possible. This now works fine.

Improved Optionals

Previously using a++ where "a" was an optional, had required using the optional result. This is now no longer the case, and

int! a = foo();
a++;

Works fine.

Updated command line options

A "quiet" option using -q is now available to suppress all non-error output. Furthermore the --debug-log and --debug-stats have been replaced by -v, -vv and -vvv for increasing verbosity.

Stdlib changes

Various fixes are in the 0.6.5, as well as a more convenient API when doing base32/64 encoding. Other small quality-of-life additions such as file::save have found its way into the standard library as well.

What's next

Looking towards the 0.7.0 release, there might be a breaking change in how pointer subscripting works.

Currently, given int[5]* a; doing a[0] will subscript the pointer, returning an int[5] value. This is often not what one desires and creates a fair amount of confusion. For this reason, subscripting pointers might be disallowed, making pointers to arrays implicitly dereference the pointer (so a[0] in our example would implicitly be (*a)[0]) with *(pointer + i) still working as usual. The future will tell how this should be tackled in C3.

Here is the full change list:

0.6.5 Change list

Changes / improvements

  • Allow splat in initializers.
  • Init command will now add test-sources to project.json #1520
  • a++ may be discarded if a is optional and ++/-- works for overloaded operators.
  • Improve support for Windows cross compilation on targets with case sensitive file systems.
  • Add "sources" support to library manifest.json, defaults to root folder if unspecified.
  • Add char_at method in DString and operators [], len, []= and &[].
  • Add -q option, make --run-once implicitly -q.
  • Add -v, -vv and -vvv options for increasing verbosity, replacing debug-log and debug-stats options.

Fixes

  • Fix bug where a > 0 ? f() : g() could cause a compiler crash if both returned void!.
  • @builtin was not respected for generic modules #1617.
  • Fix issue writing a single byte in the WriteBuffer
  • A distinct inline pointer type can now participate in pointer arithmetics.
  • Support &a[0] returning the distinct type when applying it to a distinct of a pointer.
  • Fix error when calling HashMap.remove on uninitialized HashMap.
  • Fix issue with resolved try-unwrap in defer.
  • Fix issue with overloaded subscript and ++/-- and assign ops (e.g. *=)
  • Fix issue with properties in different targets not being respected #1633.
  • Indexing an Optional slice would crash in codegen #1636.
  • SimpleHeapAllocator bug when splitting blocks allowed memory overrun.
  • Not possible to alias or take reference for extension methods on non-user defined types. #1637
  • Prevent methods from using names of properties or fields. #1638
  • b64 / hex data strings can now be used with ` as well.
  • Contracts on generic modules would evaluate too late, sometimes not catching the error until it already occurred elsewhere.
  • Fix bug preventing optionals from being used in ranges or as indices.
  • Crash compiling for arm64 when returning 16 byte and smaller structs by value not a power of 2 #1649.
  • Enforce single module compilation for static libraries to make constructors run properly.
  • Crash when using --no-obj without compile-only. #1653
  • Do not produce expression locations for windows.
  • Issue where multiple methods were accepted for the same type.
  • Issue where a method was linked to a type alias instead of the underlying type.
  • Fix Fnv1a encoding.
  • Fix issue with accessing arrays in access-overloaded types, e.g. list[1][2] #1665.
  • Cast removing arbitrary array indices and converting them to pointers should always be fine #1664
  • Incorrect "no-libc" definition of cos, making it unavailable for wasm.
  • Fix issue with the adjoint and inverse calculations for Matrix2x2.
  • It was possible to create 0 length arrays using byte literals. #1678
  • Crash when a constant null typeid is checked for properties. #1679

Stdlib changes

  • Add io::MultiReader, io::MultiWriter, and io::TeeReader structs.
  • Updated Base32 API.
  • Add file::save.
  • Add memcpy / memset / memcmp to nolibc.
  • Add sort::quickselect to find the k-th smallest element in an unordered list.
  • Add sort::is_sorted to determine if a list is sorted.

If you want to read more about C3, check out the documentation: https://c3-lang.org or download it and try it out: https://github.com/c3lang/c3c

New contract syntax with 0.6.4

Originally from: https://c3.handmade.network/blog/p/8965-new_contract_syntax_with_0.6.4

Finally 0.6.4 is out. This version has some very important improvements working with constant slices. It is also more permissive when it comes to working with generic types.

But perhaps the change which is the most obvious is the introduction of a new contract syntax using <* *>, away from the Javadoc-like /** */ which was hard to differentiate from comments.

Constant slices

Constant slices has gotten a lot of low in 0.6.4. Constant vectors can now convert into constant slices, and slicing arrays, bytes and constant slices are now compile time operations.

This also adds constant bytes to/from char[] conversions.

New contract syntax

The use of /** */ for declaring a doc/contract block has been around for a long time in C3.

Unfortunately it has a glaring problem: if you accidentally forget a * and write /* */ your docs are considered comments and are silently ignored. This is obviously very bad.

Another problem would be code where the user accidentally typed /** */ for comments inside a function or similar. This was very hard to give some proper errors for without complicating parsing.

By picking a different syntax - <* *>, these problems go away. They also address some of the criticisms against using doc-comments for contracts.

Interface/any changes

Previously it was possible to assign a void* to an interface or any. This led to the weird situation of the type inside being void, which is a nonsense value and required special handling for it to work properly in almost all code. This is no longer allowed.

As an improvement interfaces now support .ptr and .type directly without casting to an any.

Ternary

Inference now works across ternary, so for example: ZString a = b > 0 ? "x" : "y"; would do the right thing.

0.6.4 also fixes a crash when returning a struct or vector from a ternary expression.

Weakly linking the standard library

The standard library is now weakly linked, which allows multiple copies of the same standard library to work when linked together. Otherwise things like using a static library written in C3 would have problems linking to a C3 program.

What's next?

0.6.5 will clean up the CLI, tests and overall improvements. While tagged unions are discussed, it's unclear whether they will be considered for the next release.

A problem which keeps popping up is the packaged LLVM libraries. These are frequently broken causing unnecessary work for everyone. Currently the Windows libraries are precompiled by a separate CI. This will probably be the way forward for all platforms, and that work will probably go into the 0.6.5 release cycle as well.

0.6.4 Change list

There are many other fixes and changes, see the full changelist:

Changes / improvements

  • Const vector -> const slice implicit conversion.
  • Slicing arrays, slices and bytes at compile time #1466.
  • Better error for int a[4] = .... #1518
  • Better error for int Foo(int a) declarations #1516
  • Improve error message in the case of MyInterface x = foo; #1522
  • Deprecate @adhoc, allow non-nested ad hoc generic types.
  • Constant bytes <=> char[] conversion should work #1514.
  • Infer now works across ternary.
  • Interfaces now support .ptr and .type directly without casting to any.
  • Switch to <* *> docs.
  • Improve error messages on expressions like var $type = int; #1553.
  • Disallow casting a void* to any or an interface, unless it is null.
  • Defer resolution of declarations when looked up in def aliased #1559.
  • Adding constants to the Json AST #1540
  • Adding info to the globals inside Json AST #1541
  • Null-check function pointer invocation #1573.
  • string::new_struct_to_str and io::struct_to_format to dump struct data.
  • io::print will now print structs.
  • Improve error message when using void aliases as variable storage type.
  • Add a target type: "prepare" which doesn't compile anything (but may run exec)

Fixes

  • Unsupported int[*] $x = { 1, 2, 3, 4 } #1489.
  • Unexpected compile error using a typed constant with copysign #1517
  • Incorrect subscript resolution #1519.
  • Segfault with passing a program with - using stdin.
  • Using no module with - would reject the program.
  • Unintended deref of pointers with methods caused regression with hash function.
  • Fix broken sincos function.
  • Bug when a continue is copied in a defer.
  • Compiler error when any/interface initialized using {} #1533.
  • Bug when defers and $if were combined in a macro, which would cause miscompilation.
  • Fixes to the CSV reader.
  • Crash returning struct or vector from function using ternary expression #1537.
  • Improved error message on invalid subscript index type #1535.
  • Improved error message when declaring a variable void!.
  • Cannot use void as a generic parameter #1546
  • Interfaces not correctly copied with generics #1545
  • Memory leak in keys.new_list fixed.
  • Standard library is now correctly weakly linked, fixing the use of C3 .so together with executable. #1549, #1107.
  • Wrong error message for interface methods with body #1536.
  • Empty expression block would crash compiler with debug on #1554.
  • Improve infer conversions on constants, e.g. ZString a = foo ? "a" : "b"; #1561
  • Show error when declarations do not start with fn in interfaces. #1565
  • if (try foo) was handled incorrectly inside a defer.
  • &self argument not implicitly null checked. #1556.
  • (uptr)&((Foo*)null).a incorrectly inserts a null check. #1544
  • Incorrect error message when $eval is provided an invalid string. #1570
  • HashMap.copy_keys did not properly copy keys which needed to be allocated #1569
  • Named vector component access would not fold at compile time. #1574
  • $define would occasionally not properly evaluate declarations it encountered.
  • Fixes with error handling recursive @tag #1583.
  • Sometimes generating introspection info would not be in the global scope causing a crash #1586.
  • @tag on macros cannot be retrieved with tagof #1582
  • Taking the $typeof of a wildcard optional returns void!.

Stdlib changes

  • Remove unintended print of char[] as String
  • Add read/write to stream with big endian ints.
  • Move accidently hidden "wrap_bytes".
  • Added CBool #1530.
  • Added encoding/base32 module.

If you want to read more about C3, check out the documentation: https://c3-lang.org or download it and try it out: https://github.com/c3lang/c3c

Comments


Comment by Christoffer Lernö

Finally 0.6.4 is out. This version has some very important improvements working with constant slices. It is also more permissive when it comes to working with generic types.

But perhaps the change which is the most obvious is the introduction of a new contract syntax using <* *>, away from the Javadoc-like /** */ which was hard to differentiate from comments.

Constant slices

Constant slices has gotten a lot of low in 0.6.4. Constant vectors can now convert into constant slices, and slicing arrays, bytes and constant slices are now compile time operations.

This also adds constant bytes to/from char[] conversions.

New contract syntax

The use of /** */ for declaring a doc/contract block has been around for a long time in C3.

Unfortunately it has a glaring problem: if you accidentally forget a * and write /* */ your docs are considered comments and are silently ignored. This is obviously very bad.

Another problem would be code where the user accidentally typed /** */ for comments inside a function or similar. This was very hard to give some proper errors for without complicating parsing.

By picking a different syntax - <* *>, these problems go away. They also address some of the criticisms against using doc-comments for contracts.

Interface/any changes

Previously it was possible to assign a void* to an interface or any. This led to the weird situation of the type inside being void, which is a nonsense value and required special handling for it to work properly in almost all code. This is no longer allowed.

As an improvement interfaces now support .ptr and .type directly without casting to an any.

Ternary

Inference now works across ternary, so for example: ZString a = b > 0 ? "x" : "y"; would do the right thing.

0.6.4 also fixes a crash when returning a struct or vector from a ternary expression.

Weakly linking the standard library

The standard library is now weakly linked, which allows multiple copies of the same standard library to work when linked together. Otherwise things like using a static library written in C3 would have problems linking to a C3 program.

What's next?

0.6.5 will clean up the CLI, tests and overall improvements. While tagged unions are discussed, it's unclear whether they will be considered for the next release.

A problem which keeps popping up is the packaged LLVM libraries. These are frequently broken causing unnecessary work for everyone. Currently the Windows libraries are precompiled by a separate CI. This will probably be the way forward for all platforms, and that work will probably go into the 0.6.5 release cycle as well.

0.6.4 Change list

There are many other fixes and changes, see the full changelist:

Changes / improvements

  • Const vector -> const slice implicit conversion.
  • Slicing arrays, slices and bytes at compile time #1466.
  • Better error for int a[4] = .... #1518
  • Better error for int Foo(int a) declarations #1516
  • Improve error message in the case of MyInterface x = foo; #1522
  • Deprecate @adhoc, allow non-nested ad hoc generic types.
  • Constant bytes <=> char[] conversion should work #1514.
  • Infer now works across ternary.
  • Interfaces now support .ptr and .type directly without casting to any.
  • Switch to <* *> docs.
  • Improve error messages on expressions like var $type = int; #1553.
  • Disallow casting a void* to any or an interface, unless it is null.
  • Defer resolution of declarations when looked up in def aliased #1559.
  • Adding constants to the Json AST #1540
  • Adding info to the globals inside Json AST #1541
  • Null-check function pointer invocation #1573.
  • string::new_struct_to_str and io::struct_to_format to dump struct data.
  • io::print will now print structs.
  • Improve error message when using void aliases as variable storage type.
  • Add a target type: "prepare" which doesn't compile anything (but may run exec)

Fixes

  • Unsupported int[*] $x = { 1, 2, 3, 4 } #1489.
  • Unexpected compile error using a typed constant with copysign #1517
  • Incorrect subscript resolution #1519.
  • Segfault with passing a program with - using stdin.
  • Using no module with - would reject the program.
  • Unintended deref of pointers with methods caused regression with hash function.
  • Fix broken sincos function.
  • Bug when a continue is copied in a defer.
  • Compiler error when any/interface initialized using {} #1533.
  • Bug when defers and $if were combined in a macro, which would cause miscompilation.
  • Fixes to the CSV reader.
  • Crash returning struct or vector from function using ternary expression #1537.
  • Improved error message on invalid subscript index type #1535.
  • Improved error message when declaring a variable void!.
  • Cannot use void as a generic parameter #1546
  • Interfaces not correctly copied with generics #1545
  • Memory leak in keys.new_list fixed.
  • Standard library is now correctly weakly linked, fixing the use of C3 .so together with executable. #1549, #1107.
  • Wrong error message for interface methods with body #1536.
  • Empty expression block would crash compiler with debug on #1554.
  • Improve infer conversions on constants, e.g. ZString a = foo ? "a" : "b"; #1561
  • Show error when declarations do not start with fn in interfaces. #1565
  • if (try foo) was handled incorrectly inside a defer.
  • &self argument not implicitly null checked. #1556.
  • (uptr)&((Foo*)null).a incorrectly inserts a null check. #1544
  • Incorrect error message when $eval is provided an invalid string. #1570
  • HashMap.copy_keys did not properly copy keys which needed to be allocated #1569
  • Named vector component access would not fold at compile time. #1574
  • $define would occasionally not properly evaluate declarations it encountered.
  • Fixes with error handling recursive @tag #1583.
  • Sometimes generating introspection info would not be in the global scope causing a crash #1586.
  • @tag on macros cannot be retrieved with tagof #1582
  • Taking the $typeof of a wildcard optional returns void!.

Stdlib changes

  • Remove unintended print of char[] as String
  • Add read/write to stream with big endian ints.
  • Move accidently hidden "wrap_bytes".
  • Added CBool #1530.
  • Added encoding/base32 module.

If you want to read more about C3, check out the documentation: https://c3-lang.org or download it and try it out: https://github.com/c3lang/c3c

Welcome to C3 version 0.6.3

Originally from: https://c3.handmade.network/blog/p/8957-welcome_to_c3_version_0.6.3

0.6.3 is out and brings some nice improvements and changes together with bug fixes and standard library additions.

Some highlights:

New named argument syntax

The old initializer list style named arguments (.arg = value) is deprecated and replaced by a arg: value syntax which feels more natural for function invocation. So foo(.some_arg = 23, .other_arg = b) should now be foo(some_arg: 23, other_arg: b). The old syntax will be removed in 0.7.

Slicing of compile time values

C3 now allows slicing of untyped lists and strings. The upcoming 0.6.4 will further support compile time slicing on all compile time arrays, but in 0.6.3 this is still somewhat limited.

Splat everywhere

Ever had an int array and wanted to unpack it into a function call? Now you can do that with ...:

int[2] x = some_call();
foo(...x); // Same as `foo(x[0], x[1]);`

Some limitations apply: when splatting a slice into a normal argument slots the slice length needs to be known at compile time (although it's straightforward to circumvent if needed).

Environment variables for configurability

The environment variable C3C_CC is introduced to allow overriding the default C compiler chosen when compiling C sources as part of the compilation proces, and C3C_LIB allows pointing to a custom default standard library location.

Ad hoc generic type definition changes

Ad hoc generic type definitions, e.g. List(<int>) x; have been deprecated for use outside of type declarations and macros. Only generic types marked @adhoc are now valid for ad hoc declarations, examples of such "adhoc enabled" types is the Maybe type.

This only applies to types. Functions and constants can be all be used ad-hoc style, e.g. foo::some_function(<int>)(123).

$ and # "self" arguments for macro methods

It's now possible to use constant or unevaluated expressions for the "self" parameter in macro methods. This opens up for some useful functionality when using reflection data.

Extended "vendor-fetch"

A project-fetch project function has been added to download missing dependencies for projects into the correct directory, and c3c vendor-fetch can now be used to add and download a new project dependency with a single command.

Wasm improvements

For Wasm, wasm32 / wasm64 targets no longer need to specify use-libc=no as they're assumed to be freestanding by default.

In addition to this, there is now support for Wasm modules: The @wasm attribute now accepts one or two arguments - with two arguments, the first is then assumed to be the module name.

Standard library additions

As usual the standard library got a few additions. In 0.6.3 HashMap has more convenient init functions, the standard random has more variants of random out of the box, cryptographic MD5 and SHA256 hashes were added, as well as HMAC and PBKDF2 support. Datetime has more timezone-related functions and DString received some very useful insert methods.

What's next?

0.6.4 will get complete support for compile time slices, and beyond that hopefully further refinement of the standard library.

For more information about the release, see the full change list:

0.6.3 Change list

Changes / improvements

  • Introduce arg: x named arguments instead of .arg = x, deprecate old style.
  • Support splat for varargs #1352.
  • Allow var in lambdas in macros.
  • Support int[*] { 1, 2, 3 } expressions.
  • Support inline struct designated init as if inline was anonymous.
  • Introduce the .paramsof property.
  • Support environment variable 'C3C_LIB' to find the standard library.
  • Support environment variable 'C3C_CC' to find the default C compiler.
  • Support casting bitstructs to bool.
  • Allow user-defined attributes to have typed parameters.
  • Add .gitkeep files to project subfolders.
  • Add env::COMPILER_BUILD_HASH and env::COMPILER_BUILD_DATE
  • Support linking .o files in compilation command. #1417
  • Slicing constant strings at compile time works.
  • Add project fetch subcommand to fetch missing project dependencies (general and target specific)
  • Ability of vendor-fetch to download the dependencies in the first specified path dependencies-search-path
  • Ability of vendor-fetch to register the fetched dependencies in the project file.
  • Allow the "self" parameter to be $/# for macro methods.
  • Support compile time slicing of untyped lists.
  • Allow specifying an import module using @wasm #1305.
  • Deprecated inline generic types outside of struct definitions and macros unless marked @adhoc.
  • Improved method detection in earlier stages of checking.
  • Allow @norecurse attribute for non-recursive imports #1480.
  • wasm32 / wasm64 targets are use-libc=no by default.

Fixes

  • Issue where a lambda wasn't correctly registered as external. #1408
  • Generic methods were incorrectly registered as functions, leading to naming collisions. #1402
  • Deprecated tuple / triple types.
  • Converting a slice to a vector/array would copy too little data.
  • Crash when reading an empty 'manifest.json'.
  • "optsize" did not work correctly in project.json.
  • l[0].a = 1 now supported for overloads due to better lvalue handling #1357.
  • Asserts are retained regardless of optimization when running tests.
  • Limit object filename lengths. #1415
  • Fix regression for $include.
  • Correct '.so' suffix on dynamic libraries on Linux.
  • Fix bug where inline index access to array in a struct would crash the compiler.
  • Asserts are now correctly included and traced in when running tests.
  • Use atexit to fix finalizers on Windows #1361.
  • Fix bugs in "trap-on-wrap" #1434.
  • Bug with casting anyfault to error.
  • Lambda / function type would accidentally be processed as a method.
  • Fix error message when not finding a particular function.
  • Crash invoking a @body argument with the wrong number of parameters.
  • Fix reordering semantics in struct assignment.
  • Regression when passing types as #expr arguments. #1461
  • Temp allocator overwrites data when doing reset on extra allocated pages. #1462
  • User defined attributes could not have more than 1 parameter due to bug.
  • Folding a constant array of structs at compile time would cause an assert.
  • Enum attributes would be overwritten by enum value attributes.
  • LLVM issue with try when bool is combined #1467.
  • Segfault using ternary with no assignment #1468.
  • Inner types make some errors misleading #1471.
  • Fix bug when passing a type as a compile time value.
  • Fix bug due to enum associated values not being checked for liveness.
  • Regression when compile time accessing a union field not last assigned to.
  • Safer seed of rand() for WASM without libc.
  • Bad error message aliasing an ident with a path. #1481.
  • Error when slicing a struct with an inline array #1488.
  • Improved error messages on Foo a = foo { 1 }; #1496
  • Bug in json decoder escape handling.
  • Fix bug when reading zip manifest, that would not return a zero terminated string. #1490
  • Fix thread tests.
  • Detect recursion errors on non-recursive mutexes in safe mode.
  • Foreach over distinct pointer failed to be caught as error #1506.
  • Foreach over distinct iterable would ignore operator(len).
  • Compiler crash when compiling c code in a library without --obj-out #1503.

Stdlib changes

  • Additional init functions for hashmap.
  • format functions are now functions and work better with splat.
  • Add support for the QOI format.
  • Add io::read_new_fully for reading to the end of a stream.
  • Add io::wrap_bytes for reading bytes with io functions.
  • Add rnd and rand_in_range default random functions.
  • Additional timezone related functions for datetime.
  • Added MD5 and crypto::safe_compare.
  • Added generic HMAC.
  • Added generic PBKDF2 implementation.
  • DString reverse.
  • DString.insert_at now has variants for other types.
  • Added sha256.

If you want to read more about C3, check out the documentation: https://c3-lang.org or download it and try it out: https://github.com/c3lang/c3c

Comments


Comment by Christoffer Lernö

0.6.3 is out and brings some nice improvements and changes together with bug fixes and standard library additions.

Some highlights:

New named argument syntax

The old initializer list style named arguments (.arg = value) is deprecated and replaced by a arg: value syntax which feels more natural for function invocation. So foo(.some_arg = 23, .other_arg = b) should now be foo(some_arg: 23, other_arg: b). The old syntax will be removed in 0.7.

Slicing of compile time values

C3 now allows slicing of untyped lists and strings. The upcoming 0.6.4 will further support compile time slicing on all compile time arrays, but in 0.6.3 this is still somewhat limited.

Splat everywhere

Ever had an int array and wanted to unpack it into a function call? Now you can do that with ...:

int[2] x = some_call();
foo(...x); // Same as `foo(x[0], x[1]);`

Some limitations apply: when splatting a slice into a normal argument slots the slice length needs to be known at compile time (although it's straightforward to circumvent if needed).

Environment variables for configurability

The environment variable C3C_CC is introduced to allow overriding the default C compiler chosen when compiling C sources as part of the compilation proces, and C3C_LIB allows pointing to a custom default standard library location.

Ad hoc generic type definition changes

Ad hoc generic type definitions, e.g. List(<int>) x; have been deprecated for use outside of type declarations and macros. Only generic types marked @adhoc are now valid for ad hoc declarations, examples of such "adhoc enabled" types is the Maybe type.

This only applies to types. Functions and constants can be all be used ad-hoc style, e.g. foo::some_function(<int>)(123).

$ and # "self" arguments for macro methods

It's now possible to use constant or unevaluated expressions for the "self" parameter in macro methods. This opens up for some useful functionality when using reflection data.

Extended "vendor-fetch"

A project-fetch project function has been added to download missing dependencies for projects into the correct directory, and c3c vendor-fetch can now be used to add and download a new project dependency with a single command.

Wasm improvements

For Wasm, wasm32 / wasm64 targets no longer need to specify use-libc=no as they're assumed to be freestanding by default.

In addition to this, there is now support for Wasm modules: The @wasm attribute now accepts one or two arguments - with two arguments, the first is then assumed to be the module name.

Standard library additions

As usual the standard library got a few additions. In 0.6.3 HashMap has more convenient init functions, the standard random has more variants of random out of the box, cryptographic MD5 and SHA256 hashes were added, as well as HMAC and PBKDF2 support. Datetime has more timezone-related functions and DString received some very useful insert methods.

What's next?

0.6.4 will get complete support for compile time slices, and beyond that hopefully further refinement of the standard library.

For more information about the release, see the full change list:

0.6.3 Change list

Changes / improvements

  • Introduce arg: x named arguments instead of .arg = x, deprecate old style.
  • Support splat for varargs #1352.
  • Allow var in lambdas in macros.
  • Support int[*] { 1, 2, 3 } expressions.
  • Support inline struct designated init as if inline was anonymous.
  • Introduce the .paramsof property.
  • Support environment variable 'C3C_LIB' to find the standard library.
  • Support environment variable 'C3C_CC' to find the default C compiler.
  • Support casting bitstructs to bool.
  • Allow user-defined attributes to have typed parameters.
  • Add .gitkeep files to project subfolders.
  • Add env::COMPILER_BUILD_HASH and env::COMPILER_BUILD_DATE
  • Support linking .o files in compilation command. #1417
  • Slicing constant strings at compile time works.
  • Add project fetch subcommand to fetch missing project dependencies (general and target specific)
  • Ability of vendor-fetch to download the dependencies in the first specified path dependencies-search-path
  • Ability of vendor-fetch to register the fetched dependencies in the project file.
  • Allow the "self" parameter to be $/# for macro methods.
  • Support compile time slicing of untyped lists.
  • Allow specifying an import module using @wasm #1305.
  • Deprecated inline generic types outside of struct definitions and macros unless marked @adhoc.
  • Improved method detection in earlier stages of checking.
  • Allow @norecurse attribute for non-recursive imports #1480.
  • wasm32 / wasm64 targets are use-libc=no by default.

Fixes

  • Issue where a lambda wasn't correctly registered as external. #1408
  • Generic methods were incorrectly registered as functions, leading to naming collisions. #1402
  • Deprecated tuple / triple types.
  • Converting a slice to a vector/array would copy too little data.
  • Crash when reading an empty 'manifest.json'.
  • "optsize" did not work correctly in project.json.
  • l[0].a = 1 now supported for overloads due to better lvalue handling #1357.
  • Asserts are retained regardless of optimization when running tests.
  • Limit object filename lengths. #1415
  • Fix regression for $include.
  • Correct '.so' suffix on dynamic libraries on Linux.
  • Fix bug where inline index access to array in a struct would crash the compiler.
  • Asserts are now correctly included and traced in when running tests.
  • Use atexit to fix finalizers on Windows #1361.
  • Fix bugs in "trap-on-wrap" #1434.
  • Bug with casting anyfault to error.
  • Lambda / function type would accidentally be processed as a method.
  • Fix error message when not finding a particular function.
  • Crash invoking a @body argument with the wrong number of parameters.
  • Fix reordering semantics in struct assignment.
  • Regression when passing types as #expr arguments. #1461
  • Temp allocator overwrites data when doing reset on extra allocated pages. #1462
  • User defined attributes could not have more than 1 parameter due to bug.
  • Folding a constant array of structs at compile time would cause an assert.
  • Enum attributes would be overwritten by enum value attributes.
  • LLVM issue with try when bool is combined #1467.
  • Segfault using ternary with no assignment #1468.
  • Inner types make some errors misleading #1471.
  • Fix bug when passing a type as a compile time value.
  • Fix bug due to enum associated values not being checked for liveness.
  • Regression when compile time accessing a union field not last assigned to.
  • Safer seed of rand() for WASM without libc.
  • Bad error message aliasing an ident with a path. #1481.
  • Error when slicing a struct with an inline array #1488.
  • Improved error messages on Foo a = foo { 1 }; #1496
  • Bug in json decoder escape handling.
  • Fix bug when reading zip manifest, that would not return a zero terminated string. #1490
  • Fix thread tests.
  • Detect recursion errors on non-recursive mutexes in safe mode.
  • Foreach over distinct pointer failed to be caught as error #1506.
  • Foreach over distinct iterable would ignore operator(len).
  • Compiler crash when compiling c code in a library without --obj-out #1503.

Stdlib changes

  • Additional init functions for hashmap.
  • format functions are now functions and work better with splat.
  • Add support for the QOI format.
  • Add io::read_new_fully for reading to the end of a stream.
  • Add io::wrap_bytes for reading bytes with io functions.
  • Add rnd and rand_in_range default random functions.
  • Additional timezone related functions for datetime.
  • Added MD5 and crypto::safe_compare.
  • Added generic HMAC.
  • Added generic PBKDF2 implementation.
  • DString reverse.
  • DString.insert_at now has variants for other types.
  • Added sha256.

If you want to read more about C3, check out the documentation: https://c3-lang.org or download it and try it out: https://github.com/c3lang/c3c

A big fat release: C3 0.6.2

Originally from: https://c3.handmade.network/blog/p/8953-a_big_fat_release__c3_0.6.2

The 0.6.2 release is here, and a lot has happened since 0.6.1.

Mainly it's that C3 has seen a lot of new users which have explored the corners of the compiler and also filed request for improvements.

Let's start with the language additions:

&&& and ||| operators

0.5.0 introduced the compile time $and and $or functions. These worked at compile time and would not even type check the right hand side if the left hand side was invalid. So for example $and(false, a < 1.0) would be accepted even if a wasn't in the current scope.

It felt like this feature should be more part of the language proper, so two new operators were added: &&& and ||| to replace $and and $or respectively.

They were chosen for being visually distinct. Other alternatives, such as $& were discarded for not being sufficiently easy to spot.

The +++ operator

Somewhat on trial is the +++ operator. Similar to &&& and ||| it replaces a compile time function – or actually two: $concat and $append. We can for example write "name:" +++ Foo.nameof to get "name:Foo" at compile time.

The operator is slightly controversial and there will be trials with other syntax alternatives, but for now - feel welcome to try it out.

New attributes: @const, @noalias and @tag

A macro with only compile time constructs will be folded at compile time, but until now there's not been a simple way to check it. The @const attribute in 0.6.2 can be added to a macro to give an error if it's not completely compile time folded.

There is also the experimental @noalias attribute to put on parameters. As might be guessed, this indicates to the compiler that the parameter will not alias with any other pointer, similar to C's restrict.

Finally there is the @tag attribute. It allows you to add a key-value pair to any declaration, which later can be checked with .tagof and .has_tagof compile time methods.

This can be used to add arbitrary metadata to types.

Compile time additions

Aside from .tagof and .has_tagof mentioned above, there is .methodsof to get methods associated with a type, and member reflection types has a .get method to generate runtime getters: $member.get(value). And finally is_substruct to check if a struct has an inline member.

RISCV ASM support

Thanks to contributor Chuck Benedict, there is now inline asm for RISCV processors as well!

Language changes

With the addition of new operators, $and, $or, $concat and $append are getting phased out.

$vasplat() also gets a new look: it can now be used directly as $vasplat or ranged using $vasplat[2..3]. This is more consistent with the rest of the language. Similarly $vaarg and others are getting brackets rather than parenthesis: $vaarg[2].

Implicit scalar to vector conversions is also getting a bit less generous. They now only happen with initializers and in binary expressions.

Standard library changes

There has been some added functionality, but mostly tweaks and small additions to existing features.

Fixes

On top of this the 0.6.2 release contains over 50 bug fixes, from standard library bugs to unhandled corner cases in the compiler. This is all thanks to having so many more users.

Where to next?

Language feature wise, 0.6.3 will look at named parameters and allowing more pervasive use of splat (...) in calls.

Other than that, 0.6.3 will try to add more toolchain improvements and better support for other targets than x64 and Aarch64.

Here is the full change list:

Changes / improvements

  • Updated LLVM passes
  • Added is_substruct type property.
  • Scalar -> vector not implicit in call or assign.
  • Added --vector-conv to enable the old scalar->vector conversion behaviour.
  • Added "weak" type aliases def Foo = my_foo::Foo @weak;
  • *-add keys in targets in manifest.json and project.json are deprecated.
  • Made "add" the default for things like sources, dependencies and other keys in project and library files.
  • Give some symbol name suggestions when the path is matched.
  • Don't generate .o files on compile and compile-run if there is no main.
  • c3c init-lib does not create the directory with the .c3l suffix #1253
  • Permit foreach values to be optional.
  • Add --show-backtrace option to disable backtrace for even smaller binary.
  • Untested Xtensa support.
  • && doesn't work correctly with lambdas #1279.
  • Fix incorrect override of optimization levels when using projects.
  • Add experimental @noalias attribute.
  • Add a --run-once option to delete the output file after running it.
  • Add @const attribute for macros, for better error messages with constant macros.
  • Add wincrt setting to libraries.
  • Add +++ &&& ||| as replacement for $concat, $and and $or.
  • Add methodsof to type info for struct, union and bitstruct.
  • Added @tag tagof and has_tagof to user defined types and members.
  • Added c-include-dirs project/manifest setting.
  • The compiler now skips UTF8 BOM.
  • Printable values passed to the Formatter as pointers, will print as if passed by value.
  • Pointers are rendered with "0x" prefix when passed to '%s'.
  • Add temp allocator scribble.
  • Use PIC by default on Linux.
  • $exec may now provide a stdin parameter.
  • Introduce $vaarg[...] syntax and deprecate the old $vaarg(...).
  • Similar change to $vasplat: $vasplat and $vasplat[1..].
  • Add $member.get(value) to replace value.$eval($member.nameof)
  • Improve the error message when the compilation does not produce any files #1390.
  • Add fmod implementation for nolibc.

Fixes

  • Broken WASM library code.
  • Regression: Invalid is_random implementation due to changes in 0.6.
  • dbghelp.lib was linked even on nolibc on Windows.
  • Fix incorrect linker selection on some platforms.
  • Struct members declared in a single line declaration were not sharing attributes. #1266
  • opt project setting now properly documented.
  • Incorrect justify formatting of integers.
  • Assertion with duplicate function pointer signatures #1286
  • Distinct func type would not accept direct function address assign. #1287
  • Distinct inline would not implement protocol if the inlined implemented it. #1292
  • Distinct inline can now be called if it is aliasing a function pointer.
  • Bug in List add_array when reserving memory.
  • Fix issue where a compile time parameter is followed by "...".
  • Fix issue with some conversions to untyped list.
  • Issue where a if (catch e = ...) in a defer would be incorrectly copied. Causing codegen error.
  • Variable in if-try / if-catch cannot be a reused variable name.
  • Vararg interfaces were broken.
  • LLVM codegen for constants in enums could fail.
  • Fixes to the socket functions.
  • Improved output when pointer is out of range.
  • Better error when casting to a distinct fails.
  • With single module, name the .o file after what -o provides. #1306
  • Bitstruct members can now have attributes.
  • % analysis was incorrect for int vectors.
  • When resolving inherited interfaces, the interface type wasn't always resolved.
  • Fix issues when checking methods and interfaces hasn't been resolved yet.
  • Fix Vec2.angle
  • Update to libc::setjmp on Win32, to do no stack unwinding.
  • Recursively follow interfaces when looking up method.
  • Int128 alignment change in LLVM fixed on x64.
  • Fix interface lazy resolution errors.
  • Interface resolution when part of generics #1348.
  • Assert not properly traced #1354.
  • Ordering issues with $include / $exec fixed #1302.
  • Issues with wincrt linking.
  • Debug info with recursive canonical type usage could cause segfault.
  • Missing check on optional left hand side for s.x.
  • Incorrect zero analysis on foo["test"] = {} #1360.
  • Bug converting untyped list #1360.
  • Benchmark / test no longer suppresses debug info. #1364.
  • Bug when compile time subtracting a distinct type.
  • insert_at incorrectly prevented inserts at the end of a list.
  • Fix aligned alloc for Win32 targets.
  • Compiler didn't detect when a module name was used both as a generic and regular module.
  • Assigning a const zero to an aliased distinct caused an error.
  • --path is now properly respected.
  • --test will now provide the full filename and the column.
  • Fix of bug in defer (catch err) with a direct return error.
  • Too restrictive compile time checks for @const.
  • Fixes to wasm nolibc in the standard library.
  • Fixed int128 div/mod.
  • Fix WASM memory init priority.
  • Fix bug with defer (catch err) when used together with regular defer.
  • Methods can now properly be aliased using def #1393.
  • Memory leak in Object when not using temp allocators.
  • Tracking allocator would double the allocations in the report.
  • printf will now show errors in the output when there are errors.
  • Bug where if try would work incorrectly in a macro.
  • Prevent loading / storing large structs with LLVM.

Stdlib changes

  • send and recv added to libc for Posix / Win32.
  • Add support to destroy temp allocators.
  • Deprecated path.append, path.tappend, getcwd, tgetcwd, path.absolute, ls.
  • Deprecated env::get_config_dir, replaced by env::new_get_config_dir.
  • Added path.has_extension, path.new_append, path.temp_append, new_cwd, temp_cwd, path.new_absolute, new_ls, temp_ls.
  • Added dstring.replace
  • New hashmap type, Map
  • Added ElasticArray.
  • Added types::is_signed, types::is_unsigned and types::inner_type.

If you want to read more about C3, check out the documentation: https://c3-lang.org or download it and try it out: https://github.com/c3lang/c3c

Comments


Comment by Christoffer Lernö

The 0.6.2 release is here, and a lot has happened since 0.6.1.

Mainly it's that C3 has seen a lot of new users which have explored the corners of the compiler and also filed request for improvements.

Let's start with the language additions:

&&& and ||| operators

0.5.0 introduced the compile time $and and $or functions. These worked at compile time and would not even type check the right hand side if the left hand side was invalid. So for example $and(false, a < 1.0) would be accepted even if a wasn't in the current scope.

It felt like this feature should be more part of the language proper, so two new operators were added: &&& and ||| to replace $and and $or respectively.

They were chosen for being visually distinct. Other alternatives, such as $& were discarded for not being sufficiently easy to spot.

The +++ operator

Somewhat on trial is the +++ operator. Similar to &&& and ||| it replaces a compile time function – or actually two: $concat and $append. We can for example write "name:" +++ Foo.nameof to get "name:Foo" at compile time.

The operator is slightly controversial and there will be trials with other syntax alternatives, but for now - feel welcome to try it out.

New attributes: @const, @noalias and @tag

A macro with only compile time constructs will be folded at compile time, but until now there's not been a simple way to check it. The @const attribute in 0.6.2 can be added to a macro to give an error if it's not completely compile time folded.

There is also the experimental @noalias attribute to put on parameters. As might be guessed, this indicates to the compiler that the parameter will not alias with any other pointer, similar to C's restrict.

Finally there is the @tag attribute. It allows you to add a key-value pair to any declaration, which later can be checked with .tagof and .has_tagof compile time methods.

This can be used to add arbitrary metadata to types.

Compile time additions

Aside from .tagof and .has_tagof mentioned above, there is .methodsof to get methods associated with a type, and member reflection types has a .get method to generate runtime getters: $member.get(value). And finally is_substruct to check if a struct has an inline member.

RISCV ASM support

Thanks to contributor Chuck Benedict, there is now inline asm for RISCV processors as well!

Language changes

With the addition of new operators, $and, $or, $concat and $append are getting phased out.

$vasplat() also gets a new look: it can now be used directly as $vasplat or ranged using $vasplat[2..3]. This is more consistent with the rest of the language. Similarly $vaarg and others are getting brackets rather than parenthesis: $vaarg[2].

Implicit scalar to vector conversions is also getting a bit less generous. They now only happen with initializers and in binary expressions.

Standard library changes

There has been some added functionality, but mostly tweaks and small additions to existing features.

Fixes

On top of this the 0.6.2 release contains over 50 bug fixes, from standard library bugs to unhandled corner cases in the compiler. This is all thanks to having so many more users.

Where to next?

Language feature wise, 0.6.3 will look at named parameters and allowing more pervasive use of splat (...) in calls.

Other than that, 0.6.3 will try to add more toolchain improvements and better support for other targets than x64 and Aarch64.

Here is the full change list:

Changes / improvements

  • Updated LLVM passes
  • Added is_substruct type property.
  • Scalar -> vector not implicit in call or assign.
  • Added --vector-conv to enable the old scalar->vector conversion behaviour.
  • Added "weak" type aliases def Foo = my_foo::Foo @weak;
  • *-add keys in targets in manifest.json and project.json are deprecated.
  • Made "add" the default for things like sources, dependencies and other keys in project and library files.
  • Give some symbol name suggestions when the path is matched.
  • Don't generate .o files on compile and compile-run if there is no main.
  • c3c init-lib does not create the directory with the .c3l suffix #1253
  • Permit foreach values to be optional.
  • Add --show-backtrace option to disable backtrace for even smaller binary.
  • Untested Xtensa support.
  • && doesn't work correctly with lambdas #1279.
  • Fix incorrect override of optimization levels when using projects.
  • Add experimental @noalias attribute.
  • Add a --run-once option to delete the output file after running it.
  • Add @const attribute for macros, for better error messages with constant macros.
  • Add wincrt setting to libraries.
  • Add +++ &&& ||| as replacement for $concat, $and and $or.
  • Add methodsof to type info for struct, union and bitstruct.
  • Added @tag tagof and has_tagof to user defined types and members.
  • Added c-include-dirs project/manifest setting.
  • The compiler now skips UTF8 BOM.
  • Printable values passed to the Formatter as pointers, will print as if passed by value.
  • Pointers are rendered with "0x" prefix when passed to '%s'.
  • Add temp allocator scribble.
  • Use PIC by default on Linux.
  • $exec may now provide a stdin parameter.
  • Introduce $vaarg[...] syntax and deprecate the old $vaarg(...).
  • Similar change to $vasplat: $vasplat and $vasplat[1..].
  • Add $member.get(value) to replace value.$eval($member.nameof)
  • Improve the error message when the compilation does not produce any files #1390.
  • Add fmod implementation for nolibc.

Fixes

  • Broken WASM library code.
  • Regression: Invalid is_random implementation due to changes in 0.6.
  • dbghelp.lib was linked even on nolibc on Windows.
  • Fix incorrect linker selection on some platforms.
  • Struct members declared in a single line declaration were not sharing attributes. #1266
  • opt project setting now properly documented.
  • Incorrect justify formatting of integers.
  • Assertion with duplicate function pointer signatures #1286
  • Distinct func type would not accept direct function address assign. #1287
  • Distinct inline would not implement protocol if the inlined implemented it. #1292
  • Distinct inline can now be called if it is aliasing a function pointer.
  • Bug in List add_array when reserving memory.
  • Fix issue where a compile time parameter is followed by "...".
  • Fix issue with some conversions to untyped list.
  • Issue where a if (catch e = ...) in a defer would be incorrectly copied. Causing codegen error.
  • Variable in if-try / if-catch cannot be a reused variable name.
  • Vararg interfaces were broken.
  • LLVM codegen for constants in enums could fail.
  • Fixes to the socket functions.
  • Improved output when pointer is out of range.
  • Better error when casting to a distinct fails.
  • With single module, name the .o file after what -o provides. #1306
  • Bitstruct members can now have attributes.
  • % analysis was incorrect for int vectors.
  • When resolving inherited interfaces, the interface type wasn't always resolved.
  • Fix issues when checking methods and interfaces hasn't been resolved yet.
  • Fix Vec2.angle
  • Update to libc::setjmp on Win32, to do no stack unwinding.
  • Recursively follow interfaces when looking up method.
  • Int128 alignment change in LLVM fixed on x64.
  • Fix interface lazy resolution errors.
  • Interface resolution when part of generics #1348.
  • Assert not properly traced #1354.
  • Ordering issues with $include / $exec fixed #1302.
  • Issues with wincrt linking.
  • Debug info with recursive canonical type usage could cause segfault.
  • Missing check on optional left hand side for s.x.
  • Incorrect zero analysis on foo["test"] = {} #1360.
  • Bug converting untyped list #1360.
  • Benchmark / test no longer suppresses debug info. #1364.
  • Bug when compile time subtracting a distinct type.
  • insert_at incorrectly prevented inserts at the end of a list.
  • Fix aligned alloc for Win32 targets.
  • Compiler didn't detect when a module name was used both as a generic and regular module.
  • Assigning a const zero to an aliased distinct caused an error.
  • --path is now properly respected.
  • --test will now provide the full filename and the column.
  • Fix of bug in defer (catch err) with a direct return error.
  • Too restrictive compile time checks for @const.
  • Fixes to wasm nolibc in the standard library.
  • Fixed int128 div/mod.
  • Fix WASM memory init priority.
  • Fix bug with defer (catch err) when used together with regular defer.
  • Methods can now properly be aliased using def #1393.
  • Memory leak in Object when not using temp allocators.
  • Tracking allocator would double the allocations in the report.
  • printf will now show errors in the output when there are errors.
  • Bug where if try would work incorrectly in a macro.
  • Prevent loading / storing large structs with LLVM.

Stdlib changes

  • send and recv added to libc for Posix / Win32.
  • Add support to destroy temp allocators.
  • Deprecated path.append, path.tappend, getcwd, tgetcwd, path.absolute, ls.
  • Deprecated env::get_config_dir, replaced by env::new_get_config_dir.
  • Added path.has_extension, path.new_append, path.temp_append, new_cwd, temp_cwd, path.new_absolute, new_ls, temp_ls.
  • Added dstring.replace
  • New hashmap type, Map
  • Added ElasticArray.
  • Added types::is_signed, types::is_unsigned and types::inner_type.

If you want to read more about C3, check out the documentation: https://c3-lang.org or download it and try it out: https://github.com/c3lang/c3c


Comment by Christoffer Lernö

No, C3 just supports asm and LLVM IR output on top of the normal binary output.

Fixes and improvements: C3 0.6.1 is here

Originally from: https://c3.handmade.network/blog/p/8934-fixes_and_improvements__c3_0.6.1_is_here

It's time to release 0.6.1. There's been a bit longer to the x.x.1 than for 0.5.0 -> 0.5.1, and as a result there are quite a bit of additions.

Additions first: $append (appending to a compile time array) and $concat (combining two compile time arrays) were added as well as some compile time string macros: @str_hash(...) (hash a string at compile time), @str_upper / @str_lower (convert a compile time string to upper / lower case) and @str_find (find a substring in a string at compile time).

An important addition is @unaligned_load and @unaliged_store for loading/storing data to unaligned pointers.

In general there has been some relaxation constraints for some features. Trailing bodies, for example, may now use all type of macro argument kinds, like type arguments, and compile time folding of constant structs and arrays now work in all cases.

The standard library hasn't seen many changes since 0.6.0, but there have been some small enhancements.

The full changelist:

Changes / improvements

  • Addition of $append and $concat functions.
  • Added $$str_hash, $$str_upper, $$str_lower, $$str_find builtins.
  • Improved error notes when call expressions have errors.
  • Trailing body arguments may now be &ref, #hash, $const and $Type arguments.
  • "panic-msg" setting to suppress panic message output.
  • Require @export functions to have @export types.
  • Disallow leading/trailing/duplicate '_' in module names.
  • Updated mangling.
  • Added $$unaligned_load and $$unaligned_store.
  • --no-headers option to suppress creating headers when generating a library.
  • Support c-file compilation in libraries.
  • Allow using $defined(&a[1]) to check if the operation is supported.
  • Max number of members in a struct is limited to 65535.
  • The maximum number of parameters in a call is now 255, up from 127.
  • Array comparison now uses built-in memcmp on LLVM to enable optimizations.
  • Prevent implicit array casts to pointers with higher alignment #1237.
  • Macro $case statements now pick the first match and does not evaluate the rest.
  • manifest.json is now checked for incorrect keys.
  • Added --list-manifest-properties to list the available properties in manifest.json.
  • Indexing into a constant array / struct now works at compile time.
  • Improved error message when trying user foreach with an untyped list.

Fixes

  • Error with unsigned compare in @ensure when early returning 0 #1207.
  • Prevent Mach-O from removing @init and @dynamic in a more reliable way #1200.
  • Fix of missing copy of parameterized custom attributes.
  • Fixed crash on certain recursive function definitions #1209.
  • Return the typekind "FUNC" for a function pointer.
  • No longer possible to dereference a function pointer.
  • Fix bug with @jump miscompile.
  • Bit negate does implicit integer promotion.
  • Bitstructs, unions and flexible arrays now correctly emitted in headers.
  • Fix distinct inline conversions.
  • Bit negating const zero flags would give an incorrect result.
  • Fix to scalar -> vector conversions.
  • Bug fix for rethrow + defer catch.
  • Wrong size for structs containing overaligned structs #1219
  • $typeof(*x) should be valid when x is an [out] parameter #1226
  • Fix ABI lowering for 128 bit vectors on Linux.
  • Bad error message when using a generic method without generic parameters #1228
  • Private function called from nested macro not visible to linker #1232
  • Bitstructs in structs would not be correctly be handled in some cases.
  • Fix problem where a $$FUNC would return "" when evaluated for a static in a function #1236.
  • ordinal is no longer a valid associated value name for enums.
  • Constants defined by indexing into another constant could fail codegen.
  • Stdlib nolibc code bugs fixed.
  • Regression: duplicate symbols with static variable declared in macro #1248.
  • Unsplat with named parameters was accidentally disallowed.
  • Reference parameter doesn't work with vector subscript #1250.
  • The msvc_sdk script failed to work properly on windows when run in folders with spaces.

Stdlib changes

  • Added remove_first_item remove_last_item and remove_item as aliases for the match functions.
  • Added @str_hash, @str_upper, @str_lower, @str_find compile time macros.
  • Remove "panic" text from unreachable() when safe mode is turned off.
  • Added @unaligned_store and @unaligned_load.
  • Null ZString, DString or pointer prints "(null)" for printf.
  • Updated sorting API.
  • Insertion sort and counting sort added.
  • Added missing mem and mem::allocator functions for aligned allocations.
  • Added new_init_with_array and temp_init_with_array for List.
  • Fixed posix NativeMutex.lock_timeout.
  • Fixed env::ARCH_32_BIT and env::ARCH_64_BIT.
  • Added time::us.

Comments


Comment by Christoffer Lernö

It's time to release 0.6.1. There's been a bit longer to the x.x.1 than for 0.5.0 -> 0.5.1, and as a result there are quite a bit of additions.

Additions first: $append (appending to a compile time array) and $concat (combining two compile time arrays) were added as well as some compile time string macros: @str_hash(...) (hash a string at compile time), @str_upper / @str_lower (convert a compile time string to upper / lower case) and @str_find (find a substring in a string at compile time).

An important addition is @unaligned_load and @unaliged_store for loading/storing data to unaligned pointers.

In general there has been some relaxation constraints for some features. Trailing bodies, for example, may now use all type of macro argument kinds, like type arguments, and compile time folding of constant structs and arrays now work in all cases.

The standard library hasn't seen many changes since 0.6.0, but there have been some small enhancements.

The full changelist:

Changes / improvements

  • Addition of $append and $concat functions.
  • Added $$str_hash, $$str_upper, $$str_lower, $$str_find builtins.
  • Improved error notes when call expressions have errors.
  • Trailing body arguments may now be &ref, #hash, $const and $Type arguments.
  • "panic-msg" setting to suppress panic message output.
  • Require @export functions to have @export types.
  • Disallow leading/trailing/duplicate '_' in module names.
  • Updated mangling.
  • Added $$unaligned_load and $$unaligned_store.
  • --no-headers option to suppress creating headers when generating a library.
  • Support c-file compilation in libraries.
  • Allow using $defined(&a[1]) to check if the operation is supported.
  • Max number of members in a struct is limited to 65535.
  • The maximum number of parameters in a call is now 255, up from 127.
  • Array comparison now uses built-in memcmp on LLVM to enable optimizations.
  • Prevent implicit array casts to pointers with higher alignment #1237.
  • Macro $case statements now pick the first match and does not evaluate the rest.
  • manifest.json is now checked for incorrect keys.
  • Added --list-manifest-properties to list the available properties in manifest.json.
  • Indexing into a constant array / struct now works at compile time.
  • Improved error message when trying user foreach with an untyped list.

Fixes

  • Error with unsigned compare in @ensure when early returning 0 #1207.
  • Prevent Mach-O from removing @init and @dynamic in a more reliable way #1200.
  • Fix of missing copy of parameterized custom attributes.
  • Fixed crash on certain recursive function definitions #1209.
  • Return the typekind "FUNC" for a function pointer.
  • No longer possible to dereference a function pointer.
  • Fix bug with @jump miscompile.
  • Bit negate does implicit integer promotion.
  • Bitstructs, unions and flexible arrays now correctly emitted in headers.
  • Fix distinct inline conversions.
  • Bit negating const zero flags would give an incorrect result.
  • Fix to scalar -> vector conversions.
  • Bug fix for rethrow + defer catch.
  • Wrong size for structs containing overaligned structs #1219
  • $typeof(*x) should be valid when x is an [out] parameter #1226
  • Fix ABI lowering for 128 bit vectors on Linux.
  • Bad error message when using a generic method without generic parameters #1228
  • Private function called from nested macro not visible to linker #1232
  • Bitstructs in structs would not be correctly be handled in some cases.
  • Fix problem where a $$FUNC would return "" when evaluated for a static in a function #1236.
  • ordinal is no longer a valid associated value name for enums.
  • Constants defined by indexing into another constant could fail codegen.
  • Stdlib nolibc code bugs fixed.
  • Regression: duplicate symbols with static variable declared in macro #1248.
  • Unsplat with named parameters was accidentally disallowed.
  • Reference parameter doesn't work with vector subscript #1250.
  • The msvc_sdk script failed to work properly on windows when run in folders with spaces.

Stdlib changes

  • Added remove_first_item remove_last_item and remove_item as aliases for the match functions.
  • Added @str_hash, @str_upper, @str_lower, @str_find compile time macros.
  • Remove "panic" text from unreachable() when safe mode is turned off.
  • Added @unaligned_store and @unaligned_load.
  • Null ZString, DString or pointer prints "(null)" for printf.
  • Updated sorting API.
  • Insertion sort and counting sort added.
  • Added missing mem and mem::allocator functions for aligned allocations.
  • Added new_init_with_array and temp_init_with_array for List.
  • Fixed posix NativeMutex.lock_timeout.
  • Fixed env::ARCH_32_BIT and env::ARCH_64_BIT.
  • Added time::us.

C3 reaches the 0.6 milestone!

Originally from: https://c3.handmade.network/blog/p/8918-c3_reaches_the_0.6_milestone%2521

It's been three months and finally version 0.6.0 is here!

C3 was on a monthly update cycle, so what happened to version 0.5.6? Actually the main branch contained a releasable 0.5.6 for the last two months, but I didn't release it. A release takes a bit of time updating everything and making announcements and so on, and I felt that maybe I should just release version 0.6.0 instead – rather than to push the relatively small 0.5.6.

But on the other hand I kept delaying 0.6.0, because I was looking for more breaking changes to add (recall that 0.5.x cannot introduce any breaking changes for 0.5 code, so 0.6.0 is where the breaking changes happen)

However, now 0.6.0 is stable enough, and there has lately been very little to backport into the 0.5.x branch. So it's time to do the 0.6.0 release.

Let's go through what happened since 0.5.5 first:

Updated enum syntax

Using enums with associated values now look more like defining C enums than Java enums:

// Old
enum Foo : int (int value, double value2)
{
    A(1, 2.0),
    B(2, 4.0)
}
enum Bar : int (int value)
{
    C(7),
    D(9)
}

// New
enum Foo : int (int value, double value2)
{
    A = { 1, 2.0 },
    B = { 2, 4.0 }
}
enum Bar : int (int value)
{
    C = 7, // Also possible to write as C = { 7 }
    D = 9
}

The new syntax also allows the enum size to be elided when there are associated values:

enum Foo : (int value, double value2)
{
    A = { 1, 2.0 },
    B = { 2, 4.0 }
}

There were other ideas around the enums, allowing them to be used more like unordered C enums. However, the experimental attempts showed little promise. Maybe this will be revisited for version 0.7.

Changes to any / interfaces

Two things changed, the most important thing is that the 0.5 change where any was handled written as a pointer (any*) was rolled back. This was a trade-off: in some cases using any* felt like it better expressed what was going on, but in other cases it ended up being confusing.

Secondly @default implementations for interfaces were removed. The need for these were greatly reduced when the stdlib moved from "call the method on the type" to "call a function with the interface" style (see std::io for examples).

Guaranteed jump tables

For switches it's now possible to get guaranteed calculated goto jump tables by using the @jump attribute:

switch (a) @jump
{
    case 1: res(1);
    case 3: res(3);
}

The above code will lower to an array of jump destinations which is indexed into and then jumped to.

RGBA swizzling

In addition to swizzling using xyzw it's now possible to use rgba as well:

int[<4>] abc = { 1, 2, 3, 4 };
int[<3>] z = abc.xwz; // Grabs index 1, 4 and 3
int[<3>] c = abc.rab; // Same as above

More distinct types

It's now possible to make distinct types out of void, typeid, anyfault and fault types.

Catch an error in defer

It's now possible to get the error thrown when using defer catch statements:

defer catch io::printn("Exit due to some error"); 
defer (catch err) io::printfn("Exit due to: %s", err); // New

Stricter semantics for if try

It's no longer possible to do:

if (try foo() + 1) { ... }

The semantics of this code was a bit confusing, so in 0.6.0 you may no longer do "if try" binary or unary expressions. You may however still test expressions like if (try (foo() + 1)).

Changes in command-line arguments

  • Added --output-dir to set the output directory.
  • Added --print-input to print all files used for compilation.
  • Removed --system-linker and replaced it by --linker which also allows setting custom linkers.

Stricter @if evaluation

Evaluating @if on the top level is always subject to ordering issues. For this reason 0.6 does not permit conditional definition guarded by @if to depend on something that in itself depended on @if.

int foo;
// This would succeed on 0.5.x, but changing the
// ordering, placing `B` before `a` would be a compile error.
const B @if($defined(foo)) = 1;
const B @if(!$defined(foo)) = 2;
int a @if(B == 1);

In 0.6.0 the above code is an error, as "a" depends on the conditional "B".

This change helps preventing the user from accidentally building code that depends on top level ordering.

assert(false) disallowed

Aside from (compile time known) unused branches and tests, asserts that are compile time evaluated to be false are now compile time errors. This allows asserts to detect more broken code at compile time.

const A = 1;
if (A == 0)
{
  assert(false); // Ok, dead branch.
}
else
{
  assert(false); // This is a compile time error
}
assert(A == 0); // Also a compile time error.

More permissive function definitions

Functions definitions may now be recursive (e.g. a function type taking as argument a pointer to itself).

Better errors for inlined macros

The code now provides a backtrace to where the macro was first inlined when detecting an error.

Improved debug information

The debug information has gotten an overhaul, in particular debug information for macros are much improved.

Changes to the stdlib

Various fixes, but perhaps most importantly list types now consistently use push rather than add, and pop now always return an Optional result.

The full change list 0.5.5 -> 0.6.0

Changes / improvements

  • @default implementations for interfaces removed.
  • any* => any, same for interfaces.
  • Private / local globals now have internal visibility in LLVM.
  • Updated enum syntax.
  • 'rgba' also available for swizzling.
  • The name "subarray" has been replaced by the more well known name "slice' across the codebase.
  • Improved alignment handling.
  • Add --output-dir to command line. #1155
  • Allow making distinct types out of "void", "typeid", "anyfault" and faults.
  • Removed --system-linker setting.
  • "Try" expressions may not be any binary or unary expressions. So for example try foo() + 1 is disallowed.
  • Added $$REGISTER_SIZE for int register size.
  • assert(false) only allowed in unused branches or in tests. Compile time failed asserts is a compile time error.
  • Require expression blocks returning values to have the value used.
  • Detect "unsigned >= 0" as errors.
  • Improve callstack debug information #1184.
  • Request jump table using @jump for switches.
  • Improved inline debug information.
  • Improved error messages on inlined macros.
  • Introduce MSVC compatible SIMD ABI.
  • $foreach doesn't create an implicit syntactic scope.
  • Error of @if depends on @if
  • Support defer (catch err)
  • Added print-input command argument to print all files used for compilation
  • Allow recursive function definitions as long as they are pointers. #1182
  • Default CPU to native if less than AVX, otherwise use AVX.
  • Bounds checking on length for foo[1:2] slicing #1191.
  • Foreach uses non-wrapping add/dec.

Fixes

  • Fixed issue in safe mode when converting enums.
  • Better checking of operator methods.
  • Bug when assigning an optional from an optional.
  • Lambdas were not type checked thoroughly #1185.
  • Fix problems using reflection on interface types #1203.
  • @param with unnamed macro varargs could crash the compiler.
  • Compiler crash using enum nameof from different module #1205.
  • Incorrect length passed to scratch buffer printf.
  • Casting to a bitstruct would be allowed even if the type was the wrong size.
  • Generic modules parameterized with constants would sometimes get the wrong parameterized module name causing conversion errors #1192.
  • Duplicate emit of expressions on negation would incorrectly compile negated macros.
  • Casting a slice address to its pointer type should not compile #1193.
  • Union is not properly zero-initialized with designated initializer #1194.
  • Compile time fmod evaluates to 0 #1195.
  • Assertion failed when casting (unsigned) argument to enum #1196
  • Correct debug info on parameters without storage.
  • Fix location on foreach debug output.
  • Compiler crash on designated initializer for structs with bitstruct.

Stdlib changes

  • "init_new/init_temp" removed.
  • LinkedList API rewritten.
  • List "pop" and "remove" function now return Optionals.
  • RingBuffer API rewritten. Allocator interface changed.
  • Deprecated Allocator, DString and mem functions removed.
  • "identity" functions are now constants for Matrix and Complex numbers.
  • Removed 'append' from Object and List, replaced by 'push'.
  • GenericList renamed AnyList.
  • Proper handling of '.' and Win32 '//server' paths.
  • Path normalization - fix possible null terminator out of bounds.
  • Add 'zstr' variants for string::new_format / string::tformat.
  • Fix mutex and wait signatures for Win32.

0.6 has feature stability guarantees, code written for 0.6.0 will work with all of 0.6.x.

If you want to read more about C3, check out the documentation: https://c3-lang.org or download it and try it out: https://github.com/c3lang/c3c

Comments


Comment by Christoffer Lernö

It's been three months and finally version 0.6.0 is here!

C3 was on a monthly update cycle, so what happened to version 0.5.6? Actually the main branch contained a releasable 0.5.6 for the last two months, but I didn't release it. A release takes a bit of time updating everything and making announcements and so on, and I felt that maybe I should just release version 0.6.0 instead – rather than to push the relatively small 0.5.6.

But on the other hand I kept delaying 0.6.0, because I was looking for more breaking changes to add (recall that 0.5.x cannot introduce any breaking changes for 0.5 code, so 0.6.0 is where the breaking changes happen)

However, now 0.6.0 is stable enough, and there has lately been very little to backport into the 0.5.x branch. So it's time to do the 0.6.0 release.

Let's go through what happened since 0.5.5 first:

Updated enum syntax

Using enums with associated values now look more like defining C enums than Java enums:

// Old
enum Foo : int (int value, double value2)
{
    A(1, 2.0),
    B(2, 4.0)
}
enum Bar : int (int value)
{
    C(7),
    D(9)
}

// New
enum Foo : int (int value, double value2)
{
    A = { 1, 2.0 },
    B = { 2, 4.0 }
}
enum Bar : int (int value)
{
    C = 7, // Also possible to write as C = { 7 }
    D = 9
}

The new syntax also allows the enum size to be elided when there are associated values:

enum Foo : (int value, double value2)
{
    A = { 1, 2.0 },
    B = { 2, 4.0 }
}

There were other ideas around the enums, allowing them to be used more like unordered C enums. However, the experimental attempts showed little promise. Maybe this will be revisited for version 0.7.

Changes to any / interfaces

Two things changed, the most important thing is that the 0.5 change where any was handled written as a pointer (any*) was rolled back. This was a trade-off: in some cases using any* felt like it better expressed what was going on, but in other cases it ended up being confusing.

Secondly @default implementations for interfaces were removed. The need for these were greatly reduced when the stdlib moved from "call the method on the type" to "call a function with the interface" style (see std::io for examples).

Guaranteed jump tables

For switches it's now possible to get guaranteed calculated goto jump tables by using the @jump attribute:

switch (a) @jump
{
    case 1: res(1);
    case 3: res(3);
}

The above code will lower to an array of jump destinations which is indexed into and then jumped to.

RGBA swizzling

In addition to swizzling using xyzw it's now possible to use rgba as well:

int[<4>] abc = { 1, 2, 3, 4 };
int[<3>] z = abc.xwz; // Grabs index 1, 4 and 3
int[<3>] c = abc.rab; // Same as above

More distinct types

It's now possible to make distinct types out of void, typeid, anyfault and fault types.

Catch an error in defer

It's now possible to get the error thrown when using defer catch statements:

defer catch io::printn("Exit due to some error"); 
defer (catch err) io::printfn("Exit due to: %s", err); // New

Stricter semantics for if try

It's no longer possible to do:

if (try foo() + 1) { ... }

The semantics of this code was a bit confusing, so in 0.6.0 you may no longer do "if try" binary or unary expressions. You may however still test expressions like if (try (foo() + 1)).

Changes in command-line arguments

  • Added --output-dir to set the output directory.
  • Added --print-input to print all files used for compilation.
  • Removed --system-linker and replaced it by --linker which also allows setting custom linkers.

Stricter @if evaluation

Evaluating @if on the top level is always subject to ordering issues. For this reason 0.6 does not permit conditional definition guarded by @if to depend on something that in itself depended on @if.

int foo;
// This would succeed on 0.5.x, but changing the
// ordering, placing `B` before `a` would be a compile error.
const B @if($defined(foo)) = 1;
const B @if(!$defined(foo)) = 2;
int a @if(B == 1);

In 0.6.0 the above code is an error, as "a" depends on the conditional "B".

This change helps preventing the user from accidentally building code that depends on top level ordering.

assert(false) disallowed

Aside from (compile time known) unused branches and tests, asserts that are compile time evaluated to be false are now compile time errors. This allows asserts to detect more broken code at compile time.

const A = 1;
if (A == 0)
{
  assert(false); // Ok, dead branch.
}
else
{
  assert(false); // This is a compile time error
}
assert(A == 0); // Also a compile time error.

More permissive function definitions

Functions definitions may now be recursive (e.g. a function type taking as argument a pointer to itself).

Better errors for inlined macros

The code now provides a backtrace to where the macro was first inlined when detecting an error.

Improved debug information

The debug information has gotten an overhaul, in particular debug information for macros are much improved.

Changes to the stdlib

Various fixes, but perhaps most importantly list types now consistently use push rather than add, and pop now always return an Optional result.

The full change list 0.5.5 -> 0.6.0

Changes / improvements

  • @default implementations for interfaces removed.
  • any* => any, same for interfaces.
  • Private / local globals now have internal visibility in LLVM.
  • Updated enum syntax.
  • 'rgba' also available for swizzling.
  • The name "subarray" has been replaced by the more well known name "slice' across the codebase.
  • Improved alignment handling.
  • Add --output-dir to command line. #1155
  • Allow making distinct types out of "void", "typeid", "anyfault" and faults.
  • Removed --system-linker setting.
  • "Try" expressions may not be any binary or unary expressions. So for example try foo() + 1 is disallowed.
  • Added $$REGISTER_SIZE for int register size.
  • assert(false) only allowed in unused branches or in tests. Compile time failed asserts is a compile time error.
  • Require expression blocks returning values to have the value used.
  • Detect "unsigned >= 0" as errors.
  • Improve callstack debug information #1184.
  • Request jump table using @jump for switches.
  • Improved inline debug information.
  • Improved error messages on inlined macros.
  • Introduce MSVC compatible SIMD ABI.
  • $foreach doesn't create an implicit syntactic scope.
  • Error of @if depends on @if
  • Support defer (catch err)
  • Added print-input command argument to print all files used for compilation
  • Allow recursive function definitions as long as they are pointers. #1182
  • Default CPU to native if less than AVX, otherwise use AVX.
  • Bounds checking on length for foo[1:2] slicing #1191.
  • Foreach uses non-wrapping add/dec.

Fixes

  • Fixed issue in safe mode when converting enums.
  • Better checking of operator methods.
  • Bug when assigning an optional from an optional.
  • Lambdas were not type checked thoroughly #1185.
  • Fix problems using reflection on interface types #1203.
  • @param with unnamed macro varargs could crash the compiler.
  • Compiler crash using enum nameof from different module #1205.
  • Incorrect length passed to scratch buffer printf.
  • Casting to a bitstruct would be allowed even if the type was the wrong size.
  • Generic modules parameterized with constants would sometimes get the wrong parameterized module name causing conversion errors #1192.
  • Duplicate emit of expressions on negation would incorrectly compile negated macros.
  • Casting a slice address to its pointer type should not compile #1193.
  • Union is not properly zero-initialized with designated initializer #1194.
  • Compile time fmod evaluates to 0 #1195.
  • Assertion failed when casting (unsigned) argument to enum #1196
  • Correct debug info on parameters without storage.
  • Fix location on foreach debug output.
  • Compiler crash on designated initializer for structs with bitstruct.

Stdlib changes

  • "init_new/init_temp" removed.
  • LinkedList API rewritten.
  • List "pop" and "remove" function now return Optionals.
  • RingBuffer API rewritten. Allocator interface changed.
  • Deprecated Allocator, DString and mem functions removed.
  • "identity" functions are now constants for Matrix and Complex numbers.
  • Removed 'append' from Object and List, replaced by 'push'.
  • GenericList renamed AnyList.
  • Proper handling of '.' and Win32 '//server' paths.
  • Path normalization - fix possible null terminator out of bounds.
  • Add 'zstr' variants for string::new_format / string::tformat.
  • Fix mutex and wait signatures for Win32.

0.6 has feature stability guarantees, code written for 0.6.0 will work with all of 0.6.x.

If you want to read more about C3, check out the documentation: https://c3-lang.org or download it and try it out: https://github.com/c3lang/c3c