instead of globally storing unresolved and tentative defs,
store indices to actual symbols in the functions that are responsible
for symbol resolution.
This is now no longer limited to targeting macOS natively but also
tries to detect the sysroot when targeting different Apple platforms
from macOS; for instance targeting iPhone Simulator from macOS. In
this case, Zig will try detecting the SDK path by invoking
`xcrun --sdk iphonesimulator --show-sdk-path`, and if the command
fails because the SDK doesn't exist (case when having CLT installed only)
or not having either Xcode or CLT installed, we simply return null
signaling that the user has to provide the sysroot themselves.
For example, in order to run a binary on an iPhone Simulator,
you need to specify that explicitly as part of the target as
`aarch64-ios-simulator` rather than `aarch64-ios-gnu` or
`aarch64-ios` for short.
* ensure we correctly transfer `-iwithsysroot` and
`-iframeworkwithsysroot` flags with values from `build.zig` and that
they are correctly transferred forward to `zig cc`
* try to look for `libSystem.tbd` in the provided syslibroot - one
caveat that the user will have to specify library search paths too
Previously, I have incorrectly assumed that with two-level namespace
we only need to link in dylibs/frameworks that actually export symbols
which are undefined in the linked image. Turns out, regardless of
whether we link with two-level namespace (default on macOS) or a
flat namespace (more common on other platforms), we always need to
put the dylibs/frameworks as specified by the user from the linker
line into the final linked image.
Some parsers interpret these as octal, some don't, and the confusion can lead to vulnerabilities.
Return error.NonCanonical when parsing IPv4 addresses with 0 prefixes.
* Value: rename `error_union` to `eu_payload` and clarify the intended
usage in the doc comments. The way error unions is represented with
Value is fixed to not have ambiguous values.
* Fix codegen for error union constants in all the backends.
* Implement the AIR instructions having to do with error unions in the
LLVM backend.
* New AIR instructions: ptr_add, ptr_sub, ptr_elem_val, ptr_ptr_elem_val
- See the doc comments for details.
* Sema: implement runtime pointer arithmetic.
* Sema: implement elem_val for many-pointers.
* Sema: support coercion from `*[N:s]T` to `[*]T`.
* Type: isIndexable handles many-pointers.
* Introduce `ret_load` ZIR instruction which does return semantics
based on a corresponding `ret_ptr` instruction. If the return type of
the function has storage for the return type, it simply returns.
However if the return type of the function is by-value, it loads the
return value from the `ret_ptr` allocation and returns that.
* AstGen: improve `finishThenElseBlock` to not emit break instructions
after a return instruction in the same block.
* Sema: `ret_ptr` instruction works correctly in comptime contexts.
Same with `alloc_mut`.
The test case with a recursive inline function having an implicitly
comptime return value now has a runtime return value because of the fact
that it calls a function in a non-comptime context.
This commit solves the problem in a much simpler way: putting
runtime-known values in place of non-comptime arguments when
instantiating a generic function.
The `comptime_args` field of Fn has a clarified purpose:
For generic function instantiations, there is a `TypedValue` here
for each parameter of the function:
* Non-comptime parameters are marked with a `generic_poison` for the value.
* Non-anytype parameters are marked with a `generic_poison` for the type.
Sema now has a `fn_ret_ty` field. Doc comments reproduced here:
> When semantic analysis needs to know the return type of the function whose body
> is being analyzed, this `Type` should be used instead of going through `func`.
> This will correctly handle the case of a comptime/inline function call of a
> generic function which uses a type expression for the return type.
> The type will be `void` in the case that `func` is `null`.
Various places in Sema are modified in accordance with this guidance.
Fixed `resolveMaybeUndefVal` not returning `error.GenericPoison` when
Value Tag of `generic_poison` is encountered.
Fixed generic function memoization incorrect equality checking. The
logic now clearly deals properly with any combination of anytype and
comptime parameters.
Fixed not removing generic function instantiation from the table in case
a compile errors in the rest of `call` semantic analysis. This required
introduction of yet another adapter which I have called
`GenericRemoveAdapter`. This one is nice and simple - it's the same hash
function (the same precomputed hash is passed in) but the equality
function checks pointers rather than doing any logic.
Inline/comptime function calls coerce each argument in accordance with
the function parameter type expressions. Likewise the return type
expression is evaluated and provided (see `fn_ret_ty` above).
There's a new compile error "unable to monomorphize function". It's
pretty unhelpful and will need to get improved in the future. It happens
when a type expression in a generic function did not end up getting
resolved at a callsite. This can happen, for example, if a runtime
parameter is attempted to be used where it needed to be comptime known:
```zig
fn foo(x: anytype) [x]u8 { _ = x; }
```
In this example, even if we pass a number such as `10` for `x`, it is
not marked `comptime`, so `x` will have a runtime known value, making
the return type unable to resolve.
In the LLVM backend I implement cmp instructions for float types to pass
some behavior tests that used floats.
When doing a function call, if the return type requires comptime, the
function is analyzed as an inline/comptime call.
There is an important TODO here. I will reproduce the comment from this
commit:
> In the case of a comptime/inline function call of a generic function,
> the function return type needs to be the resolved return type based on
> the function parameter type expressions being evaluated with comptime arguments
> passed in. Otherwise, it ends up being .generic_poison and failing the
> comptime/inline function call analysis.
For now these errors are handled via `@panic` rather than `unreachable`.
These are relatively likely bugs to occur at this early stage of
development, and handling them as panics lets us ship release builds
of the compiler without worrying about undefined behavior.
Furthermore, in stage1, `@panic` is implemented to include an error
return trace, while `unreachable` is not. In this case, the error return
traces are extremely helpful in debugging the compiler.
There was a bug in stage2 regarding iteration of function parameter AST.
This resulted in a false negative "unused parameter" compile error,
which, when fixed, revealed a bug in the std lib HashMap implementation.
* ZIR encoding for function instructions have a body for the return
type. This lets Sema for generic functions do the same thing it does
for parameters, handling `error.GenericPoison` in the evaluation of
the return type by marking the function as generic.
* Sema: fix missing block around the new Decl arena finalization. This
led to a memory corruption.
* Added some floating point support to the LLVM backend but didn't get
far enough to pass any new tests.
Module has a new field `monomorphed_funcs` which stores the set of
`*Module.Fn` objects which are generic function instantiations.
The hash is based on hashes of comptime values of parameters known to be
comptime based on an explicit comptime keyword or must-be-comptime
type expressions that can be evaluated without performing monomorphization.
This allows function calls to be semantically analyzed cheaply for
generic functions which are already instantiated.
The table is updated with a single `getOrPutAdapted` in the semantic
analysis of `call` instructions, by pre-allocating the `Fn` object and
passing it to the child `Sema`.
* The `indexable_ptr_len` ZIR instruction now uses a `none_or_ref`
ResultLoc. This prevents an unnecessary `ref` instruction from being
emitted.
* Sema: Fix `analyzeCall` using the incorrect ZIR object for the
generic function callee.
* LLVM backend: `genTypedValue` supports a `Slice` type encoded with
the `decl_ref` `Value`.
AstGen result locations now have a `coerced_ty` tag which is the same as
`ty` except it assumes that Sema will do a coercion, so it does not
redundantly add an `as` instruction into the ZIR code. This results in
cleaner ZIR and about a 14% reduction of ZIR bytes.
param and param_comptime ZIR instructions now have a block body for
their type expressions. This allows Sema to skip evaluation of the
block in the case that the parameter is comptime-provided. It also
allows a new mechanism to function: when evaluating type expressions of
generic functions, if it would depend on another parameter, it returns
`error.GenericPoison` which bubbles up and then is caught by the
param/param_comptime instruction and then handled.
This allows parameters to be evaluated independently so that the type
info for functions which have comptime or anytype parameters will still
have types populated for parameters that do not depend on values of
previous parameters (because evaluation of their param blocks will return
successfully instead of `error.GenericPoison`).
It also makes iteration over the block that contains function parameters
slightly more efficient since it now only contains the param
instructions.
Finally, it fixes the case where a generic function type expression contains
a function prototype. Formerly, this situation would cause shared state
to clobber each other; now it is in a proper tree structure so that
can't happen. This fix also required adding a field to Sema
`comptime_args_fn_inst` to make sure that the `comptime_args` field
passed into Sema is applied to the correct `func` instruction.
Source location for `node_offset_asm_ret_ty` is fixed; it was pointing at
the asm output name rather than the return type as intended.
Generic function instantiation is fixed, notably with respect to
parameter type expressions that depend on previous parameters, and with
respect to types which must be always comptime-known. This involves
passing all the comptime arguments at a callsite of a generic function,
and allowing the generic function semantic analysis to coerce the values
to the proper types (since it has access to the evaluated parameter type
expressions) and then decide based on the type whether the parameter is
runtime known or not. In the case of explicitly marked `comptime`
parameters, there is a check at the semantic analysis of the `call`
instruction.
Semantic analysis of `call` instructions does type coercion on the
arguments, which is needed both for generic functions and to make up for
using `coerced_ty` result locations (mentioned above).
Tasks left in this branch:
* Implement the memoization table.
* Add test coverage.
* Improve error reporting and source locations for compile errors.
which internally calls `ArrayHashMap.pop`, however, returns `?KV`
instead and performs the bounds checking automatically.
This function correponds to `ArrayList.popOrNull` and is meant
to fill the gap for situations where we want the quick lookup offered
by the hash map with elegant ability to iterate and pop of the
container with automatic bound checking that plugs in well with
a `while`-loop such as
```zig
var map = std.ArrayHashMap(K, V).init(allocator);
map.deinit();
while (map.popOrNull()) |entry| {
// ... do something
}
assert(map.count() == 0);
```
The general strategy is that Sema will pre-map comptime arguments into
the inst_map, and then re-run the block body that contains the `param`
and `func` instructions. This re-runs all the parameter type expressions
except with comptime values populated.
In Sema, param instructions are now handled specially: they detect
whether they are comptime-elided or not. If so, they skip putting a
value in the inst_map, since it is already pre-populated. If not, then
they append to the `fields` field of `Sema` for use with the `func`
instruction.
So when the block body is re-run, a new function is generated with
all the comptime arguments elided, and the new function type has only
runtime parameters in it. TODO: give the generated Decls better names
than "foo__anon_x".
The new function is then added to the work queue to have its body
analyzed and a runtime call AIR instruction to the new function is
emitted.
When the new function gets semantically analyzed, comptime parameters are
pre-mapped to the corresponding `comptime_args` values rather than
mapped to an `arg` AIR instruction. `comptime_args` is a new field that
`Fn` has which is a `TypedValue` for each parameter. This field is non-null
for generic function instantiations only. The values are the comptime
arguments. For non-comptime parameters, a sentinel value is used. This is
because we need to know the information of which parameters are
comptime-known.
Additionally:
* AstGen: align and section expressions are evaluated in the scope that
has comptime parameters in it.
There are still some TODO items left; see the BRANCH_TODO file.
* ZIR function instructions encode the index of the block that
contains the function instruction. This allows Zig to later scan the
block and find the parameter instructions, which is needed for
semantically analyzing function bodies.
* Runtime function calls insert AIR arg instructions and then inserts
Sema inst_map entries mapping the ZIR param instructions to them.
* comptime/inline function call inserts Sema inst_map entries mapping
the ZIR param instructions to the AIR callsite arguments.
With this commit we are back to the tests passing.
#8589 introduced correct handling of signed (possibly negative) array access
of pointers. Since unadorned integer literals in C are signed, this resulted
in inefficient generated code when indexing a pointer by a non-negative
integer literal.
ZIR encoding for functions is changed in preparation for generic
function support. As an example:
```zig
const std = @import("std");
const expect = std.testing.expect;
test "example" {
var x: usize = 0;
x += checkSize(i32, 1);
x += checkSize(bool, true);
try expect(x == 5);
}
fn checkSize(comptime T: type, x: T) usize {
_ = x;
return @sizeOf(T);
}
```
Previous ZIR for the `checkSize` function:
```zir
[165] checkSize line(10) hash(0226f62e189fd0b1c5fca02cf4617562): %55 = block_inline({
%56 = decl_val("T") token_offset:11:35
%57 = as_node(@Ref.type_type, %56) node_offset:11:35
%69 = extended(func([comptime @Ref.type_type, %57], @Ref.usize_type, {
%58 = arg("T") token_offset:11:23
%59 = as_node(@Ref.type_type, %58) node_offset:11:35
%60 = arg("x") token_offset:11:32
%61 = dbg_stmt(11, 4)
```
ZIR for the `checkSize` function after this commit:
```zir
[157] checkSize line(10) hash(0226f62e189fd0b1c5fca02cf4617562): %55 = block_inline({
%56 = param_comptime("T", @Ref.type_type) token_offset:11:23
%57 = as_node(@Ref.type_type, %56) node_offset:11:35
%58 = param("x", %57) token_offset:11:32
%67 = func(@Ref.usize_type, {
%59 = dbg_stmt(11, 4)
```
Noted differences:
* Previously the type expression was redundantly repeated.
* Previously the parameter names were redundantly stored in the ZIR
extra array.
* Instead of `arg` ZIR instructions as the first instructions within a
function body, they are now outside the function body, in the same
block as the `func` instruction. There are variants:
- param
- param_comptime
- param_anytype
- param_anytype_comptime
* The param instructions additionally encode the type.
* Because of the param instructions, the `func` instruction no longer
encodes the list of parameter types or the comptime bits.
It's implied that Sema will collect the parameters so that when a `func`
instruction is encountered, they will be implicitly used to construct
the function's type. This is so that we can satisfy all 3 ways of
performing semantic analysis on a function:
1. runtime: Sema will insert AIR arg instructions for each parameter,
and insert into the Sema inst_map ZIR param => AIR arg.
2. comptime/inline: Sema will insert into the inst_map ZIR param =>
callsite arguments.
3. generic: Sema will map *only the comptime* ZIR param instructions to
the AIR instructions for the comptime arguments at the callsite, and
then re-run Sema for the function's Decl. This will produce a new
function which is the monomorphized function.
Additionally:
* AstGen: Update usage of deprecated `ensureCapacity` to
`ensureUnusedCapacity` or `ensureTotalCapacity`.
* Introduce `Type.fnInfo` for getting a bunch of data about a function
type at once, and use it in `analyzeCall`.
This commit starts a branch to implement generic functions in stage2.
Test regressions have not been addressed yet.
Remove some unused debugging machinery such as full printing of the
symtab after symbol resolution. It was there only for the time of
rewriting the linker.