Skip to content

Core 0.4.2

Latest

Choose a tag to compare

@zweiler1 zweiler1 released this 28 Sep 19:03
· 12 commits to main since this release
02377ac

TLDR

  • Upgrade from LLVM 21.1.8 to 22.1.8
  • Published Flint packages on Ubuntu: https://launchpad.net/~zweiler1/+archive/ubuntu/flint/+packages
  • [Breaking] Redesigned initializers (Wiki)
  • [Breaking] Implemented the basic version of the Comptime Parameter List, adding support for generics as a result (Wiki)
  • Added support for export keyword to make Flint callable from C when using the --lib <name> flag (Wiki)
  • Added support for cross-compilation, the --target <target> compiler flag now works properly
  • Added #fip_disable annotation for extern functions (Wiki)
  • [Breaking] Renamed annotation #test_output_always to #test_output_show_on_success
  • [Breaking] Renamed annotation #test_output_never to #test_output_silent_on_failure
  • Added new testing annotations (#test_pre, #test_post, #test_post_always, #test_init, #test_deinit, #test_entry) (Wiki)
  • Added proper stack traces for runtime errors
  • Completely overhauled debug flags (debug builds are now silent by default)
  • Added the generic array Core module (Wiki)
  • [Breaking] Changed abs overloads in Core.math to return unsigned values (Wiki)
  • Many bugfixes, as usual

Breaking Changes

  • Changed the abs function overloads of the Core.math module to return unsigned values, e.g. abs(i32 value) -> u32 for example
  • Two annotations were pretty misleading, they were renamed to make it clearer what they do. Their functionality or behaviour did not change, only their names did:
    • #test_output_always -> #test_output_show_on_success
    • #test_output_never -> #test_output_silent_on_failure

Initializer Redesign

Initializers were redesigned entirely. They used the T(...) syntax until now, this syntax changed to T{...} for various reasons. In the old system, you needed to define the order of initialization when defining types (like data and objects) manually:

// Old
data SomeData:
    i32 x;
    i32 y;
    SomeData(x, y);
    
// New
data SomeData:
    i32 x;
    i32 y;

meaning that you always wrote the type and the field names twice for every single definition. Then, to intantiate a value of type SomeData you "called" the "Constructor":

d := SomeData(10, 20);

the problem is that a "Constructor" looks a lot like a call syntactically, and also looks like a typecast too. To default-initialize some fields you then needed to write some funky syntax using the default-operator like:

d := SomeData(10, _);

and this whole category of special rules, like "If default-operator is last value in initializer, all values after it are default-initialized too." made things much more complex than they needed to be.

With the new syntax, writing SomeData(x, y) in the definition is no longer needed, so definitions are cleaned up. Additionally, with the new syntax the ability to have named field initialization was added, for example

d := SomeData{ .y = 20 };

we can now describe that we only want to initialize certain fields and leave all other fields default-initialized. Regarding default-initialization: A new default-initialization system was added, which is applied recursively. Data fields now do no longer require a rhs to be default-initializable. It is enough that the fields type is default-initializable. This allows us to default-initialize even very complex types like objects by just writing T{}:

o := SomeComplexObject{};
o2 := SomeComplexObject{ SomeData{ .z = 300 }, SomeOtherData{ .speed = 3.4 } };

which makes the whole initialization-story of Flint a lot more ergonomic.

This whole initialization-story does not only apply do data but to tuples and objects as well, meaning that initialization now is unified across the types. For more details or in-depth feature explainations, look at the Wiki.

Comptime Parameter List

You were able to define data and variant types in-line using data<i32, f32, str> or variant<i32, f32, str> up until now. Callables also used the <...> syntax, like fn<i32, i32 -> i32>. Through the additions of generics and the comptime parameter list the syntax for these "inline-generic" types changed to make "generic application" use only one syntax across the board. So, instead of data<...>, variant<...> and fn<...> you now need to write it as data[...], variant[...] and fn[...].

This is the only breaking change regarding the addition of the CPL. The <...> syntax was only a placeholder anyway, it could have been changed earlier, but I waited for replacing it until actually implementing the CPL.

Feature Additions

  • Added the Comptime Parameter List to support generics:
use Core.print

data DListNode[type T]:
	T value;
	DListNode[T]? next = none;

data DListHead[type T]:
	DListNode[T]? head;

object List[type T]:
	data: DListHead[T] list;

	def append(T value):
		if list.head == none:
			list.head = DListNode[T]{value};
			return;
		DListNode[T]? current = list.head;
		while current!.next != none:
			current = current!.next;
		current!.next = DListNode[T]{value};

	const def print():
		DListNode[T]? current = list.head;
		while current != none:
			print(str(current!.value));
			if current!.next != none:
				print(" -> ");
			current = current!.next;
		print("\n");

	const def size() -> u64:
		u64 size = 0;
		DListNode[T]? current = list.head;
		while current != none:
			size++;
			current = current!.next;
		return size;

def main():
	l1 := List[i32]{};
	l1.append(10);
	l1.append(20);
	l1.append(30);
	print($"l1.size() = {l1.size()}\n");
	l1.print();

	l2 := List[f32]{};
	l2.append(3.14);
	l2.append(6.9);
	l2.append(42.0);
	print($"l2.size() = {l2.size()}\n");
	l2.print();
l1.size() = 3
10 -> 20 -> 30
l2.size() = 3
3.14 -> 6.9 -> 42.0
  • Added the generic array Core module:
use Core.array
use Core.print

def print_arr(i32[] arr):
	print("[");
	for (i, e) in arr:
		if i > 0:
			print(", ");
		print(str(e));
	print("]\n");

def main():
	i32[] arr = i32[_]{1, 3, 4};
	print_arr(arr);

	insert[i32](arr, 1, 2);
	print_arr(arr);

	resize[i32](arr, 9);
	print_arr(arr);

	shrink[i32](arr, 2);
	print_arr(arr);

	_ = remove[i32](arr, 3);
	print_arr(arr);

	i32[] src = i32[_]{8, 9, 10};
	merge[i32](arr, src, arr.len);
	print_arr(arr);
[1, 3, 4]
[1, 3, 1, 4]
[1, 3, 1, 4, 0, 0, 0, 0, 0]
[1, 3, 1, 4, 0, 0, 0]
[1, 3, 1, 0, 0, 0]
[1, 3, 1, 0, 0, 0, 8, 9, 10]
  • Added export keyword to mark functions which are exported when using the --lib flag:
export def add(i32 x, i32 y) -> i32:
    return x + y;
  • Added --lib <name> flag to produce a pair of <name>.h and <name>.o files, making exported Flint functions callable from C
  • Added support for cross-compilation, the --target <target> flag now finally works
  • Added the #fip_disable annotation to be able to define extern functions without FIP running:
#fip_disable
extern def malloc(u64 n) -> opaque;
  • Added new testing annotations (#test_pre, #test_post, #test_init, #test_deinit, #test_entry) :
use Core.assert
use Core.print

shared data G:
	i32 c = 0;

shared data Tests:
	bool t1 = false;
	bool t2 = false;

#test_init
test "init":
	G.c = 10;

#test_deinit
test "deinit":
	G.c = 0;

#test_pre
test "pre":
	G.c++;

#test_post
test "post":
	G.c++;

test "t1":
	if not Tests.t1:
		return;
	print($"G.c = {G.c}\n");
	assert(G.c == 11);

test "t2":
	if not Tests.t2:
		return;
	print($"G.c = {G.c}\n");
	assert(G.c == 13);

def print_help() -> i32:
	print("Usage: test [TEST]*\n");
	print("\n");
	print("Available TESTs:\n");
	print("  t1    Execute test t1, skipped by default\n");
	print("  t2    Execute test t2, skipped by default\n");
	return 1;

#test_entry
def entry(str[] args) -> i32:
	for u64 i = 1; i < args.len; i++:
		const str arg = args[i];
		if arg == "--help":
			return print_help();
		else if arg == "t1":
			Tests.t1 = true;
		else if arg == "t2":
			Tests.t2 = true;
		else:
			print($"Unrecognized argument: {arg}\n");
			return print_help();
$ ./test --help
Usage: test [TEST]*

Available TESTs:
  t1    Execute test t1, skipped by default
  t2    Execute test t2, skipped by default
$./test t1 t2
main.ft:
 ├─ t1     ✓ passed
 └─ t2     ✓ passed

✓ All tests passed!
  • Added proper stack traces for runtime errors
error ErrBar:
	Baz;

def bar():
    throw ErrBar.Baz("Boom");

def foo():
	bar();

def main():
	foo();
Runtime Error: ErrBar.Baz
  │ » Boom
  └─┬── Name ──┬── Location ──┬─ Info ──┐
    ├ bar        main.ft:5:5    origin  ┤
    ├ foo        main.ft:8:5    rethrow ┤
    └ _main      main.ft:11:5           ┘

Changelog

  • Added the #fip_disable annotation appliccable to extern functions (commit)
  • Added support for exported functions and the --lib <libname> flag:
    • Implemented code-generation of the export C-ABI wrapper functions to make exported functions now callable from within C. This feature is still marked as experimental, as I did not do much testing of it (commit)
    • Added --lib flag to produce a .o file with he builtin main and _main not being emitted in it (commit)
    • Added parsing support for the export keyword to be used on functions (commit)
    • Added flint.init.<libname> function as the single init function visible to the outside world (for the --lib flag) (commit)
    • Added ability to generate a <libname>.h file alongside the <libname>.o file when the --lib <name> flag is used (commit)
    • Renamed flint.dima.init_heads function to just flint.dima.init (commit)
    • Renamed flint.globals.<mylib>.init to flint.globals.init.<mylib> to match how the other init functions are called (commit)
    • Flipped boolean return value meaning of <libname>_init function to be more conventional (commit)
    • Fixed missing emission of const keyword for const parameters of functions (commit)
    • Added forward-declaration of all data types in the auto-generated exported header file to enable recursive types in C (commit)
    • Changed data-as-parameter passing of exported functions to always pass data as pointers, no matter if they are mutable or immutable (commit)
    • Added test to showcase the export keyword in action, added the test to the regression system (commit)
  • Added ubuntu package files (commit)
  • Added support for cross-compilation:
    • Added ability to produce windows executables from Flint programs on Linux with MinGW (commit)
    • Split up linking of linux-gnu and linux-musl to provide the back-bones of Windows->Linux cross-compilation. Needs to be tested on Windows (commit)
    • Fixed mistakes where #ifdef __WIN32__ was hard-coded for the builtin generation preventing Windows->Linux cross-compilation from ever working correctly (commit)
    • Changed all __WIN32__ define checks to _WIN32 (commit)
    • Added target field to metadata.json file to automatically rebuild the builtins lib when the target changes (commit)
    • Fixed windows-msvc target not working on Windows (commit)
    • Removed the need to call llvm-config when compiling to make cross-compilation on Windows work (commit)
  • Added ability to only build the Flint parser without any of the codegen stuff. This makes compilation a bit faster (since parser-related files do not need to be built twice, for the compiler and the LSP). This also is a preparation of the future fip-ft IM. Fixed deprecations (commit)
    • Moved build_flint-parser.zig to new build directory (commit)
    • Renamed build_flintc-parser.zig to flintc-parser.zig (commit)
    • Split up very large build.zig file into multiple smaller build files in the build directory (commit)
    • Fixed bug where the test build option would not depend on the install step of flintc, leading to the install step failing silently a lot of times as the binary is in-use (by the testing suite) while it tries to install it (commit)
    • Fixed mistake where install steps were skipped when not using the test build option (commit)
    • Fixed bug where the fls binary was not installed, as its install step was missing from the step dependency graph (commit)
    • Fixed the fls, it was built incorrectly (missing FLINT_LSP definition in the parser) and hard-crashed on "unknown type" errors (commit)
  • Initializer redesign and default-constructibility implementation:
    • Implemented the initializer redesign in its first form with default-construction still missing (commit)
    • Added ability to all types to be potentially default-constructible to be able to leave out fields when initializing values (commit)
    • Added ability to initialize tuples with the new T{...} syntax too (commit)
    • Added ability to leave out the field names and just rely on positional initialization (commit)
    • Added throwing errors when initializers are missing the braces (commit)
    • Updated AOC/2025/8, pong, pong-2.0, pong-3.0, pong-4.0 and perf examples to the new initializer syntax (commit)
    • Made data accessors in object definitions optional again, as we can rely on positional initialization for unnamed included data (commit)
    • Fixed explicit vector->vector casting not working (commit)
    • Fixed literal->vector casting not working, an expression like i32x2(5) would assign the type i32x2 to the literal 5 instead of assigning i32 and wrapping in a i32x2 type cast (commit)
    • Fixed bug where inline array initializers were matched as "regular" initializers by the ExprTrie matcher (commit)
    • Updated regression tests 0001-0100 to new initializer system (commit)
    • Updated regression tests 0101-0200 to new initializer system (commit)
    • Added ability that writing T[N](_) would try to default-construct a value of type T instead of creating a default-node expression (commit)
    • Updated regression tests 0201-0300 to new initializer system (commit)
    • Fixed bug where default-constructed strings would blow up the generator (commit)
    • Updated regression tests 0301-0400 to new initializer system (commit)
    • Updated regression tests 0401-0500 to new initializer system (commit)
    • Replaced DefaultNode with the proper default-construction system, renamed it to SwitchDefaultNode as this is the only place where it is actually needed (commit)
    • Updated spec tests to new initializer system (commit)
    • Updated the Wiki tests to new initializer system (commit)
    • Fixed bug where storing T-typed values on a field of type T? in the initializer would crash the compiler as it would not emit a direct store but try to clone it instead (commit)
  • Added ErrExprNotRecognizable and ErrStmtNotRecognizable error types (commit)
  • Added the ErrFnVoidInReturnGroup and ErrFnVoidParamType error types (commit)
  • Added the ErrLitIntTooLarge and ErrLitIntTooSmall error types (commit)
  • Implemented generics:
    • Changed syntax of "generic" containers from data<...>, variant<...> and fn<...> to data[...], variant[...] and fn[...] (commit)
    • Added the CPL to work with all definitions which do not contain a body, e.g. data and variant. The func, interface and object contain functions which also need to become generic, adding support for generic functions will be done next (commit)
    • Updated debug.cpp to print the CPL if present on all types which will support generics in the future (commit)
    • Fixed bug where LSP did not clean up Specializer global state in between parsing calls leading to stale data and other unexpected errors (commit)
    • Refactored the Line struct, type collapsing types now can happen in parallel as every definition owns its tokens (commit)
    • Implemented parameterized generic functions + calls (commit)
    • Implemented ability to infer comptime parameters from values passed into the function (commit)
    • Added ability to define generic func components (commit)
    • Added ability to define generic interfaces (commit)
    • Implemented ability to define and use generic object types (commit)
    • Swapped now-incorrect cpl.empty() checks to is_generic_template calls which is the correct check in a few places (commit)
    • Fixed bug where the implements clause was unable to contain generic interfaces (commit)
  • Added the array Core module containing only generic functions (commit)
  • Added new testing annotations to make Flint tests more powerful:
    • Added parsing support for the #test_pre, #test_post, #test_init and #test_deinit annotations (commit)
    • Added codegen support for the #test_init, #test_deinit, #test_pre and #test_post test annotations (commit)
    • Fixed bug where --test flag would lead to a crash when no functions were present (commit)
    • Added parsing support for the #test_entry annotation (commit)
    • Added codegen support for the #test_entry annotation (commit)
  • Added proper stack-traces to runtime errors (commit)
  • Added the #test_post_always annotation (commit)
  • Added tests for new Comptime chapter of the Wiki (commit)
  • Completely overhauled the debug flags and debut printing, the debug build now is silent by default (commit)
  • Added normalize_crlf function to the string module to also be able to normalize the crlf in the system_command, end_capture and end_capture_lines functions of the Core.system module (commit)
  • Changed --print-libbuiltins-path to no longer exit with an exit code of 1 (commit)
  • Upgrade from LLVM 21.1.8 to 22.1.8 (commit)
  • Changed fip to be built as a library (commit)
  • Updated auto-generated fip binding generation, removed emission of one newline too much (commit)
  • Remembered that I have a separate one_of overload which makes a lot of matcher patterns a lot cleaner (commit)
  • Changed all integer abs functions of the math Core module to return unsigned numbers (commit)
  • Changed project structure a bit:
    • Deleted test_files and .fip root directories (commit)
    • [1/2] Changed directory structure to have a top-level tests with the examples being a subdirectory of it (commit)
    • [2/2] Changed directory structure to have a top-level tests with the examples being a subdirectory of it (commit)
    • Updated build.zig file to make test step work again (commit)
  • Refactored generator_error.cpp. Removed err from within the function names, moved it to a flint.error. prefix instead (commit)
  • Renamed misleading annotations #test_output_always and #test_output_never (commit)
  • Changed name of libbuiltins.lib file to be builtins.lib on Windows so that you can link to it using -lbuiltins regardless of platform (commit)
  • Updated all tests for Windows (different expected output / exit codes if on Windows etc) (commit)
  • Refactored allocation system to reduce HW stack usage (commit)
  • Removed dependency on json-mini repository, integrated the two small files into the project (commit)
  • Removed manual cloning of the fip dependency, added it to the zig build system instead (commit)
  • Fixed --no-fip flag not working for fip imports with a module tag (commit)
  • Fixed some weird compile erros when doing zig build all (commit)
  • Fixed f32_to_str and f64_to_str casting functions, exponent output was wrong and finally fixed the .0 stripping, the result of casting 3.0 to a string now is 3.0 instead of 3 like it was until now (commit)
  • Fixed bug where the linearizer would not detect that the end of the object definition is reached when it encountered a const def or export def function, the "end of declaration part" detection now is more robust (commit)
  • Fixed bug in Parser::resolve_all_imports function where the import resolving would not work for aliased imports (commit)
  • Fixed resolve_all_unknown_types function as the INTERFACE variation was not handled (commit)
  • Fixed bug where objects with bodies of less than 2 logical lines (like incorrect object definitions) would crash the compiler (commit)
  • Fixed named opaques not working when importing them from another file (commit)
  • Fixed bug where symbols of aliased imports were placed in the private section of a file, this should not happen (commit)
  • Fixed bug where passing multiple freeable variants to a function would lead to later parameters overwriting earlier ones as both write to the scratchspace (commit)
  • Fixed bug where switch expressions where all branches are pure string literals would lead to the whole switch expression being a literal too leading to runtime segfaults (commit)
  • Fixed bug where various types were not handled to be the base type of arrays blocking array access code generation (commit)
  • Fixed bug where variant declarations where the rhs is more complex than a variant literal would lead to codegen crashes (commit)
  • Fixed bug where calling a function inside a variant switch expression arm would generate malformed IR code (commit)
  • Fixed bug where variant literals which are not freeable were tried to be loaded before passing them to functions (commit)
  • Fixed bug where post test would run unconditionally even if the actual test would fail (commit)
  • Fixed mistake where to_string and to_diagnostic functions were not purely virtual (commit)
  • Fixed Windows build (Windows is more sensitive to alignment issues) (commit)
  • Fixed CRLF problems on Windows by updating the read_file function and completely rewriting the read_lines function from scratch (that one was horribly written) (commit)
  • Fixed bug where on windows the system command would always append a space at the end (commit)
  • Fixed big TS mistake (commit)
  • Fixed stack-trace-bookkeping relying on hardware stack allocations, reducing HW stack usage further (commit)

Full Changelog: v0.4.1-core...v0.4.2-core