general purpose programming language, in the vein of C++
No new features, possibly some regressions. Changes:
Mainly a release to fix the CI builds, but we have one new feature; the compile-time interpreter will now refuse to call out to external functions via FFI by default, unless --ffi-escape
is passed on the command line.
This is kinda similar to --shell-escape
used in LaTeX.
Overriding methods now check for a compatible method (in name and signature) in the base class hierarchy, and will error if none is found
Method "compatibility" has been expanded to include covariant return types and contravariant parameter types. Note that this applies only to pointer-to-class types, and not value-of-class types.
class Foo : Bar
{
init(x: int, y: int) : super(x * y) { ... }
}
Foo().bar(...)
Binaries and headers are licensed under their respective licenses.
export
s and import
s can now refer to paths, eg. import foo::bar
will look for foo/bar.flx
.
Exporting a path will simply place the (public) contents of the file into a namespace with the same name. Bits of the new std
library use this, for example class map
is in map.flx
, but it exports std
-- meaning the definition of map
will live in std::
.
Note that importing a string literal is still supported, but they no longer add the extension if missing -- you would need to import "foo/bar.flx"
!
Identifier paths (eg foo::bar
) can now start with ::
to refer to the root scope, and include ^
to refer to the parent scope. For example:
fn main()
{
let foo = 10
do {
let foo = 20
libc::printf("^::foo = %d!\n", ^::foo) // prints 10!
}
}
Implicit method calls (ie. calling a method without doing self.method(...)
) work again
Floating point values can now be compared with each other (oops)
Flip the declaration of ffi-as
, because external names might have strange characters (like spaces! or $
or @
or whatever) that are not valid in Flax identifiers. Made more sense to have the Flax-visible name as part of the actual declaration.
Also, made the external name a string literal, for the reasons stated above.
More QoL changes:
import
s can now either be private
(the default), or public
. Private imports are the old behaviour (I think -- I can't remember if we successfully removed re-exporting previously), where whatever the module imports is not transitively exported to other modules that import it. The reverse is true of public imports.Given the following:
export foo
public fn foo() { }
// ...
export bar
public import "foo"
// ...
export baz
private import "foo" // you'll actually get a warning that it's redundant
fn main()
{
// foo::foo is visible from bar, as bar::foo::foo()
bar::foo::foo()
// foo is not visible from baz!
// baz::foo::foo()
}
ffi
) as
something -- these alias names participate in overloading (and the 'real' names are no longer visible). Particularly useful for a certain someone:export gl
public ffi fn glVertex2i(x: int, y: int) as vertex
public ffi fn glVertex2f(x: float, y: float) as vertex
public ffi fn glVertex2d(x: double, y: double) as vertex
public ffi fn glVertex3i(x: int, y: int, z: int) as vertex
public ffi fn glVertex3f(x: float, y: float, z: float) as vertex
public ffi fn glVertex3d(x: double, y: double, z: double) as vertex
// ...
import "gl"
gl::vertex(1, 1) // calls glVertex2i
gl::vertex(1.0, 1.0, 0.5) // calls glVertex3d
Oops, fixed a bug where the first branch of an #if
had scoping issues where the declarations were not correctly placed at the top-level scope.
Also, there are obvious security implications for being able to #run
arbitrary code at compile-time, eg. #run system("rm -rf /*")
(thanks, sim642), especially from an imported library.
We'll probably disable calling out to external functions during compile time, or have some way to change that (flag, or directive on imports, or something).
Fixes an issue detailed in the previous release (0.41.0):
Optional arguments in a variadic function must be specified somewhere in the argument list, to prevent the variadic arguments from being "stolen" by the positional parameter (then you get an error about how you must pass the optional argument by name, even though that wasn't your intention). For example, this:
fn qux(x: str, y: int = 3, args: [str: ...]) { ... }
Will result in this error:
error: optional argument 'y' must be passed by name at: ultratiny.flx:28:26 | 28 | qux("hello, world!", "hi", "my", "name", "is", "bob", "ross") | ‾‾‾‾
Now, the example above will work "intuitively"; the strings (starting from "hi"
) will be passed as varargs to the function, and y
will have the default value of 3
.
Note that this means if the intention was to pass a value to y
(but you forgot the name), like this: foo("bla", 7, "a", "bunch", "of", "strings")
, the error will now be the failure in casting 7
to str
, because we presume you wanted to pass 7
as a vararg.
We can't have it both ways, but I feel like the new behaviour will be the more helpful error in the majority of cases.