asoffer / Icarus

An experimental general-purpose programming language
Apache License 2.0
9 stars 2 forks source link

Program startup #82

Closed asoffer closed 2 years ago

asoffer commented 2 years ago

This is a proposal for a bunch of things related to program startup.

Initialization

Global variables are initialized according to some ordering consistent with the following rules:

This means that if a imports b and c, and b and c each import d, we know that every variable in d is initialized before any variable in any other module, but we cannot make any claims about the initialization of variables in b or c relative to the other. They may even be interleaved.

Destruction

It is not guaranteed that any global variable is destroyed. However it is guaranteed that if a is initialized before b, and a is destroyed, b is destroyed as well.

This effectively allows us to remove a suffix of destructor calls.

Command-line arguments

We'll provide a builtin variable, tentatively called arguments available only at global scope (but available in every module) of type [][]char, which holds the command-line arguments, effectively akin to C's argc and argv. These arguments are immutable.

asoffer commented 2 years ago

One oddity of this approach is that we cannot guarantee that if

a ::= import "a.ic"
b ::= import "b.ic"

That the initialization of a happens before the initialization of b.

Imagine the main binary imports x and y in some order, and that x imports a then b, and that y imports b then a. There is no consistent ordering here.

This might be surprising, but it should be difficult to rely on this in any way. Any way one might attempt to rely on this would have to be external to the system: It could involve writing a file in one module and attempting to read it in another, for example.

This is similar to the C++ static initialization order fiasco, but significantly reduced in scope.

This doesn't feel like a non-starter to me, but I think there would be an item in Effective Icarus saying something like "avoid IO during initialization." (though that's not exactly the right thing to avoid).

perimosocordiae commented 2 years ago

This is similar to the way Python handles imports: a module will only be imported once (barring hot-reload hacks), but unless you control the main binary you can't make assumptions about import ordering. It generally doesn't cause trouble, probably due to a cultural avoidance of side-effects in module-level code.

asoffer commented 2 years ago

I'm closing this as effectively implemented now. We've implemented the initialization and arguments parts, but not the destructor-elision parts. That is, even at global scope, destructors are currently guaranteed to be run. This is certainly fine for now; it's not particularly surprising. I do think there's likely value in defaulting this to not running them unless the user specifies that it's really important that they be run, but there's a lot more design work for that to even decide if it's worth doing.