This adds a forkv statement which is used to iterate over a map with a
body of statements. This is an important data transformation tool which
should be used sparingly, but is important to have.
An import statement inside of a forkv loop is not currently supported.
We have a simple hack to detect the obvious cases, but more deeply
nested scenarios probably won't be caught, and you'll get an obscure
error message if you try to do this.
This was incredibly challenging to get right, and it's all thanks to Sam
for his brilliance.
Note, I couldn't think of a better keyword that "forkv" but suggestions
are welcome if you think you have a better idea. Other ideas were formap
and foreach, but neither got me very excited.
This adds a for statement which is used to iterate over a list with a
body of statements. This is an important data transformation tool which
should be used sparingly, but is important to have.
An import statement inside of a for loop is not currently supported. We
have a simple hack to detect the obvious cases, but more deeply nested
scenarios probably won't be caught, and you'll get an obscure error
message if you try to do this.
This was incredibly challenging to get right, and it's all thanks to Sam
for his brilliance.
Co-authored-by: Samuel Gélineau <gelisam@gmail.com>
This adds an initial implementation of printing line numbers on type
unification errors. It also attempts to print a visual position
indicator for most scenarios.
This patch was started by Felix Frank and finished by James Shubin.
Co-authored-by: Felix Frank <Felix.Frank.de@gmail.com>
This is a first attempt to add a new function for URL parsing, using
go's net/url package and the simple API. This is still a barebones
implementation, there's possibility to expose more information. It also
includes simple tests.
It would be a likely mistake to create a self-referential frag, and mgmt
would spin forever updating the file... We probably don't want this, so
let's just catch this case in Validate.
Of course you could get around this with multiple files, and a fancier
search could statically check the graph, but the goal isn't to prevent
any bad code, since that's not likely to be possible.
We forgot to reject this corner case which could lead to a runtime error
since the expected type from the incoming struct would not match what
we're handling.
If more than one star import is present in the same scope, allow it. If
one star import could overwrite something, ordering is not guaranteed.
We allow this for now, but we might create a compiler fix to stop it.
This adds a test to notice both of these behaviours.
Even if they have no side effect, they aren't legal, since it would be
surprising if suddenly something new got added in one which then broke
the imports.
If you had ambiguous code, and specified an invalid type, this could
sneak through and become a runtime error, instead of a compile-time
error. We fix this and add a test.
This adds a modern type unification algorithm, which drastically
improves performance, particularly for bigger programs.
This required a change to the AST to add TypeCheck methods (for Stmt)
and Infer/Check methods (for Expr). This also changed how the functions
express their invariants, and as a result this was changed as well.
This greatly improves the way we express these invariants, and as a
result it makes adding new polymorphic functions significantly easier.
This also makes error output for the user a lot better in pretty much
all scenarios.
The one downside of this patch is that a good chunk of it is merged in
this giant single commit since it was hard to do it step-wise. That's
not the end of the world.
This couldn't be done without the guidance of Sam who helped me in
explaining, debugging, and writing all the sneaky algorithmic parts and
much more. Thanks again Sam!
Co-authored-by: Samuel Gélineau <gelisam@gmail.com>
This removes the exclusive from the res names and edge names. We now
require that the names should be lists of strings, however they can
still be single strings if that can be determined statically.
Programmers should explicitly wrap their variables in a string by
interpolation to force this, or in square brackets to force a list. The
former is generally preferable because it generates a small function
graph since it doesn't need to build a list.
If we had a single list wrapped in an interpolated string, it could
sneak through type unification, which is not correct. Wrapping a
variable by interpolation in a string, must force it to be a string.
A regression in 4b0cdf9123 caused the
basic send/recv functionality to break for simple scenarios. This was
due to inadequate testing, and a partial misunderstanding of the
situation.
New testing should hopefully catch more cases, but send/recv and
compile-time checks are still not as complete as is probably possible.
This improves the autogrouping algorithm to support hierarchical
autogrouping. It's not guaranteed to work if we replace the reachability
grouper with something more efficient, but it's good enough for now.
Some of our special tests can only be run once per `go test` invocation.
That is, using the test -count flag will cause a guaranteed failure
since we depend on a global being initialized only once as part of that
test.
This adds a per-test config option so that a user can specify to never
run a particular test more than once. This lets us continue to use the
-count flag with the test suite, without it causing some tests to fail.
The Ordering and DAG detection code is challenging because we need
Ordering to do SetScope, but Ordering itself needs to know about scopes.
This improved variant should hopefully catch all the scenarios of
identically named variables causing invalid loops.
Co-authored-by: Samuel Gélineau <gelisam@gmail.com>
We are planning to implement an optimization in which some function
calls are compiled to a single static graph rather than to a CallFunc
which dynamically creates a sub-graph at runtime. These test cases
exercise corner cases for which it would be theoretically possible to
use a static graph, but which we might not be able to optimize.
This implements a new type of syntactic sugar for the common pattern of
a base class which returns a child class, and so on. Instead of needing
to repeatedly indent the child classes, we can instead prefix them at
the definition site (where created with the class keyword) with the name
of the parent class, followed by a colon, to get the desired embedded
sugar.
For example, instead of writing:
class base() {
class inner() {
class deepest() {
}
}
}
You can instead write:
class base() {
}
class base:inner() {
}
class base:inner:deepest() {
}
Of course, you can only access any of the inner classes by first
including (with the include keyword) a parent class, and then
subsequently including the inner one.
This adds support for `include as <identifier>` type statements which in
addition to pulling in any defined resources, it also makes the contents
of the scope of the class available to the scope of the include
statement, but prefixed by the identifier specified.
This makes passing data between scopes much more powerful, and it also
allows classes to return useful classes for subsequent use.
This also improves the SetScope procedure and adds to the Ordering
stage. It's unclear if the current Ordering stage can handle all code,
or if there exist corner-cases which are valid code, but which would
produce a wrong or imprecise topological sort.
Some extraneous scoping bugs still exist, which expose certain variables
that we should not depend on in future code.
Co-authored-by: Samuel Gélineau <gelisam@gmail.com>
This fixes two small races we had while simultaneously reading from and
writing to the vertex timestamp, and simultaneously writing to the
vertex isStateOK (dirty) flag.
They were actually "safe" races, in that it doesn't matter if the
read/write race got the old or new value, or that the double write
happened. The time sequencing was correct (I believe) in both cases, but
this triggers the race detector now that we have tests for it.
Since this special one_instance function uses global state, if it's
re-used in more than one test, this won't work since they still all use
the whole global state. Make new ones for each test.
This also breaks the count=2 feature (any number other than 1) when
running these, which is not ideal. Create a cleanup API that we can run
between tests to reset the global state.
This adds ExprTopLevel and ExprSingleton and ensures that ExprBind is
now monomorphic.
This corrects a previous design bug where it was not monomorphic and
would thus cause spawning of many more copies than necessary. In most
cases this was only harmful to memory and performance, and not
behaviour, since these functions were pure, and we didn't have a test
for this.
This also adds a bunch more tests. Most notably, the graph shape tests
generally produce smaller graphs now.
Lastly, a lambda cannot have two different types when used at two
different call sites. It is rare that this would be used, and when it
would make sense, there are easy workarounds to accomplish equivalent
goals.
This was mostly authored by Sam, James helped with some cleanup and
debugging.
Co-authored-by: James Shubin <james@shubin.ca>
We don't allow a lambda variable to be polymorphic anymore. Of course
this isn't bad, but it makes things too difficult, at least for now.
It's also likely that we don't need this specific feature very often I
think.
We should not call either of these functions more than once for their
values. If we do, it means we have made a mistake with a compiler
optimization.
This is important, because otherwise if you had code like:
$x = random_password()
Then this would obviously be a problem. Thankfully, the situations where
functions generate unique data is rare, but it's probably something we
should take care of.
We've previously not received a value from within an autogrouped
resource. It turns out this would be quite useful, and so this patch
implements the additional plumbing and testing so that this works!
Testing that an autogrouped resource can still send values has not been
done at this time.
This adds a new series of "get*" functions which can read values from
the associated "value" resources. The key name of the function must
match the name value of the resource for things to work.
Type unification isn't yet perfect in these scenarios, so you should use
casually and with caution.
This test detects a mistake which is easy to make: when making a
recursive call to the target of an ExprVar, it would be easy to
accidentally pass the environment, like we usually do with every other
recursive call. For variables, this is a mistake, because the lambda
parameters which are in scope where the variable is used must not be in
scope where the variable is defined.
In fact, ExprVar.Graph() currently makes this mistake. The test passes
anyway, because an earlier phase (SetScope) correctly clears the
environment and detects the problem before the Graph phase. Thus, this
test does not guarantee that all the phases correctly clear their
environment, it merely detects the unlikely case in which all the phases
make the same mistake.
This modifies the panic feature to accept a boolean or a string. If true
or not empty, then it will cause the panic. This makes some of the error
code a little less ugly.
This is a newer implementation of the panic magic. I kept the old commit
in for posterity and to show the difference. The two versions are
identical to the end-user with one exception: the newer version doesn't
include a useless panic resource in the graph when there is no panic. In
this version, the panic function returns false and the if statement it's
the condition of, doesn't produce the resource within. On error, we
still consume the function in the if expression, and doing so causes
everything to shutdown.
The other benefit is that the implementation is much cleaner and doesn't
need the interpolate hack.