* remove the unused is_native_tail_call_heuristic forward reference
This forward-reference from Lambda to Asmcomp was used to generate
machine-specific tailcall information in -annot output; this only use
was removed in 57d329e07b, so we can now
remove it to simplify the codebase.
The logic was non-trivial and might be useful again in the future.
* [minor] testsuite: convert warnings/w51.ml to an expect-test
* [minor] translattribute: refactor attribute payload deconstruction
* [@tailcall false]: warn if the call *is* a tailcall
(+ constructor renaming suggested by Nicolás during review)
* Changes
* testsuite: add an example with the 'invalid payload' exception
(suggested by Nicolás during review)
Actually *using* the location in the failure handler would be
incorrect, as it adds noise to backtraces involving partial exception
handlers. This is now properly documented.
We still take the caller location (which is currently ignored) for
consistency with other toplevel Matching functions, and because that
may become useful if we want to add more error-reporting or warnings
in compile_matching.
It is invalid to reuse a Lambda.t term twice, because bound variables
may be used non-uniquely. If we want to perform a code transformation
may duplicate subterms in some cases, we have to refresh all bound
variables of the copied subterm.
The present PR implements a function
Lambda.duplicate : lambda -> lambda
that does exactly this. It is implemented by making Lambda.subst
parametrized over a transformation on bound variables.
We want to start allowing more information in the payload of
[@tailcall] attributes (currently no payload is supported), for
example we could consider using [@tailcall false] to ask the code
generator to disable a tail call.
A first required step in this direction is to use a custom datatype to
represent the tail-call attribute, instead of a boolean. This is
consistent with the other application-site
attributes (inline_attribute, specialise_attribute, local_attribute),
so it makes the code more regular -- but the change itself is
boilerplate-y.
This commit introduces a quantity Lambda.max_arity that is the maximal
number of parameters that a Lambda function can have.
Uncurrying is throttled so that, for example, assuming the limit is 10,
a 15-argument curried function fun x1 ... x15 -> e
becomes a 10-argument function (x1...x10) that returns a 5-argument
function (x11...x15).
Concerning untupling, a function that takes a N-tuple of arguments,
where N is above the limit, remains a function that takes a single
argument that is a tuple.
Currently, max_arity is set to 126 in native-code, to match the new
representation of closures implemented by #9619. A signed 8-bit field
is used to store the arity. 126 instead of 127 to account for the
extra "environment" argument.
In bytecode the limit is infinity (max_int) because there are no needs
yet for a limit on the number of parameters.
Lambda and Clambda distinguish Const_int from Const_pointer only so
that they can pass the information to Cmm. But now that that
Const_pointer is gone from Cmm (#9578), there's no need for the
distinction in Lambda either.
This PR requires a bootstrap, because the .cmo format changes:
Lambda.structured_constant has one fewer constructor. The bootstrap
is in the following commit.
This loses no information (descriptions contain the tag), but it will
make it easier to obtain the descriptions inside `combine_constructor`
without doing a dynamic check on the patterns. This will in turn help
simplify the interaction with `Parmatch.complete_constrs`.
Note: after this patch we use `Types.equal_tag` instead of `( = )` to
compare tags during pattern-matching compilation. This is better code,
and we believe that it does not change the behavior: `Types.equal_tag`
is mostly similar to a type-specialized version of `( = )`, except
that it calls `Ident.same` that just compares the stamps and ignore
the names, which (assuming well-formedness of idents) is equivalent
and slightly faster.
"rows" are common abstraction of both pattern analysis and
compilation, but clauses carrying a Lambda.t term are
compilation-specific. Separating rows will thus enable moving more
logic to typing/patterns for use in both settings.
The aim is to also move the Simple/Half_simple/General stuff from
matching, but we need to split in those modules the part that are
purely structural (they go in Patterns) and the parts that are
actually compilation logic (Half_simple.of_clause), those stay in
Matching.
Before, each head construction had a `make_<foo>_matching` construct that
was responsible for three things:
- consuming the argument from the argument list
(the "argument" is a piece of lambda code to access
the value of the current scrutinee)
- building arguments for the subpatterns of the scrutinee
and pushing them to the argument list
- building a `cell` structure out of this, representing a head group
during compilation
Only the second point is really specific to each construction.
This refactoring turns this second point into a construct-specific
`get_expr_args_<foo>` function (similarly to `get_pat_args_<foo>`),
and moves the first and third point to a generic `make_matching`
function.
Note: this commit contains a minor improvement to the location used to
force (lazy ..) arguments.
This commit is delicate and needs a careful review.
The `matcher_of_pattern` function is a temporary measure to reduce the
invasiveness of the patch, and make it easier to review.
(Note for reviewers: in the previous version the Record case had
a funny handling of Any, but it is in fact equivalent to just adding
omegas as we now do in all cases.)
There are two obvious directions for improvement:
- Get rid of matcher_of_pattern and pass a head directly to the
various make_matching_* functions.
- Try to factorize this code with ctx_matcher which, it is now
obvious, does essentially the same thing.
Another, less immediate area of attack would be to consider
a presentation of Pattern_head.t where the Any case can be statically
ruled out -- maybe the description could have two levels, one
isomorphic to option (Any or not?) and one for non-any heads.
(This commit is more tricky than the previous ones in the patchset
and requires a careful review.)
This refactoring clarifies and simplifies the specialize_matrix logic
by getting rid of the OrPat exception used in a higher-order
way (or sometimes not used in certain matchers, when it is possible to
"push" the or-pattern down in the pattern). Instead it uses an
arity-based criterion to implement the or-pattern-related logic once
in the specializer, instead of having to implement it in each
matcher. As a result, the compiler improves a bit as it will push
or-patterns down during specialization in valid situations that were
not implemented before -- probably because they are not terribly
important in practice: all constant and arity-1 constructs benefit
from optimized or-pattern handling, in particular the following are
new:
- lazy patterns
- non-constant polymorphic variants
- size-one records and arrays
Several functions in the pattern-matching compiler do recursive
"specialization" through a filter_rec helper written in tail-call
style with a 'rem' parameter containing the matrix rows yet to be
processed as input. Typical returns of the function are
foo :: filter_rec rem
to add a new output, and
filter_rec (foo :: rem)
to add a new input to be processed (usually by partial decomposition
of the current input row).
Some places would contain the declaration (let rem = filter_rec rem)
to factorize outputs of the first kind, but this gives a programming
style that is very confusing as `rem` may now represent either an
input or an output of the filter.
Using better types (as will be done farther away in the
pattern-matching refactoring) avoids this problem completely:
specialization then has different input and output types (typically,
from general to half-simple patterns), so incorrectly mixing inputs
and outputs is impossible. Yay typing.