zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 3edaef9e011ac500f66c9ee0ba3ea24be905bcde (tree)
parent b80abf0296de5034ddaf149074fe7de18347bc20
Author: mlugg <mlugg@noreply.codeberg.org>
Date:   Tue, 10 Mar 2026 22:06:05 +0100

Merge pull request 'compiler: rework type resolution' (#31403) from lets-get-typing into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31403

Diffstat:
MCMakeLists.txt | 16++++++++++++----
Mbootstrap.c | 24+++++++++++++++++++++++-
Mbuild.zig | 8++++----
Mci/aarch64-freebsd-debug.sh | 1-
Mci/aarch64-freebsd-release.sh | 1-
Mci/aarch64-linux-debug.sh | 1-
Mci/aarch64-linux-release.sh | 1-
Mci/aarch64-macos-debug.sh | 1-
Mci/aarch64-macos-release.sh | 1-
Mci/aarch64-netbsd-debug.sh | 1-
Mci/aarch64-netbsd-release.sh | 1-
Mci/aarch64-windows.ps1 | 1-
Mci/loongarch64-linux-debug.sh | 1-
Mci/loongarch64-linux-release.sh | 1-
Mci/powerpc64le-linux-debug.sh | 1-
Mci/powerpc64le-linux-release.sh | 1-
Mci/s390x-linux-debug.sh | 1-
Mci/s390x-linux-release.sh | 1-
Mci/x86_64-freebsd-debug.sh | 1-
Mci/x86_64-freebsd-release.sh | 1-
Mci/x86_64-linux-debug-llvm.sh | 1-
Mci/x86_64-linux-debug.sh | 1-
Mci/x86_64-linux-release.sh | 4++--
Mci/x86_64-netbsd-debug.sh | 1-
Mci/x86_64-netbsd-release.sh | 1-
Mci/x86_64-openbsd-debug.sh | 1-
Mci/x86_64-openbsd-release.sh | 1-
Mdoc/langref.html.in | 5+++--
Mdoc/langref/test_comptime_invalid_error_code.zig | 7++-----
Mdoc/langref/test_missized_packed_struct.zig | 2+-
Mdoc/langref/test_variable_alignment.zig | 15++++++++++-----
Mlib/compiler/aro/aro/InitList.zig | 20+++++++++++++-------
Mlib/compiler/aro/aro/Parser.zig | 6+++---
Mlib/compiler/aro/aro/Toolchain.zig | 6+++---
Mlib/compiler/objcopy.zig | 4++--
Mlib/compiler/resinator/cvtres.zig | 2+-
Mlib/compiler/test_runner.zig | 8++++----
Mlib/std/Build/Fuzz.zig | 4++--
Mlib/std/Build/Module.zig | 14+++++++-------
Mlib/std/Build/Step.zig | 2+-
Mlib/std/Build/Step/Run.zig | 8++++----
Mlib/std/Build/Step/UpdateSourceFiles.zig | 2+-
Mlib/std/Build/Step/WriteFile.zig | 4++--
Mlib/std/Io/Dir.zig | 2+-
Mlib/std/array_list.zig | 4++--
Mlib/std/builtin.zig | 12++++++++----
Mlib/std/c/darwin.zig | 2+-
Mlib/std/c/darwin/dispatch.zig | 2+-
Mlib/std/compress/lzma.zig | 2+-
Mlib/std/compress/lzma2.zig | 2+-
Mlib/std/debug/Coverage.zig | 6+++---
Mlib/std/elf.zig | 4++--
Mlib/std/hash_map.zig | 6+++---
Mlib/std/macho.zig | 2+-
Mlib/std/math/big/int.zig | 14++++++++++++--
Mlib/std/mem.zig | 16++++++++++++----
Mlib/std/mem/Allocator.zig | 99+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mlib/std/meta.zig | 4++--
Mlib/std/multi_array_list.zig | 24+++++++++++++++---------
Mlib/std/os/linux.zig | 2+-
Mlib/std/os/windows.zig | 10+++++-----
Mlib/std/pdb.zig | 4++--
Mlib/std/testing.zig | 5++---
Mlib/std/zig.zig | 13+++++++++++--
Mlib/std/zig/Ast.zig | 8++++----
Mlib/std/zig/AstGen.zig | 1632++++++++++++++++++++++++++-----------------------------------------------------
Mlib/std/zig/ErrorBundle.zig | 16++++++++++------
Mlib/std/zig/Zir.zig | 950+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mlib/std/zig/llvm/BitcodeReader.zig | 10+++++-----
Mlib/std/zig/llvm/Builder.zig | 364++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mlib/std/zig/target.zig | 17+++++++++--------
Mlib/std/zon/Serializer.zig | 4+++-
Mlib/std/zon/parse.zig | 6+++---
Mlib/zig.h | 10+++++++++-
Msrc/Air.zig | 13+++++--------
Msrc/Air/Liveness.zig | 10+++++-----
Msrc/Air/Liveness/Verify.zig | 2+-
Msrc/Air/print.zig | 44+++++++++++++++++---------------------------
Dsrc/Air/types_resolved.zig | 536-------------------------------------------------------------------------------
Msrc/Compilation.zig | 775++++++-------------------------------------------------------------------------
Msrc/IncrementalDebugServer.zig | 14++++++--------
Msrc/InternPool.zig | 5505++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/Package/Manifest.zig | 6+++---
Msrc/Sema.zig | 11216+++++++++++++++++++++++++++++--------------------------------------------------
Msrc/Sema/LowerZon.zig | 132+++++++++++++++++++++++++++++++++++++------------------------------------------
Msrc/Sema/arith.zig | 42+++++++++++++++++++++++-------------------
Msrc/Sema/bitcast.zig | 184++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/Sema/comptime_ptr_access.zig | 38+++++++++++++++++++-------------------
Asrc/Sema/type_resolution.zig | 1398+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Type.zig | 4060++++++++++++++++++++++++++++++++-----------------------------------------------
Msrc/Value.zig | 1192++++++++++++++++++++-----------------------------------------------------------
Msrc/Zcu.zig | 1148+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/Zcu/PerThread.zig | 2169+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/codegen.zig | 154+++++++++++++++++++++++++++++--------------------------------------------------
Msrc/codegen/aarch64/Select.zig | 151+++++++++++++++++++++++++++++++++----------------------------------------------
Msrc/codegen/aarch64/abi.zig | 2+-
Msrc/codegen/arm/abi.zig | 24+++++++++++++-----------
Msrc/codegen/c.zig | 5580++++++++++++++++++++++++++++++++++---------------------------------------------
Dsrc/codegen/c/Type.zig | 3472-------------------------------------------------------------------------------
Asrc/codegen/c/type.zig | 1023+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/codegen/c/type/render_defs.zig | 710+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/codegen/llvm.zig | 2062+++++++++++++++++++++++++++++++++++--------------------------------------------
Msrc/codegen/mips/abi.zig | 4++--
Msrc/codegen/riscv64/CodeGen.zig | 103+++++++++++++++++++++++++++++++++++--------------------------------------------
Msrc/codegen/riscv64/abi.zig | 4++--
Msrc/codegen/sparc64/CodeGen.zig | 22+++++++++++-----------
Msrc/codegen/spirv/CodeGen.zig | 206++++++++++++++++++++++++++++++++-----------------------------------------------
Msrc/codegen/wasm/CodeGen.zig | 228+++++++++++++++++++++++++++----------------------------------------------------
Msrc/codegen/wasm/abi.zig | 6+++---
Msrc/codegen/x86_64/CodeGen.zig | 166++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/codegen/x86_64/abi.zig | 12++++++------
Msrc/link.zig | 53++++++++++++++++++++++++++++++++++++++++-------------
Msrc/link/C.zig | 1899+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/link/Coff.zig | 2+-
Asrc/link/ConstPool.zig | 288+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/link/Dwarf.zig | 1746+++++++++++++++++++++++++++++++++++++------------------------------------------
Msrc/link/Elf.zig | 14++------------
Msrc/link/Elf/Object.zig | 2+-
Msrc/link/Elf/ZigObject.zig | 11++++++-----
Msrc/link/Elf2.zig | 2+-
Msrc/link/MachO/Atom.zig | 2+-
Msrc/link/MachO/ZigObject.zig | 14+++++++-------
Msrc/link/MachO/file.zig | 2+-
Msrc/link/Wasm.zig | 8++++----
Msrc/link/Wasm/Flush.zig | 6+++---
Msrc/link/tapi/parse.zig | 2+-
Msrc/main.zig | 18+++++++++---------
Msrc/mutable_value.zig | 44++++++++++++++++++--------------------------
Msrc/print_value.zig | 121+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/print_zir.zig | 541+++++++++++++++++--------------------------------------------------------------
Mstage1/zig.h | 10+++++++++-
Mstage1/zig1.wasm | 0
Mtest/behavior.zig | 1-
Mtest/behavior/align.zig | 57+++++++++++++++++++++++++++++++++++++++++++++++----------
Mtest/behavior/alignof.zig | 5+++++
Mtest/behavior/array.zig | 22----------------------
Mtest/behavior/bitcast.zig | 32--------------------------------
Mtest/behavior/call.zig | 11+++++------
Dtest/behavior/empty_union.zig | 66------------------------------------------------------------------
Mtest/behavior/enum.zig | 54++++++++++++++++++++++++++++++++++++++----------------
Mtest/behavior/error.zig | 33+++++++++++++++++++++++++++++++++
Mtest/behavior/eval.zig | 121-------------------------------------------------------------------------------
Mtest/behavior/generics.zig | 5+----
Mtest/behavior/packed-struct.zig | 100+------------------------------------------------------------------------------
Mtest/behavior/packed-union.zig | 26++++++++++++++++++--------
Mtest/behavior/sizeof_and_typeof.zig | 42+-----------------------------------------
Mtest/behavior/slice.zig | 2+-
Mtest/behavior/struct.zig | 74+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mtest/behavior/struct_contains_slice_of_itself.zig | 2+-
Mtest/behavior/switch.zig | 11+++++------
Mtest/behavior/tuple.zig | 11+++++++++++
Mtest/behavior/tuple_declarations.zig | 4++--
Mtest/behavior/type.zig | 4++--
Mtest/behavior/type_info.zig | 16++++++++--------
Mtest/behavior/union.zig | 73++++++++++++++++++++-----------------------------------------------------
Mtest/c_abi/main.zig | 4++--
Mtest/cases/compile_errors/@import_zon_bad_type.zig | 8++++----
Mtest/cases/compile_errors/@import_zon_opt_in_err.zig | 20++++++++++----------
Mtest/cases/compile_errors/@import_zon_opt_in_err_struct.zig | 8++++----
Dtest/cases/compile_errors/@intFromPtr_with_bad_type.zig | 9---------
Dtest/cases/compile_errors/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig | 12------------
Mtest/cases/compile_errors/aggregate_too_large.zig | 16+++++++---------
Mtest/cases/compile_errors/alignOf_bad_type.zig | 10++++++++--
Mtest/cases/compile_errors/align_zero.zig | 8++++----
Dtest/cases/compile_errors/assign_inline_fn_to_non-comptime_var.zig | 10----------
Mtest/cases/compile_errors/bit_ptr_non_packed.zig | 8++++++--
Mtest/cases/compile_errors/bitsize_of_packed_struct_checks_backing_int_ty.zig | 4+++-
Dtest/cases/compile_errors/c_pointer_to_void.zig | 9---------
Atest/cases/compile_errors/call_runtime_known_inline_fn_ptr.zig | 11+++++++++++
Mtest/cases/compile_errors/coerce_int_to_float.zig | 12++++++------
Mtest/cases/compile_errors/comptime_var_referenced_by_type.zig | 2+-
Mtest/cases/compile_errors/direct_struct_loop.zig | 2+-
Mtest/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig | 6++++--
Atest/cases/compile_errors/empty_extern_union.zig | 8++++++++
Atest/cases/compile_errors/empty_packed_union.zig | 8++++++++
Atest/cases/compile_errors/enum_backed_by_comptime_int.zig | 8++++++++
Dtest/cases/compile_errors/enum_backed_by_comptime_int_must_be_casted_from_comptime_value.zig | 12------------
Dtest/cases/compile_errors/enum_backed_by_comptime_int_must_be_comptime.zig | 9---------
Mtest/cases/compile_errors/enum_field_value_references_enum.zig | 12++++--------
Mtest/cases/compile_errors/enum_field_value_references_nonexistent_circular.zig | 2+-
Atest/cases/compile_errors/enum_uses_own_typeinfo.zig | 15+++++++++++++++
Mtest/cases/compile_errors/enum_value_already_taken.zig | 4++--
Mtest/cases/compile_errors/error_set_membership.zig | 3++-
Mtest/cases/compile_errors/exported_enum_without_explicit_integer_tag_type.zig | 4++--
Dtest/cases/compile_errors/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig | 45---------------------------------------------
Mtest/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig | 4++--
Rtest/cases/compile_errors/AstGen_comptime_known_struct_is_resolved_before_error.zig -> test/cases/compile_errors/fn_body_in_struct_runtime_known.zig | 0
Atest/cases/compile_errors/fn_type_returning_pointer_to_itself.zig | 8++++++++
Mtest/cases/compile_errors/function_ptr_alignment.zig | 2+-
Mtest/cases/compile_errors/function_with_non-extern_non-packed_enum_parameter.zig | 4++--
Mtest/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig | 2+-
Mtest/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig | 2+-
Mtest/cases/compile_errors/generic_function_returning_opaque_type.zig | 2+-
Atest/cases/compile_errors/implicit_backing_type_in_extern_context.zig | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/cases/compile_errors/indexing_an_array_of_size_zero.zig | 2+-
Mtest/cases/compile_errors/indexing_an_array_of_size_zero_with_runtime_index.zig | 2+-
Mtest/cases/compile_errors/indirect_struct_loop.zig | 6+++++-
Atest/cases/compile_errors/initialize_empty_union.zig | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/cases/compile_errors/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig | 2+-
Mtest/cases/compile_errors/instantiating_an_undefined_value_for_an_invalid_union_that_contains_itself.zig | 2+-
Mtest/cases/compile_errors/invalid_dependency_on_struct_size.zig | 22++++++++++++----------
Mtest/cases/compile_errors/invalid_optional_type_in_extern_struct.zig | 4++--
Mtest/cases/compile_errors/invalid_pointer_arithmetic.zig | 8+-------
Mtest/cases/compile_errors/invalid_type_in_builtin_extern.zig | 14++++++++++----
Mtest/cases/compile_errors/non-const_variables_of_things_that_require_const_variables.zig | 41++++++++++++++++++-----------------------
Mtest/cases/compile_errors/non-exhaustive_enum_marker_assigned_a_value.zig | 11-----------
Atest/cases/compile_errors/non-exhaustive_enum_missing_tag_type.zig | 10++++++++++
Mtest/cases/compile_errors/non-exhaustive_enum_specifies_every_value.zig | 2+-
Mtest/cases/compile_errors/non-inline_for_loop_on_a_type_that_requires_comptime.zig | 2+-
Mtest/cases/compile_errors/non_constant_expression_in_array_size.zig | 2+-
Dtest/cases/compile_errors/noreturn_struct_field.zig | 10----------
Mtest/cases/compile_errors/old_fn_ptr_in_extern_context.zig | 6------
Mtest/cases/compile_errors/overflow_in_enum_value_allocation.zig | 2+-
Mtest/cases/compile_errors/packed_struct_backing_int_wrong.zig | 8++++++--
Atest/cases/compile_errors/packed_struct_uses_own_size.zig | 10++++++++++
Atest/cases/compile_errors/packed_struct_uses_own_typeinfo.zig | 13+++++++++++++
Mtest/cases/compile_errors/packed_struct_with_fields_of_not_allowed_types.zig | 35+++++++++++++++++++++++------------
Mtest/cases/compile_errors/packed_union_fields_mismatch.zig | 10++++++----
Dtest/cases/compile_errors/packed_union_given_enum_tag_type.zig | 18------------------
Dtest/cases/compile_errors/packed_union_with_automatic_layout_field.zig | 18------------------
Atest/cases/compile_errors/packed_union_with_fields_of_not_allowed_types.zig | 21+++++++++++++++++++++
Atest/cases/compile_errors/pointer_in_bitpack.zig | 22++++++++++++++++++++++
Mtest/cases/compile_errors/reify_enum_with_duplicate_field.zig | 7++++---
Mtest/cases/compile_errors/reify_enum_with_duplicate_tag_value.zig | 7++++---
Mtest/cases/compile_errors/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig | 2+-
Dtest/cases/compile_errors/reify_type_for_tagged_packed_union.zig | 11-----------
Mtest/cases/compile_errors/reify_type_for_tagged_union_with_extra_enum_field.zig | 5++---
Mtest/cases/compile_errors/reify_type_for_tagged_union_with_no_union_fields.zig | 6++----
Mtest/cases/compile_errors/reify_type_for_union_with_opaque_field.zig | 8+++++---
Mtest/cases/compile_errors/reify_type_with_invalid_field_alignment.zig | 8++++++--
Mtest/cases/compile_errors/resolve_inferred_error_set_of_generic_fn.zig | 3+--
Mtest/cases/compile_errors/runtime_@ptrFromInt_to_comptime_only_type.zig | 5++---
Mtest/cases/compile_errors/runtime_index_into_comptime_only_many_ptr.zig | 6+++---
Mtest/cases/compile_errors/runtime_index_into_comptime_type_slice.zig | 3+--
Mtest/cases/compile_errors/runtime_indexing_comptime_array.zig | 6+++---
Mtest/cases/compile_errors/runtime_operation_in_comptime_scope.zig | 8++++----
Mtest/cases/compile_errors/self_referential_struct_requires_comptime.zig | 1-
Mtest/cases/compile_errors/self_referential_union_requires_comptime.zig | 1-
Atest/cases/compile_errors/simple_struct_loop.zig | 16++++++++++++++++
Mtest/cases/compile_errors/sizeOf_bad_type.zig | 28++++++++++++++++++++++++++--
Atest/cases/compile_errors/sizeof_alignof_empty_union.zig | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/cases/compile_errors/slice_used_as_extern_fn_param.zig | 2+-
Mtest/cases/compile_errors/specify_enum_tag_type_that_is_too_small.zig | 18+++++++++---------
Mtest/cases/compile_errors/store_comptime_only_type_to_runtime_pointer.zig | 11++---------
Mtest/cases/compile_errors/struct_depends_on_itself_via_non_initial_field.zig | 4++--
Mtest/cases/compile_errors/struct_depends_on_itself_via_optional_field.zig | 5++++-
Dtest/cases/compile_errors/struct_depends_on_pointer_alignment.zig | 11-----------
Atest/cases/compile_errors/struct_field_queries_hasfield_of_itself.zig | 14++++++++++++++
Atest/cases/compile_errors/struct_uses_reified_type_which_queries_struct_alignment.zig | 13+++++++++++++
Atest/cases/compile_errors/struct_uses_sizeof_self_as_array_len.zig | 10++++++++++
Mtest/cases/compile_errors/too_big_packed_struct.zig | 2+-
Mtest/cases/compile_errors/top_level_decl_dependency_loop.zig | 7++++++-
Mtest/cases/compile_errors/unable_to_evaluate_comptime_expr.zig | 19-------------------
Mtest/cases/compile_errors/undef_arith_is_illegal.zig | 3016++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mtest/cases/compile_errors/undef_arith_returns_undef.zig | 3588++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mtest/cases/compile_errors/undef_shifts_are_illegal.zig | 1174++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mtest/cases/compile_errors/union_auto-enum_value_already_taken.zig | 4++--
Atest/cases/compile_errors/union_backed_by_enum_backed_by_comptime_int.zig | 9+++++++++
Dtest/cases/compile_errors/union_depends_on_pointer_alignment.zig | 11-----------
Mtest/cases/compile_errors/union_enum_field_missing.zig | 5++---
Mtest/cases/compile_errors/union_field_ordered_differently_than_enum.zig | 7+++----
Mtest/cases/compile_errors/union_noreturn_field_initialized.zig | 12++++++------
Mtest/cases/compile_errors/union_with_specified_enum_omits_field.zig | 5++---
Mtest/cases/compile_errors/union_with_too_small_explicit_signed_tag_type.zig | 3+--
Mtest/cases/compile_errors/union_with_too_small_explicit_unsigned_tag_type.zig | 3+--
Mtest/cases/compile_errors/untagged_union_integer_conversion.zig | 2+-
Mtest/cases/compile_errors/variadic_arg_validation.zig | 2+-
Mtest/cases/compile_errors/zero_width_nonexhaustive_enum.zig | 15+++++++++------
Mtest/incremental/change_enum_tag_type | 2+-
Atest/incremental/type_dependency_loop | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtools/incr-check.zig | 95++++++++++++++++++++++++++++++++++++++++++-------------------------------------
271 files changed, 26975 insertions(+), 33509 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -330,7 +330,6 @@ set(ZIG_STAGE2_SOURCES src/Air/Liveness.zig src/Air/Liveness/Verify.zig src/Air/print.zig - src/Air/types_resolved.zig src/Builtin.zig src/Compilation.zig src/Compilation/Config.zig @@ -344,6 +343,7 @@ set(ZIG_STAGE2_SOURCES src/Sema.zig src/Sema/bitcast.zig src/Sema/comptime_ptr_access.zig + src/Sema/type_resolution.zig src/Type.zig src/Value.zig src/Zcu.zig @@ -360,7 +360,8 @@ set(ZIG_STAGE2_SOURCES src/codegen/aarch64/Mir.zig src/codegen/aarch64/Select.zig src/codegen/c.zig - src/codegen/c/Type.zig + src/codegen/c/type.zig + src/codegen/c/type/render_defs.zig src/codegen/llvm.zig src/codegen/llvm/bindings.zig src/crash_report.zig @@ -375,6 +376,7 @@ set(ZIG_STAGE2_SOURCES src/libs/libunwind.zig src/link.zig src/link/C.zig + src/link/ConstPool.zig src/link/Coff.zig src/link/Dwarf.zig src/link/Elf.zig @@ -606,8 +608,8 @@ if(MSVC) set(ZIG2_LINK_FLAGS "/STACK:16777216 /FORCE:MULTIPLE") else() set(ZIG_WASM2C_COMPILE_FLAGS "-std=c99 -O2") - set(ZIG1_COMPILE_FLAGS "-std=c99 -Os") - set(ZIG2_COMPILE_FLAGS "-std=c99 -O0 -fno-sanitize=undefined -fno-stack-protector") + set(ZIG1_COMPILE_FLAGS "-std=c99 -Os -fno-strict-aliasing") + set(ZIG2_COMPILE_FLAGS "-std=c99 -O0 -fno-sanitize=undefined -fno-stack-protector -fno-strict-aliasing") # Must match the condition in build.zig. if(ZIG_HOST_TARGET_ARCH MATCHES "^(arm|thumb)(eb)?$" OR ZIG_HOST_TARGET_ARCH MATCHES "^powerpc(64)?(le)?$") set(ZIG1_COMPILE_FLAGS "${ZIG1_COMPILE_FLAGS} -ffunction-sections -fdata-sections") @@ -623,6 +625,12 @@ else() else() set(ZIG2_LINK_FLAGS "-Wl,-z,stack-size=0x10000000") endif() + # Prevent GCC from miscompiling 'zig2.c'. See also 'workaround_gcc_sra_miscomp' in 'bootstrap.c'. + if (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND + CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "13.0" AND + CMAKE_C_COMPILER_VERSION VERSION_LESS_EQUAL "15.2") + set(ZIG2_COMPILE_FLAGS "${ZIG2_COMPILE_FLAGS} -fno-tree-sra") + endif() endif() set(ZIG1_WASM_MODULE "${PROJECT_SOURCE_DIR}/stage1/zig1.wasm") diff --git a/bootstrap.c b/bootstrap.c @@ -102,6 +102,26 @@ int main(int argc, char **argv) { const char *cc = get_c_compiler(); const char *host_triple = get_host_triple(); + // GCC versions 13.0--14.1 have a miscompilation where some bytes of a union may get clobbered + // depending on the union layout and the order in which types are defined. This miscompilation + // affects the output of the C backend, and thus can affect the bootstrap process. Specifically, + // we observe that using the self-hosted x86_64 backend in 'zig2' will cause all function calls + // to be relocated incorrectly, causing immediate crashes on any binary produced by it. + // + // The only reliable workaround for this bug is to disable the optimization pass containing it, + // so here we check for a CLI flag requesting that workaround. + // + // The upstream bug is fixed in GCC version 15.2 onwards (and was also backported to the 13 and + // 14 branches). Once this bug is no longer widespread, we can remove this CLI flag. + // + // Upstream bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119085 + bool workaround_gcc_sra_miscomp = false; + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--workaround-gcc-sra-miscomp")) { + workaround_gcc_sra_miscomp = true; + } + } + { const char *child_argv[] = { cc, "-o", "zig-wasm2c", "stage1/wasm2c.c", "-O2", "-std=c99", NULL, @@ -116,7 +136,7 @@ int main(int argc, char **argv) { } { const char *child_argv[] = { - cc, "-o", "zig1", "zig1.c", "stage1/wasi.c", "-std=c99", "-Os", "-lm", NULL, + cc, "-o", "zig1", "zig1.c", "stage1/wasi.c", "-std=c99", "-Os", "-fno-strict-aliasing", "-lm", NULL, }; print_and_run(child_argv); } @@ -193,6 +213,8 @@ int main(int argc, char **argv) { #if defined(__GNUC__) "-pthread", #endif + "-fno-strict-aliasing", + workaround_gcc_sra_miscomp ? "-fno-tree-sra" : NULL, NULL, }; print_and_run(child_argv); diff --git a/build.zig b/build.zig @@ -568,7 +568,7 @@ pub fn build(b: *std.Build) !void { .skip_linux = skip_linux, .skip_llvm = skip_llvm, .skip_libc = skip_libc, - .max_rss = 8_500_000_000, + .max_rss = 9_300_000_000, })); const unit_tests_step = b.step("test-unit", "Run the compiler source unit tests"); @@ -584,7 +584,7 @@ pub fn build(b: *std.Build) !void { .use_llvm = use_llvm, .use_lld = use_llvm, .zig_lib_dir = b.path("lib"), - .max_rss = 2_500_000_000, + .max_rss = 2_700_000_000, }); if (link_libc) { unit_tests.root_module.link_libc = true; @@ -611,7 +611,7 @@ pub fn build(b: *std.Build) !void { .skip_linux = skip_linux, .skip_llvm = skip_llvm, .skip_release = skip_release, - .max_rss = 3_000_000_000, + .max_rss = 3_300_000_000, })); test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, enable_ios_sdk, enable_symlinks_windows)); test_step.dependOn(tests.addStackTraceTests(b, test_filters, skip_non_native)); @@ -767,7 +767,7 @@ fn addCompilerMod(b: *std.Build, options: AddCompilerModOptions) *std.Build.Modu fn addCompilerStep(b: *std.Build, options: AddCompilerModOptions) *std.Build.Step.Compile { const exe = b.addExecutable(.{ .name = "zig", - .max_rss = 7_900_000_000, + .max_rss = 8_700_000_000, .root_module = addCompilerMod(b, options), }); exe.stack_size = stack_size; diff --git a/ci/aarch64-freebsd-debug.sh b/ci/aarch64-freebsd-debug.sh @@ -47,7 +47,6 @@ stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ --test-timeout 2m diff --git a/ci/aarch64-freebsd-release.sh b/ci/aarch64-freebsd-release.sh @@ -47,7 +47,6 @@ stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ --test-timeout 2m diff --git a/ci/aarch64-linux-debug.sh b/ci/aarch64-linux-debug.sh @@ -47,7 +47,6 @@ stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ diff --git a/ci/aarch64-linux-release.sh b/ci/aarch64-linux-release.sh @@ -47,7 +47,6 @@ stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ diff --git a/ci/aarch64-macos-debug.sh b/ci/aarch64-macos-debug.sh @@ -47,7 +47,6 @@ stage3-debug/bin/zig build test docs \ -Denable-macos-sdk \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ --search-prefix "$PREFIX" \ --test-timeout 2m diff --git a/ci/aarch64-macos-release.sh b/ci/aarch64-macos-release.sh @@ -46,7 +46,6 @@ stage3-release/bin/zig build test docs \ -Denable-macos-sdk \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ --search-prefix "$PREFIX" \ --test-timeout 2m diff --git a/ci/aarch64-netbsd-debug.sh b/ci/aarch64-netbsd-debug.sh @@ -47,7 +47,6 @@ stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ --test-timeout 4m diff --git a/ci/aarch64-netbsd-release.sh b/ci/aarch64-netbsd-release.sh @@ -47,7 +47,6 @@ stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ --test-timeout 4m diff --git a/ci/aarch64-windows.ps1 b/ci/aarch64-windows.ps1 @@ -60,7 +60,6 @@ Write-Output "Main test suite..." --search-prefix "$PREFIX_PATH" ` -Dstatic-llvm ` -Dskip-non-native ` - -Dskip-test-incremental ` -Denable-symlinks-windows ` --test-timeout 30m CheckLastExitCode diff --git a/ci/loongarch64-linux-debug.sh b/ci/loongarch64-linux-debug.sh @@ -48,7 +48,6 @@ stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ diff --git a/ci/loongarch64-linux-release.sh b/ci/loongarch64-linux-release.sh @@ -48,7 +48,6 @@ stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ diff --git a/ci/powerpc64le-linux-debug.sh b/ci/powerpc64le-linux-debug.sh @@ -48,7 +48,6 @@ stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ -Dtarget=native-native-musl \ -Dcpu=native+longcall \ --search-prefix "$PREFIX" \ diff --git a/ci/powerpc64le-linux-release.sh b/ci/powerpc64le-linux-release.sh @@ -48,7 +48,6 @@ stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ -Dtarget=native-native-musl \ -Dcpu=native+longcall \ --search-prefix "$PREFIX" \ diff --git a/ci/s390x-linux-debug.sh b/ci/s390x-linux-debug.sh @@ -48,7 +48,6 @@ stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ diff --git a/ci/s390x-linux-release.sh b/ci/s390x-linux-release.sh @@ -48,7 +48,6 @@ stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ diff --git a/ci/x86_64-freebsd-debug.sh b/ci/x86_64-freebsd-debug.sh @@ -53,7 +53,6 @@ stage3-debug/bin/zig build test docs \ -Dskip-openbsd \ -Dskip-windows \ -Dskip-darwin \ - -Dskip-test-incremental \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ --test-timeout 2m diff --git a/ci/x86_64-freebsd-release.sh b/ci/x86_64-freebsd-release.sh @@ -53,7 +53,6 @@ stage3-release/bin/zig build test docs \ -Dskip-openbsd \ -Dskip-windows \ -Dskip-darwin \ - -Dskip-test-incremental \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ --test-timeout 2m diff --git a/ci/x86_64-linux-debug-llvm.sh b/ci/x86_64-linux-debug-llvm.sh @@ -64,7 +64,6 @@ stage3-debug/bin/zig build test docs \ -Dskip-openbsd \ -Dskip-windows \ -Dskip-darwin \ - -Dskip-test-incremental \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ diff --git a/ci/x86_64-linux-debug.sh b/ci/x86_64-linux-debug.sh @@ -63,7 +63,6 @@ stage3-debug/bin/zig build test docs \ -Dskip-windows \ -Dskip-darwin \ -Dskip-llvm \ - -Dskip-test-incremental \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ diff --git a/ci/x86_64-linux-release.sh b/ci/x86_64-linux-release.sh @@ -21,7 +21,8 @@ export ZIG_LOCAL_CACHE_DIR="$PWD/zig-local-cache" # Test building from source without LLVM. cc -o bootstrap bootstrap.c -./bootstrap +# See comments in bootstrap.c for an explanation of the flag given here. +./bootstrap --workaround-gcc-sra-miscomp ./zig2 build -Dno-lib ./zig-out/bin/zig test test/behavior.zig @@ -64,7 +65,6 @@ stage3-release/bin/zig build test docs \ --libc-runtimes $HOME/deps/glibc-2.43-musl-1.2.5 \ -fwasmtime \ -Dstatic-llvm \ - -Dskip-test-incremental \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ diff --git a/ci/x86_64-netbsd-debug.sh b/ci/x86_64-netbsd-debug.sh @@ -47,7 +47,6 @@ stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ --test-timeout 2m diff --git a/ci/x86_64-netbsd-release.sh b/ci/x86_64-netbsd-release.sh @@ -47,7 +47,6 @@ stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ --test-timeout 2m diff --git a/ci/x86_64-openbsd-debug.sh b/ci/x86_64-openbsd-debug.sh @@ -47,7 +47,6 @@ stage3-debug/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ --test-timeout 2m diff --git a/ci/x86_64-openbsd-release.sh b/ci/x86_64-openbsd-release.sh @@ -47,7 +47,6 @@ stage3-release/bin/zig build test docs \ --maxrss ${ZSF_MAX_RSS:-0} \ -Dstatic-llvm \ -Dskip-non-native \ - -Dskip-test-incremental \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ --test-timeout 2m diff --git a/doc/langref.html.in b/doc/langref.html.in @@ -2103,8 +2103,9 @@ or less than {#syntax#}1 << 29{#endsyntax#}. </p> <p> - In Zig, a pointer type has an alignment value. If the value is equal to the - alignment of the underlying type, it can be omitted from the type: + Pointer types may explicitly specify an alignment in bytes. If it is not + specified, the alignment is assumed to be equal to the alignment of the + underlying type. </p> {#code|test_variable_alignment.zig#} diff --git a/doc/langref/test_comptime_invalid_error_code.zig b/doc/langref/test_comptime_invalid_error_code.zig @@ -1,8 +1,5 @@ comptime { - const err = error.AnError; - const number = @intFromError(err) + 10; - const invalid_err = @errorFromInt(number); - _ = invalid_err; + _ = @errorFromInt(12345); } -// test_error=integer value '11' represents no error +// test_error=integer value '12345' represents no error diff --git a/doc/langref/test_missized_packed_struct.zig b/doc/langref/test_missized_packed_struct.zig @@ -3,4 +3,4 @@ test "missized packed struct" { _ = S{ .a = 4, .b = 2 }; } -// test_error=backing integer type 'u32' has bit size 32 but the struct fields have a total bit size of 24 +// test_error=backing integer bit width does not match total bit width of fields diff --git a/doc/langref/test_variable_alignment.zig b/doc/langref/test_variable_alignment.zig @@ -1,15 +1,20 @@ const std = @import("std"); const builtin = @import("builtin"); +const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; test "variable alignment" { var x: i32 = 1234; - const align_of_i32 = @alignOf(@TypeOf(x)); + try expectEqual(*i32, @TypeOf(&x)); - try expectEqual(*align(align_of_i32) i32, *i32); - if (builtin.target.cpu.arch == .x86_64) { - try expectEqual(4, @typeInfo(*i32).pointer.alignment); - } + + try expect(@intFromPtr(&x) % @alignOf(i32) == 0); + + // The implicitly-aligned pointer can be coerced to be explicitly-aligned to + // the alignment of the underlying type `i32`: + const ptr: *align(@alignOf(i32)) i32 = &x; + + try expectEqual(1234, ptr.*); } // test diff --git a/lib/compiler/aro/aro/InitList.zig b/lib/compiler/aro/aro/InitList.zig @@ -22,9 +22,15 @@ const Item = struct { const InitList = @This(); -list: std.ArrayList(Item) = .empty, -node: Node.OptIndex = .null, -tok: TokenIndex = 0, +list: std.ArrayList(Item), +node: Node.OptIndex, +tok: TokenIndex, + +pub const empty: InitList = .{ + .list = .empty, + .node = .null, + .tok = 0, +}; /// Deinitialize freeing all memory. pub fn deinit(il: *InitList, gpa: Allocator) void { @@ -43,7 +49,7 @@ pub fn find(il: *InitList, gpa: Allocator, index: u64) !*InitList { if (il.list.items.len == 0) { const item = try il.list.addOne(gpa); item.* = .{ - .list = .{}, + .list = .empty, .index = index, }; return &item.list; @@ -51,7 +57,7 @@ pub fn find(il: *InitList, gpa: Allocator, index: u64) !*InitList { // Append a new value to the end of the list. const new = try il.list.addOne(gpa); new.* = .{ - .list = .{}, + .list = .empty, .index = index, }; return &new.list; @@ -70,7 +76,7 @@ pub fn find(il: *InitList, gpa: Allocator, index: u64) !*InitList { // Insert a new value into a sorted position. try il.list.insert(gpa, left, .{ - .list = .{}, + .list = .empty, .index = index, }); return &il.list.items[left].list; @@ -78,7 +84,7 @@ pub fn find(il: *InitList, gpa: Allocator, index: u64) !*InitList { test "basic usage" { const gpa = testing.allocator; - var il: InitList = .{}; + var il: InitList = .empty; defer il.deinit(gpa); { diff --git a/lib/compiler/aro/aro/Parser.zig b/lib/compiler/aro/aro/Parser.zig @@ -3977,7 +3977,7 @@ fn initializer(p: *Parser, init_qt: QualType) Error!Result { final_init_qt = .invalid; } - var il: InitList = .{}; + var il: InitList = .empty; defer il.deinit(p.comp.gpa); try p.initializerItem(&il, final_init_qt, l_brace); @@ -4028,12 +4028,12 @@ fn initializerItem(p: *Parser, il: *InitList, init_qt: QualType, l_brace: TokenI try p.err(first_tok, .initializer_overrides, .{}); try p.err(item.il.tok, .previous_initializer, .{}); item.il.deinit(gpa); - item.il.* = .{}; + item.il.* = .empty; } try p.initializerItem(item.il, item.qt, inner_l_brace); } else { // discard further values - var tmp_il: InitList = .{}; + var tmp_il: InitList = .empty; defer tmp_il.deinit(gpa); try p.initializerItem(&tmp_il, .invalid, inner_l_brace); if (!warned_excess) try p.err(first_tok, switch (init_qt.base(p.comp).type) { diff --git a/lib/compiler/aro/aro/Toolchain.zig b/lib/compiler/aro/aro/Toolchain.zig @@ -43,13 +43,13 @@ const Toolchain = @This(); driver: *Driver, /// The list of toolchain specific path prefixes to search for libraries. -library_paths: PathList = .{}, +library_paths: PathList = .empty, /// The list of toolchain specific path prefixes to search for files. -file_paths: PathList = .{}, +file_paths: PathList = .empty, /// The list of toolchain specific path prefixes to search for programs. -program_paths: PathList = .{}, +program_paths: PathList = .empty, selected_multilib: Multilib = .{}, diff --git a/lib/compiler/objcopy.zig b/lib/compiler/objcopy.zig @@ -388,8 +388,8 @@ const BinaryElfOutput = struct { pub fn parse(allocator: Allocator, in: *File.Reader, elf_hdr: elf.Header) !Self { var self: Self = .{ - .segments = .{}, - .sections = .{}, + .segments = .empty, + .sections = .empty, .allocator = allocator, .shstrtab = null, }; diff --git a/lib/compiler/resinator/cvtres.zig b/lib/compiler/resinator/cvtres.zig @@ -410,7 +410,7 @@ pub const ResourceDirectoryTable = extern struct { }; pub const ResourceDirectoryEntry = extern struct { - entry: packed union { + entry: packed union(u32) { name_offset: packed struct(u32) { address: u31, /// This is undocumented in the PE/COFF spec, but the high bit diff --git a/lib/compiler/test_runner.zig b/lib/compiler/test_runner.zig @@ -38,10 +38,10 @@ pub fn main(init: std.process.Init.Minimal) void { } if (need_simple) { - return mainSimple() catch @panic("test failure"); + return mainSimple() catch |err| std.debug.panic("test failure: {t}", .{err}); } - const args = init.args.toSlice(fba.allocator()) catch @panic("unable to parse command line args"); + const args = init.args.toSlice(fba.allocator()) catch |err| std.debug.panic("unable to parse command line args: {t}", .{err}); var listen = false; var opt_cache_dir: ?[]const u8 = null; @@ -55,7 +55,7 @@ pub fn main(init: std.process.Init.Minimal) void { } else if (std.mem.startsWith(u8, arg, "--cache-dir")) { opt_cache_dir = arg["--cache-dir=".len..]; } else { - @panic("unrecognized command line argument"); + std.debug.panic("unrecognized command line argument: {s}", .{arg}); } } @@ -65,7 +65,7 @@ pub fn main(init: std.process.Init.Minimal) void { } if (listen) { - return mainServer(init) catch @panic("internal test runner failure"); + return mainServer(init) catch |err| std.debug.panic("internal test runner failure: {t}", .{err}); } else { return mainTerminal(init); } diff --git a/lib/std/Build/Fuzz.zig b/lib/std/Build/Fuzz.zig @@ -390,7 +390,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO .coverage = std.debug.Coverage.init, .mapped_memory = undefined, // populated below .source_locations = undefined, // populated below - .entry_points = .{}, + .entry_points = .empty, .start_timestamp = ws.now(), .start_n_runs = undefined, // populated below }; @@ -450,7 +450,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO // Unfortunately the PCs array that LLVM gives us from the 8-bit PC // counters feature is not sorted. - var sorted_pcs: std.MultiArrayList(struct { pc: u64, index: u32, sl: Coverage.SourceLocation }) = .{}; + var sorted_pcs: std.MultiArrayList(struct { pc: u64, index: u32, sl: Coverage.SourceLocation }) = .empty; defer sorted_pcs.deinit(gpa); try sorted_pcs.resize(gpa, pcs.len); @memcpy(sorted_pcs.items(.pc), pcs); diff --git a/lib/std/Build/Module.zig b/lib/std/Build/Module.zig @@ -275,18 +275,18 @@ pub fn init( m.* = .{ .owner = owner, .root_source_file = if (options.root_source_file) |lp| lp.dupe(owner) else null, - .import_table = .{}, + .import_table = .empty, .resolved_target = options.target, .optimize = options.optimize, .link_libc = options.link_libc, .link_libcpp = options.link_libcpp, .dwarf_format = options.dwarf_format, - .c_macros = .{}, - .include_dirs = .{}, - .lib_paths = .{}, - .rpaths = .{}, - .frameworks = .{}, - .link_objects = .{}, + .c_macros = .empty, + .include_dirs = .empty, + .lib_paths = .empty, + .rpaths = .empty, + .frameworks = .empty, + .link_objects = .empty, .strip = options.strip, .unwind_tables = options.unwind_tables, .single_threaded = options.single_threaded, diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig @@ -250,7 +250,7 @@ pub fn init(options: StepOptions) Step { const first_ret_addr = options.first_ret_addr orelse @returnAddress(); break :blk std.debug.captureCurrentStackTrace(.{ .first_address = first_ret_addr }, addr_buf); }, - .result_error_msgs = .{}, + .result_error_msgs = .empty, .result_error_bundle = std.zig.ErrorBundle.empty, .result_stderr = "", .result_cached = false, diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig @@ -213,13 +213,13 @@ pub fn create(owner: *std.Build, name: []const u8) *Run { .owner = owner, .makeFn = make, }), - .argv = .{}, + .argv = .empty, .cwd = null, .environ_map = null, .disable_zig_progress = false, .stdio = .infer_from_args, .stdin = .none, - .file_inputs = .{}, + .file_inputs = .empty, .rename_step_with_output_arg = true, .skip_foreign_checks = false, .failing_to_execute_foreign_is_an_error = true, @@ -228,7 +228,7 @@ pub fn create(owner: *std.Build, name: []const u8) *Run { .captured_stderr = null, .dep_output_file = null, .has_side_effects = false, - .fuzz_tests = .{}, + .fuzz_tests = .empty, .rebuilt_executable = null, .producer = null, }; @@ -642,7 +642,7 @@ pub fn addCheck(run: *Run, new_check: StdIo.Check) void { switch (run.stdio) { .infer_from_args => { - run.stdio = .{ .check = .{} }; + run.stdio = .{ .check = .empty }; run.stdio.check.append(b.allocator, new_check) catch @panic("OOM"); }, .check => |*checks| checks.append(b.allocator, new_check) catch @panic("OOM"), diff --git a/lib/std/Build/Step/UpdateSourceFiles.zig b/lib/std/Build/Step/UpdateSourceFiles.zig @@ -35,7 +35,7 @@ pub fn create(owner: *std.Build) *UpdateSourceFiles { .owner = owner, .makeFn = make, }), - .output_source_files = .{}, + .output_source_files = .empty, }; return usf; } diff --git a/lib/std/Build/Step/WriteFile.zig b/lib/std/Build/Step/WriteFile.zig @@ -94,8 +94,8 @@ pub fn create(owner: *std.Build) *WriteFile { .owner = owner, .makeFn = make, }), - .files = .{}, - .directories = .{}, + .files = .empty, + .directories = .empty, .generated_directory = .{ .step = &write_file.step }, }; return write_file; diff --git a/lib/std/Io/Dir.zig b/lib/std/Io/Dir.zig @@ -334,7 +334,7 @@ pub fn walkSelectively(dir: Dir, allocator: Allocator) !SelectiveWalker { return .{ .stack = stack, - .name_buffer = .{}, + .name_buffer = .empty, .allocator = allocator, }; } diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig @@ -582,10 +582,10 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type { /// functions of this ArrayList in accordance with the respective /// documentation. In all cases, "invalidated" means that the memory /// has been passed to an allocator's resize or free function. - items: Slice = &[_]T{}, + items: Slice, /// How many T values this list can hold without allocating /// additional memory. - capacity: usize = 0, + capacity: usize, /// An ArrayList containing no elements. pub const empty: Self = .{ diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig @@ -592,8 +592,8 @@ pub const Type = union(enum) { size: Size, is_const: bool, is_volatile: bool, - /// TODO make this u16 instead of comptime_int - alignment: comptime_int, + /// `null` means implicit alignment, which is equivalent to `@alignOf(child)`. + alignment: ?usize, address_space: AddressSpace, child: type, is_allowzero: bool, @@ -670,7 +670,9 @@ pub const Type = union(enum) { /// See also: `defaultValue`. default_value_ptr: ?*const anyopaque, is_comptime: bool, - alignment: comptime_int, + /// `null` means the field alignment was not explicitly specified. The + /// field will still be aligned to at least `@alignOf` its `type`. + alignment: ?usize, /// Loads the field's default value from `default_value_ptr`. /// Returns `null` if the field has no default value. @@ -747,7 +749,9 @@ pub const Type = union(enum) { pub const UnionField = struct { name: [:0]const u8, type: type, - alignment: comptime_int, + /// `null` means the field alignment was not explicitly specified. The + /// field will still be aligned to at least `@alignOf` its `type`. + alignment: ?usize, /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig @@ -436,7 +436,7 @@ pub const thread_state_flavor_t = c_int; pub const ipc_space_t = mach_port_t; pub const ipc_space_port_t = ipc_space_t; -pub const mach_msg_option_t = packed union { +pub const mach_msg_option_t = packed union(integer_t) { RCV: MACH.RCV, SEND: MACH.SEND, diff --git a/lib/std/c/darwin/dispatch.zig b/lib/std/c/darwin/dispatch.zig @@ -210,7 +210,7 @@ pub const source_timer_flags_t = packed struct(usize) { STRICT: bool = false, unused1: @Int(.unsigned, @bitSizeOf(usize) - 1) = 0, }; -pub const source_flags_t = packed union { +pub const source_flags_t = packed union(usize) { raw: usize, MACH_SEND: source_mach_send_flags_t, MACH_RECV: source_mach_recv_flags_t, diff --git a/lib/std/compress/lzma.zig b/lib/std/compress/lzma.zig @@ -349,7 +349,7 @@ pub const Decode = struct { pub fn init(dict_size: usize, mem_limit: usize) CircularBuffer { return .{ - .buf = .{}, + .buf = .empty, .dict_size = dict_size, .mem_limit = mem_limit, .cursor = 0, diff --git a/lib/std/compress/lzma2.zig b/lib/std/compress/lzma2.zig @@ -16,7 +16,7 @@ pub const AccumBuffer = struct { pub fn init(memlimit: usize) AccumBuffer { return .{ - .buf = .{}, + .buf = .empty, .memlimit = memlimit, .len = 0, }; diff --git a/lib/std/debug/Coverage.zig b/lib/std/debug/Coverage.zig @@ -27,10 +27,10 @@ string_bytes: std.ArrayList(u8), mutex: Io.Mutex, pub const init: Coverage = .{ - .directories = .{}, - .files = .{}, + .directories = .empty, + .files = .empty, .mutex = .init, - .string_bytes = .{}, + .string_bytes = .empty, }; pub const String = enum(u32) { diff --git a/lib/std/elf.zig b/lib/std/elf.zig @@ -1071,7 +1071,7 @@ pub const Elf32 = struct { pub const Shdr = extern struct { name: Word, type: SHT, - flags: packed struct { shf: SHF }, + flags: packed struct(Word) { shf: SHF }, addr: Elf32.Addr, offset: Elf32.Off, size: Word, @@ -1161,7 +1161,7 @@ pub const Elf64 = struct { pub const Shdr = extern struct { name: Word, type: SHT, - flags: packed struct { shf: SHF, unused: Word = 0 }, + flags: packed struct(Xword) { shf: SHF, unused: Word = 0 }, addr: Elf64.Addr, offset: Elf64.Off, size: Xword, diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig @@ -1526,9 +1526,9 @@ pub fn HashMapUnmanaged( } comptime { - if (!builtin.strip_debug_info) _ = switch (builtin.zig_backend) { - .stage2_llvm => &dbHelper, - .stage2_x86_64 => KV, + if (!builtin.strip_debug_info) switch (builtin.zig_backend) { + .stage2_llvm => _ = &dbHelper, + .stage2_x86_64 => _ = @as(KV, undefined), else => {}, }; } diff --git a/lib/std/macho.zig b/lib/std/macho.zig @@ -851,7 +851,7 @@ pub const nlist = extern struct { pub const nlist_64 = extern struct { n_strx: u32, - n_type: packed union { + n_type: packed union(u8) { bits: packed struct(u8) { ext: bool, type: enum(u3) { diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig @@ -924,7 +924,12 @@ pub const Mutable = struct { /// Asserts the result fits in `r`. Upper bound on the number of limbs needed by /// r is `calcTwosCompLimbCount(bit_count)`. pub fn bitReverse(r: *Mutable, a: Const, signedness: Signedness, bit_count: usize) void { - if (bit_count == 0) return; + if (bit_count == 0) { + r.limbs[0] = 0; + r.len = 1; + r.positive = true; + return; + } r.copy(a); @@ -986,7 +991,12 @@ pub const Mutable = struct { /// Asserts the result fits in `r`. Upper bound on the number of limbs needed by /// r is `calcTwosCompLimbCount(8*byte_count)`. pub fn byteSwap(r: *Mutable, a: Const, signedness: Signedness, byte_count: usize) void { - if (byte_count == 0) return; + if (byte_count == 0) { + r.limbs[0] = 0; + r.len = 1; + r.positive = true; + return; + } r.copy(a); const limbs_required = calcTwosCompLimbCount(8 * byte_count); diff --git a/lib/std/mem.zig b/lib/std/mem.zig @@ -38,6 +38,10 @@ pub const Alignment = enum(math.Log2Int(usize)) { return @enumFromInt(@ctz(n)); } + pub fn fromByteUnitsOptional(maybe_n: ?usize) ?Alignment { + return if (maybe_n) |n| .fromByteUnits(n) else null; + } + pub inline fn of(comptime T: type) Alignment { return comptime fromByteUnits(@alignOf(T)); } @@ -2287,8 +2291,8 @@ pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *a ptr.* = @bitCast(@byteSwap(@as(Int, @bitCast(ptr.*)))); } else inline for (std.meta.fields(S)) |f| { switch (@typeInfo(f.type)) { - .@"struct" => byteSwapAllFieldsAligned(f.type, .fromByteUnits(f.alignment), &@field(ptr, f.name)), - .@"union", .array => byteSwapAllFieldsAligned(f.type, .fromByteUnits(f.alignment), &@field(ptr, f.name)), + .@"struct" => byteSwapAllFieldsAligned(f.type, .fromByteUnits(f.alignment orelse @alignOf(f.type)), &@field(ptr, f.name)), + .@"union", .array => byteSwapAllFieldsAligned(f.type, .fromByteUnits(f.alignment orelse @alignOf(f.type)), &@field(ptr, f.name)), .@"enum" => { @field(ptr, f.name) = @enumFromInt(@byteSwap(@intFromEnum(@field(ptr, f.name)))); }, @@ -4330,7 +4334,7 @@ pub fn alignPointerOffset(ptr: anytype, align_to: usize) ?usize { @compileError("expected many item pointer, got " ++ @typeName(T)); // Do nothing if the pointer is already well-aligned. - if (align_to <= info.pointer.alignment) + if (align_to <= info.pointer.alignment orelse @alignOf(info.pointer.child)) return 0; // Calculate the aligned base address with an eye out for overflow. @@ -4388,7 +4392,11 @@ fn CopyPtrAttrs( .@"const" = ptr.is_const, .@"volatile" = ptr.is_volatile, .@"allowzero" = ptr.is_allowzero, - .@"align" = ptr.alignment, + .@"align" = ptr.alignment orelse a: { + // If the new child is aligned differently than the old one, explicitly align the type. + const want = @alignOf(ptr.child); + break :a if (@alignOf(child) == want) null else want; + }, .@"addrspace" = ptr.address_space, }, child, null); } diff --git a/lib/std/mem/Allocator.zig b/lib/std/mem/Allocator.zig @@ -179,7 +179,11 @@ pub fn destroy(self: Allocator, ptr: anytype) void { const T = info.child; if (@sizeOf(T) == 0) return; const non_const_ptr = @as([*]u8, @ptrCast(@constCast(ptr))); - self.rawFree(non_const_ptr[0..@sizeOf(T)], .fromByteUnits(info.alignment), @returnAddress()); + self.rawFree( + non_const_ptr[0..@sizeOf(T)], + .fromByteUnits(info.alignment orelse @alignOf(T)), + @returnAddress(), + ); } /// Allocates an array of `n` items of type `T` and sets all the @@ -266,7 +270,7 @@ pub inline fn allocAdvancedWithRetAddr( n: usize, return_address: usize, ) Error![]align(if (alignment) |a| a.toByteUnits() else @alignOf(T)) T { - const a = comptime (alignment orelse Alignment.of(T)); + const a: Alignment = alignment orelse comptime .of(T); const ptr: [*]align(a.toByteUnits()) T = @ptrCast(try self.allocWithSizeAndAlignment(@sizeOf(T), a, n, return_address)); return ptr[0..n]; } @@ -278,7 +282,7 @@ fn allocWithSizeAndAlignment( n: usize, return_address: usize, ) Error![*]align(alignment.toByteUnits()) u8 { - const byte_count = math.mul(usize, size, n) catch return Error.OutOfMemory; + const byte_count = math.mul(usize, size, n) catch return error.OutOfMemory; return self.allocBytesWithAlignment(alignment, byte_count, return_address); } @@ -293,7 +297,7 @@ fn allocBytesWithAlignment( return @as([*]align(alignment.toByteUnits()) u8, @ptrFromInt(ptr)); } - const byte_ptr = self.rawAlloc(byte_count, alignment, return_address) orelse return Error.OutOfMemory; + const byte_ptr = self.rawAlloc(byte_count, alignment, return_address) orelse return error.OutOfMemory; @memset(byte_ptr[0..byte_count], undefined); return @alignCast(byte_ptr); } @@ -308,9 +312,9 @@ fn allocBytesWithAlignment( /// /// `new_len` may be zero, in which case the allocation is freed. pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool { - const Slice = @typeInfo(@TypeOf(allocation)).pointer; - const T = Slice.child; - const alignment = Slice.alignment; + const slice_info = @typeInfo(@TypeOf(allocation)).pointer; + comptime assert(slice_info.size == .slice); + const T = slice_info.child; if (new_len == 0) { self.free(allocation); return true; @@ -323,7 +327,12 @@ pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool { // on WebAssembly: https://github.com/ziglang/zig/issues/9660 //const new_len_bytes = new_len *| @sizeOf(T); const new_len_bytes = math.mul(usize, @sizeOf(T), new_len) catch return false; - return self.rawResize(old_memory, .fromByteUnits(alignment), new_len_bytes, @returnAddress()); + return self.rawResize( + old_memory, + .fromByteUnits(slice_info.alignment orelse @alignOf(T)), + new_len_bytes, + @returnAddress(), + ); } /// Request to modify the size of an allocation, allowing relocation. @@ -342,14 +351,11 @@ pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool { /// `new_len` may be zero, in which case the allocation is freed. /// /// If the allocation's elements' type is zero bytes sized, `allocation.len` is set to `new_len`. -pub fn remap(self: Allocator, allocation: anytype, new_len: usize) t: { - const Slice = @typeInfo(@TypeOf(allocation)).pointer; - break :t ?[]align(Slice.alignment) Slice.child; -} { - const Slice = @typeInfo(@TypeOf(allocation)).pointer; - const T = Slice.child; - - const alignment = Slice.alignment; +pub fn remap(self: Allocator, allocation: anytype, new_len: usize) ?@TypeOf(allocation) { + const slice_info = @typeInfo(@TypeOf(allocation)).pointer; + comptime assert(slice_info.size == .slice); + const T = slice_info.child; + if (new_len == 0) { self.free(allocation); return allocation[0..0]; @@ -367,9 +373,13 @@ pub fn remap(self: Allocator, allocation: anytype, new_len: usize) t: { // on WebAssembly: https://github.com/ziglang/zig/issues/9660 //const new_len_bytes = new_len *| @sizeOf(T); const new_len_bytes = math.mul(usize, @sizeOf(T), new_len) catch return null; - const new_ptr = self.rawRemap(old_memory, .fromByteUnits(alignment), new_len_bytes, @returnAddress()) orelse return null; - const new_memory: []align(alignment) u8 = @alignCast(new_ptr[0..new_len_bytes]); - return mem.bytesAsSlice(T, new_memory); + const new_ptr = self.rawRemap( + old_memory, + .fromByteUnits(slice_info.alignment orelse @alignOf(T)), + new_len_bytes, + @returnAddress(), + ) orelse return null; + return @ptrCast(@alignCast(new_ptr[0..new_len_bytes])); } /// This function requests a new size for an existing allocation, which @@ -386,10 +396,7 @@ pub fn remap(self: Allocator, allocation: anytype, new_len: usize) t: { /// do the realloc more efficiently than the caller /// * `resize` which returns `false` when the `Allocator` implementation cannot /// change the size without relocating the allocation. -pub fn realloc(self: Allocator, old_mem: anytype, new_n: usize) t: { - const Slice = @typeInfo(@TypeOf(old_mem)).pointer; - break :t Error![]align(Slice.alignment) Slice.child; -} { +pub fn realloc(self: Allocator, old_mem: anytype, new_n: usize) Error!@TypeOf(old_mem) { return self.reallocAdvanced(old_mem, new_n, @returnAddress()); } @@ -398,51 +405,49 @@ pub fn reallocAdvanced( old_mem: anytype, new_n: usize, return_address: usize, -) t: { - const Slice = @typeInfo(@TypeOf(old_mem)).pointer; - break :t Error![]align(Slice.alignment) Slice.child; -} { - const Slice = @typeInfo(@TypeOf(old_mem)).pointer; - const T = Slice.child; +) Error!@TypeOf(old_mem) { + const slice_info = @typeInfo(@TypeOf(old_mem)).pointer; + comptime assert(slice_info.size == .slice); + const T = slice_info.child; if (old_mem.len == 0) { - return self.allocAdvancedWithRetAddr(T, .fromByteUnits(Slice.alignment), new_n, return_address); + return self.allocAdvancedWithRetAddr(T, .fromByteUnitsOptional(slice_info.alignment), new_n, return_address); } if (new_n == 0) { self.free(old_mem); - const ptr = comptime std.mem.alignBackward(usize, math.maxInt(usize), Slice.alignment); - return @as([*]align(Slice.alignment) T, @ptrFromInt(ptr))[0..0]; + const alignment = slice_info.alignment orelse @alignOf(T); + const addr = comptime std.mem.alignBackward(usize, math.maxInt(usize), alignment); + const ptr: *align(alignment) [0]T = @ptrFromInt(addr); + return ptr; } const old_byte_slice = mem.sliceAsBytes(old_mem); - const byte_count = math.mul(usize, @sizeOf(T), new_n) catch return Error.OutOfMemory; + const byte_count = math.mul(usize, @sizeOf(T), new_n) catch return error.OutOfMemory; // Note: can't set shrunk memory to undefined as memory shouldn't be modified on realloc failure - if (self.rawRemap(old_byte_slice, .fromByteUnits(Slice.alignment), byte_count, return_address)) |p| { - const new_bytes: []align(Slice.alignment) u8 = @alignCast(p[0..byte_count]); - return mem.bytesAsSlice(T, new_bytes); + if (self.rawRemap(old_byte_slice, .fromByteUnits(slice_info.alignment orelse @alignOf(T)), byte_count, return_address)) |p| { + return @ptrCast(@alignCast(p[0..byte_count])); } - const new_mem = self.rawAlloc(byte_count, .fromByteUnits(Slice.alignment), return_address) orelse + const new_mem = self.rawAlloc(byte_count, .fromByteUnits(slice_info.alignment orelse @alignOf(T)), return_address) orelse return error.OutOfMemory; const copy_len = @min(byte_count, old_byte_slice.len); @memcpy(new_mem[0..copy_len], old_byte_slice[0..copy_len]); @memset(old_byte_slice, undefined); - self.rawFree(old_byte_slice, .fromByteUnits(Slice.alignment), return_address); + self.rawFree(old_byte_slice, .fromByteUnits(slice_info.alignment orelse @alignOf(T)), return_address); - const new_bytes: []align(Slice.alignment) u8 = @alignCast(new_mem[0..byte_count]); - return mem.bytesAsSlice(T, new_bytes); + return @ptrCast(@alignCast(new_mem[0..byte_count])); } /// Free an array allocated with `alloc`. /// If memory has length 0, free is a no-op. /// To free a single item, see `destroy`. pub fn free(self: Allocator, memory: anytype) void { - const Slice = @typeInfo(@TypeOf(memory)).pointer; - const bytes = mem.sliceAsBytes(memory); - const bytes_len = bytes.len + if (Slice.sentinel() != null) @sizeOf(Slice.child) else 0; - if (bytes_len == 0) return; - const non_const_ptr = @constCast(bytes.ptr); - @memset(non_const_ptr[0..bytes_len], undefined); - self.rawFree(non_const_ptr[0..bytes_len], .fromByteUnits(Slice.alignment), @returnAddress()); + const slice_info = @typeInfo(@TypeOf(memory)).pointer; + comptime assert(slice_info.size == .slice); + const mem_with_sent = memory[0 .. memory.len + @intFromBool(slice_info.sentinel() != null)]; + const bytes: []u8 = @ptrCast(@constCast(mem_with_sent)); + if (bytes.len == 0) return; + @memset(bytes, undefined); + self.rawFree(bytes, .fromByteUnits(slice_info.alignment orelse @alignOf(slice_info.child)), @returnAddress()); } /// Copies `m` to newly allocated memory. Caller owns the memory. diff --git a/lib/std/meta.zig b/lib/std/meta.zig @@ -63,7 +63,7 @@ pub fn alignment(comptime T: type) comptime_int { .pointer, .@"fn" => alignment(info.child), else => @alignOf(T), }, - .pointer => |info| info.alignment, + .pointer => |info| info.alignment orelse @alignOf(info.child), else => @alignOf(T), }; } @@ -315,7 +315,7 @@ test declarationInfo { try testing.expect(comptime mem.eql(u8, info.name, "a")); } } -pub fn fields(comptime T: type) switch (@typeInfo(T)) { +pub inline fn fields(comptime T: type) switch (@typeInfo(T)) { .@"struct" => []const Type.StructField, .@"union" => []const Type.UnionField, .@"enum" => []const Type.EnumField, diff --git a/lib/std/multi_array_list.zig b/lib/std/multi_array_list.zig @@ -19,7 +19,11 @@ const testing = std.testing; /// For unions you can call `.items(.tags)` or `.items(.data)`. pub fn MultiArrayList(comptime T: type) type { return struct { - bytes: [*]align(@alignOf(T)) u8 = undefined, + /// This pointer is always aligned to the boundary `sizes.big_align`; this is not specified + /// in the type to avoid `MultiArrayList(T)` depending on the alignment of `T` because this + /// can lead to dependency loops. See `allocatedBytes` which `@alignCast`s this pointer to + /// the correct type. + bytes: [*]u8 = undefined, len: usize = 0, capacity: usize = 0, @@ -133,10 +137,8 @@ pub fn MultiArrayList(comptime T: type) type { if (self.ptrs.len == 0 or self.capacity == 0) { return .{}; } - const unaligned_ptr = self.ptrs[sizes.fields[0]]; - const aligned_ptr: [*]align(@alignOf(Elem)) u8 = @alignCast(unaligned_ptr); return .{ - .bytes = aligned_ptr, + .bytes = self.ptrs[sizes.fields[0]], .len = self.len, .capacity = self.capacity, }; @@ -179,6 +181,7 @@ pub fn MultiArrayList(comptime T: type) type { const fields = meta.fields(Elem); /// `sizes.bytes` is an array of @sizeOf each T field. Sorted by alignment, descending. /// `sizes.fields` is an array mapping from `sizes.bytes` array index to field index. + /// `sizes.big_align` is the overall alignment of the allocation, which equals the maximum field alignment. const sizes = blk: { const Data = struct { size: usize, @@ -186,12 +189,14 @@ pub fn MultiArrayList(comptime T: type) type { alignment: usize, }; var data: [fields.len]Data = undefined; + var big_align: usize = 1; for (fields, 0..) |field_info, i| { data[i] = .{ .size = @sizeOf(field_info.type), .size_index = i, - .alignment = if (@sizeOf(field_info.type) == 0) 1 else field_info.alignment, + .alignment = field_info.alignment orelse @alignOf(field_info.type), }; + big_align = @max(big_align, data[i].alignment); } const Sort = struct { fn lessThan(context: void, lhs: Data, rhs: Data) bool { @@ -210,6 +215,7 @@ pub fn MultiArrayList(comptime T: type) type { break :blk .{ .bytes = sizes_bytes, .fields = field_indexes, + .big_align = mem.Alignment.fromByteUnits(big_align), }; }; @@ -452,7 +458,7 @@ pub fn MultiArrayList(comptime T: type) type { assert(new_len <= self.capacity); assert(new_len <= self.len); - const other_bytes = gpa.alignedAlloc(u8, .of(Elem), capacityInBytes(new_len)) catch { + const other_bytes = gpa.alignedAlloc(u8, sizes.big_align, capacityInBytes(new_len)) catch { const self_slice = self.slice(); inline for (fields, 0..) |field_info, i| { if (@sizeOf(field_info.type) != 0) { @@ -533,7 +539,7 @@ pub fn MultiArrayList(comptime T: type) type { /// `new_capacity` must be greater or equal to `len`. pub fn setCapacity(self: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void { assert(new_capacity >= self.len); - const new_bytes = try gpa.alignedAlloc(u8, .of(Elem), capacityInBytes(new_capacity)); + const new_bytes = try gpa.alignedAlloc(u8, sizes.big_align, capacityInBytes(new_capacity)); if (self.len == 0) { gpa.free(self.allocatedBytes()); self.bytes = new_bytes.ptr; @@ -650,8 +656,8 @@ pub fn MultiArrayList(comptime T: type) type { return elem_bytes * capacity; } - fn allocatedBytes(self: Self) []align(@alignOf(Elem)) u8 { - return self.bytes[0..capacityInBytes(self.capacity)]; + fn allocatedBytes(self: Self) []align(sizes.big_align.toByteUnits()) u8 { + return @alignCast(self.bytes[0..capacityInBytes(self.capacity)]); } fn FieldType(comptime field: Field) type { diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig @@ -7113,7 +7113,7 @@ pub const io_uring_buf_reg = extern struct { flags: Flags, resv: [3]u64, - pub const Flags = packed struct { + pub const Flags = packed struct(u16) { _0: u1 = 0, /// Incremental buffer consumption. inc: bool, diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig @@ -4160,7 +4160,7 @@ pub const RUNTIME_FUNCTION = switch (native_arch) { BeginAddress: DWORD, DUMMYUNIONNAME: extern union { UnwindData: DWORD, - DUMMYSTRUCTNAME: packed struct { + DUMMYSTRUCTNAME: packed struct(u32) { Flag: u2, FunctionLength: u11, Ret: u2, @@ -4177,7 +4177,7 @@ pub const RUNTIME_FUNCTION = switch (native_arch) { BeginAddress: DWORD, DUMMYUNIONNAME: extern union { UnwindData: DWORD, - DUMMYSTRUCTNAME: packed struct { + DUMMYSTRUCTNAME: packed struct(u32) { Flag: u2, FunctionLength: u11, RegF: u3, @@ -5013,7 +5013,7 @@ pub const KUSER_SHARED_DATA = extern struct { KdDebuggerEnabled: BOOLEAN, DummyUnion1: extern union { MitigationPolicies: UCHAR, - Alt: packed struct { + Alt: packed struct(u8) { NXSupportPolicy: u2, SEHValidationPolicy: u2, CurDirDevicesSkippedForDlls: u2, @@ -5029,7 +5029,7 @@ pub const KUSER_SHARED_DATA = extern struct { SafeBootMode: BOOLEAN, DummyUnion2: extern union { VirtualizationFlags: UCHAR, - Alt: packed struct { + Alt: packed struct(u8) { ArchStartedInEl2: u1, QcSlIsSupported: u1, SpareBits: u6, @@ -5038,7 +5038,7 @@ pub const KUSER_SHARED_DATA = extern struct { Reserved12: [2]UCHAR, DummyUnion3: extern union { SharedDataFlags: ULONG, - Alt: packed struct { + Alt: packed struct(u32) { DbgErrorPortPresent: u1, DbgElevationEnabled: u1, DbgVirtEnabled: u1, diff --git a/lib/std/pdb.zig b/lib/std/pdb.zig @@ -332,7 +332,7 @@ pub const ProcSym = extern struct { name: [1]u8, // null-terminated }; -pub const ProcSymFlags = packed struct { +pub const ProcSymFlags = packed struct(u8) { has_fp: bool, has_iret: bool, has_fret: bool, @@ -373,7 +373,7 @@ pub const LineFragmentHeader = extern struct { code_size: u32, }; -pub const LineFlags = packed struct { +pub const LineFlags = packed struct(u16) { /// CV_LINES_HAVE_COLUMNS have_columns: bool, unused: u15, diff --git a/lib/std/testing.zig b/lib/std/testing.zig @@ -950,9 +950,8 @@ test "expectEqualDeep primitive type" { } test "expectEqualDeep pointer" { - const a = 1; - const b = 1; - try expectEqualDeep(&a, &b); + try comptime expectEqualDeep(&1, &1); + try expectEqualDeep(&@as(u32, 1), &@as(u32, 1)); } test "expectEqualDeep composite type" { diff --git a/lib/std/zig.zig b/lib/std/zig.zig @@ -837,6 +837,10 @@ pub const SimpleComptimeReason = enum(u32) { tuple_field_types, enum_field_names, enum_field_values, + union_enum_tag_type, + enum_int_tag_type, + packed_struct_backing_int_type, + packed_union_backing_int_type, // Evaluating at comptime because decl/field name must be comptime-known. decl_name, @@ -864,7 +868,7 @@ pub const SimpleComptimeReason = enum(u32) { casted_to_comptime_enum, casted_to_comptime_int, casted_to_comptime_float, - panic_handler, + std_builtin_decl, pub fn message(r: SimpleComptimeReason) []const u8 { return switch (r) { @@ -925,6 +929,11 @@ pub const SimpleComptimeReason = enum(u32) { .enum_field_names => "enum field names must be comptime-known", .enum_field_values => "enum field values must be comptime-known", + .union_enum_tag_type => "enum tag type of union must be comptime-known", + .enum_int_tag_type => "integer tag type of enum must be comptime-known", + .packed_struct_backing_int_type => "packed struct backing integer type must be comptime-known", + .packed_union_backing_int_type => "packed struct backing integer type must be comptime-known", + .decl_name => "declaration name must be comptime-known", .field_name => "field name must be comptime-known", .tuple_field_index => "tuple field index must be comptime-known", @@ -948,7 +957,7 @@ pub const SimpleComptimeReason = enum(u32) { .casted_to_comptime_enum => "value casted to enum with 'comptime_int' tag type must be comptime-known", .casted_to_comptime_int => "value casted to 'comptime_int' must be comptime-known", .casted_to_comptime_float => "value casted to 'comptime_float' must be comptime-known", - .panic_handler => "panic handler must be comptime-known", + .std_builtin_decl => "'std.builtin' declaration values must be comptime-known", // zig fmt: on }; } diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig @@ -175,10 +175,10 @@ pub fn parseTokens( .source = source, .gpa = gpa, .tokens = tokens, - .errors = .{}, - .nodes = .{}, - .extra_data = .{}, - .scratch = .{}, + .errors = .empty, + .nodes = .empty, + .extra_data = .empty, + .scratch = .empty, .tok_i = 0, }; defer parser.errors.deinit(gpa); diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig @@ -1780,7 +1780,7 @@ fn structInitExpr( try gop.value_ptr.append(sfba_allocator, name_token); any_duplicate = true; } else { - gop.value_ptr.* = .{}; + gop.value_ptr.* = .empty; try gop.value_ptr.append(sfba_allocator, name_token); } } @@ -3975,81 +3975,67 @@ fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node. return rvalue(gz, ri, result, node); } -const WipMembers = struct { - payload: *ArrayList(u32), - payload_top: usize, - field_bits_start: u32, - fields_start: u32, - fields_end: u32, - decl_index: u32 = 0, - field_index: u32 = 0, - - const Self = @This(); - - fn init(gpa: Allocator, payload: *ArrayList(u32), decl_count: u32, field_count: u32, comptime bits_per_field: u32, comptime max_field_size: u32) Allocator.Error!Self { - const payload_top: u32 = @intCast(payload.items.len); - const field_bits_start = payload_top + decl_count; - const fields_start = field_bits_start + if (bits_per_field > 0) blk: { - const fields_per_u32 = 32 / bits_per_field; - break :blk (field_count + fields_per_u32 - 1) / fields_per_u32; - } else 0; - const payload_end = fields_start + field_count * max_field_size; - try payload.resize(gpa, payload_end); +const Scratch = struct { + astgen: *AstGen, + scratch_top: u32, + fn init(astgen: *AstGen) Scratch { return .{ - .payload = payload, - .payload_top = payload_top, - .field_bits_start = field_bits_start, - .fields_start = fields_start, - .fields_end = fields_start, + .astgen = astgen, + .scratch_top = @intCast(astgen.scratch.items.len), }; } - - fn nextDecl(self: *Self, decl_inst: Zir.Inst.Index) void { - self.payload.items[self.payload_top + self.decl_index] = @intFromEnum(decl_inst); - self.decl_index += 1; + fn reset(s: *Scratch) void { + s.astgen.scratch.shrinkRetainingCapacity(s.scratch_top); + s.* = undefined; } - - fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void { - const fields_per_u32 = 32 / bits_per_field; - const index = self.field_bits_start + self.field_index / fields_per_u32; - assert(index < self.fields_start); - var bit_bag: u32 = if (self.field_index % fields_per_u32 == 0) 0 else self.payload.items[index]; - bit_bag >>= bits_per_field; - comptime var i = 0; - inline while (i < bits_per_field) : (i += 1) { - bit_bag |= @as(u32, @intFromBool(bits[i])) << (32 - bits_per_field + i); - } - self.payload.items[index] = bit_bag; - self.field_index += 1; + fn addSlice(s: *Scratch, len: u32) Allocator.Error!Slice { + const start: u32 = @intCast(s.astgen.scratch.items.len); + try s.astgen.scratch.resize(s.astgen.gpa, start + len); + return .{ .start = start, .len = len }; } - - fn appendToField(self: *Self, data: u32) void { - assert(self.fields_end < self.payload.items.len); - self.payload.items[self.fields_end] = data; - self.fields_end += 1; + fn addOptionalSlice(s: *Scratch, present: bool, len: u32) Allocator.Error!?Slice { + if (!present) return null; + return try addSlice(s, len); } - - fn finishBits(self: *Self, comptime bits_per_field: u32) void { - if (bits_per_field > 0) { - const fields_per_u32 = 32 / bits_per_field; - const empty_field_slots = fields_per_u32 - (self.field_index % fields_per_u32); - if (self.field_index > 0 and empty_field_slots < fields_per_u32) { - const index = self.field_bits_start + self.field_index / fields_per_u32; - self.payload.items[index] >>= @intCast(empty_field_slots * bits_per_field); - } - } + fn appendBodyWithFixups(s: *Scratch, body: []const Zir.Inst.Index) Allocator.Error!u32 { + const len = countBodyLenAfterFixups(s.astgen, body); + try s.astgen.scratch.ensureUnusedCapacity(s.astgen.gpa, len); + appendBodyWithFixupsArrayList(s.astgen, &s.astgen.scratch, body); + return len; } - - fn declsSlice(self: *Self) []u32 { - return self.payload.items[self.payload_top..][0..self.decl_index]; + /// Returns the slice containing all data added to this `Scratch`. + fn all(s: *Scratch) Slice { + const len = s.astgen.scratch.items.len - s.scratch_top; + return .{ .start = s.scratch_top, .len = @intCast(len) }; } + const Slice = struct { + start: u32, + len: u32, + fn get(s: Slice, astgen: *AstGen) []u32 { + return astgen.scratch.items[s.start..][0..s.len]; + } + }; +}; - fn fieldsSlice(self: *Self) []u32 { - return self.payload.items[self.field_bits_start..self.fields_end]; - } +const WipDecls = struct { + astgen: *AstGen, + slice: Scratch.Slice, + index: u32, - fn deinit(self: *Self) void { - self.payload.items.len = self.payload_top; + fn init(scratch: *Scratch, decls_len: u32) Allocator.Error!WipDecls { + return .{ + .astgen = scratch.astgen, + .slice = try scratch.addSlice(decls_len), + .index = 0, + }; + } + fn finish(wip: *WipDecls) void { + assert(wip.index == wip.slice.len); + wip.* = undefined; + } + fn nextDecl(wip: *WipDecls, decl_inst: Zir.Inst.Index) void { + wip.slice.get(wip.astgen)[wip.index] = @intFromEnum(decl_inst); + wip.index += 1; } }; @@ -4057,7 +4043,7 @@ fn fnDecl( astgen: *AstGen, gz: *GenZir, scope: *Scope, - wip_members: *WipMembers, + wip_decls: *WipDecls, decl_node: Ast.Node.Index, body_node: Ast.Node.OptionalIndex, fn_proto: Ast.full.FnProto, @@ -4133,7 +4119,7 @@ fn fnDecl( assert(!is_extern); // validated by parser (TODO why???) } - wip_members.nextDecl(decl_inst); + wip_decls.nextDecl(decl_inst); var type_gz: GenZir = .{ .is_comptime = true, @@ -4488,7 +4474,7 @@ fn globalVarDecl( astgen: *AstGen, gz: *GenZir, scope: *Scope, - wip_members: *WipMembers, + wip_decls: *WipDecls, node: Ast.Node.Index, var_decl: Ast.full.VarDecl, ) InnerError!void { @@ -4533,7 +4519,7 @@ fn globalVarDecl( const decl_column = astgen.source_column; const decl_inst = try gz.makeDeclaration(node); - wip_members.nextDecl(decl_inst); + wip_decls.nextDecl(decl_inst); if (var_decl.ast.init_node.unwrap()) |init_node| { if (is_extern) { @@ -4635,7 +4621,7 @@ fn comptimeDecl( astgen: *AstGen, gz: *GenZir, scope: *Scope, - wip_members: *WipMembers, + wip_decls: *WipDecls, node: Ast.Node.Index, ) InnerError!void { const tree = astgen.tree; @@ -4650,7 +4636,7 @@ fn comptimeDecl( // Up top so the ZIR instruction index marks the start range of this // top-level declaration. const decl_inst = try gz.makeDeclaration(node); - wip_members.nextDecl(decl_inst); + wip_decls.nextDecl(decl_inst); astgen.advanceSourceCursorToNode(node); // This is just needed for the `setDeclaration` call. @@ -4698,7 +4684,7 @@ fn testDecl( astgen: *AstGen, gz: *GenZir, scope: *Scope, - wip_members: *WipMembers, + wip_decls: *WipDecls, node: Ast.Node.Index, ) InnerError!void { const tree = astgen.tree; @@ -4714,7 +4700,7 @@ fn testDecl( // top-level declaration. const decl_inst = try gz.makeDeclaration(node); - wip_members.nextDecl(decl_inst); + wip_decls.nextDecl(decl_inst); astgen.advanceSourceCursorToNode(node); // This is just needed for the `setDeclaration` call. @@ -4914,7 +4900,7 @@ fn structDeclInner( node: Ast.Node.Index, container_decl: Ast.full.ContainerDecl, layout: std.builtin.Type.ContainerLayout, - backing_int_node: Ast.Node.OptionalIndex, + maybe_backing_int_node: Ast.Node.OptionalIndex, name_strat: Zir.Inst.NameStrategy, ) InnerError!Zir.Inst.Ref { const astgen = gz.astgen; @@ -4930,27 +4916,29 @@ fn structDeclInner( if (node == .root) { return astgen.failNode(tuple_field_node, "file cannot be a tuple", .{}); } else { - return tupleDecl(gz, scope, node, container_decl, layout, backing_int_node); + return tupleDecl(gz, scope, node, container_decl, layout, maybe_backing_int_node); } } + astgen.advanceSourceCursorToNode(node); + const decl_inst = try gz.reserveInstructionIndex(); - if (container_decl.ast.members.len == 0 and backing_int_node == .none) { + if (container_decl.ast.members.len == 0 and maybe_backing_int_node == .none) { try gz.setStruct(decl_inst, .{ .src_node = node, + .name_strat = name_strat, .layout = layout, - .captures_len = 0, - .fields_len = 0, + .backing_int_type_body_len = null, .decls_len = 0, - .has_backing_int = false, - .known_non_opv = false, - .known_comptime_only = false, + .fields_len = 0, + .any_field_aligns = false, + .any_field_defaults = false, .any_comptime_fields = false, - .any_default_inits = false, - .any_aligned_fields = false, - .fields_hash = std.zig.hashSrc(@tagName(layout)), - .name_strat = name_strat, + .fields_hash = @splat(0), + .captures = &.{}, + .capture_names = &.{}, + .remaining = &.{}, }); return decl_inst.toRef(); } @@ -4967,7 +4955,6 @@ fn structDeclInner( // The struct_decl instruction introduces a scope in which the decls of the struct // are in scope, so that field types, alignments, and default value expressions // can refer to decls within the struct itself. - astgen.advanceSourceCursorToNode(node); var block_scope: GenZir = .{ .parent = &namespace.base, .decl_node_index = node, @@ -4979,197 +4966,134 @@ fn structDeclInner( }; defer block_scope.unstack(); - const scratch_top = astgen.scratch.items.len; - defer astgen.scratch.items.len = scratch_top; - - var backing_int_body_len: usize = 0; - const backing_int_ref: Zir.Inst.Ref = blk: { - if (backing_int_node.unwrap()) |arg| { - if (layout != .@"packed") { - return astgen.failNode(arg, "non-packed struct does not support backing integer type", .{}); - } else { - const backing_int_ref = try typeExpr(&block_scope, &namespace.base, arg); - if (!block_scope.isEmpty()) { - if (!block_scope.endsWithNoReturn()) { - _ = try block_scope.addBreak(.break_inline, decl_inst, backing_int_ref); - } - - const body = block_scope.instructionsSlice(); - const old_scratch_len = astgen.scratch.items.len; - try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); - appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); - backing_int_body_len = astgen.scratch.items.len - old_scratch_len; - block_scope.instructions.items.len = block_scope.instructions_top; - } - break :blk backing_int_ref; - } - } else { - break :blk .none; - } - }; + const scan_result = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"struct"); - const decl_count = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"struct"); - const field_count: u32 = @intCast(container_decl.ast.members.len - decl_count); + var scratch: Scratch = .init(astgen); + defer scratch.reset(); - const bits_per_field = 4; - const max_field_size = 5; - var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size); - defer wip_members.deinit(); + // Replicate the structure of the ZIR trailing data in `scratch` + var wip_decls: WipDecls = try .init(&scratch, scan_result.decls_len); + const field_names = try scratch.addSlice(scan_result.fields_len); + const field_type_body_lens = try scratch.addSlice(scan_result.fields_len); + const field_align_body_lens = try scratch.addOptionalSlice(scan_result.any_field_aligns, scan_result.fields_len); + const field_default_body_lens = try scratch.addOptionalSlice(scan_result.any_field_values, scan_result.fields_len); + const field_comptime_bits = try scratch.addOptionalSlice( + scan_result.any_comptime_fields, + std.math.divCeil(u32, scan_result.fields_len, 32) catch unreachable, + ); + if (field_comptime_bits) |bits| @memset(bits.get(astgen), 0); - // We will use the scratch buffer, starting here, for the bodies: - // bodies: { // for every fields_len - // field_type_body_inst: Inst, // for each field_type_body_len - // align_body_inst: Inst, // for each align_body_len - // init_body_inst: Inst, // for each init_body_len - // } - // Note that the scratch buffer is simultaneously being used by WipMembers, however - // it will not access any elements beyond this point in the ArrayList. It also - // accesses via the ArrayList items field so it can handle the scratch buffer being - // reallocated. - // No defer needed here because it is handled by `wip_members.deinit()` above. - const bodies_start = astgen.scratch.items.len; + // Before any field bodies comes the backing int type, if specified. + const backing_int_type_body_len: ?u32 = if (maybe_backing_int_node.unwrap()) |backing_int_node| len: { + if (layout != .@"packed") return astgen.failNode( + backing_int_node, + "non-packed struct does not support backing integer type", + .{}, + ); + const type_ref = try typeExpr(&block_scope, &namespace.base, backing_int_node); + if (!block_scope.endsWithNoReturn()) { + _ = try block_scope.addBreak(.break_inline, decl_inst, type_ref); + } + const body_len = try scratch.appendBodyWithFixups(block_scope.instructionsSlice()); + block_scope.instructions.items.len = block_scope.instructions_top; + break :len body_len; + } else null; const old_hasher = astgen.src_hasher; defer astgen.src_hasher = old_hasher; - astgen.src_hasher = std.zig.SrcHasher.init(.{}); - astgen.src_hasher.update(@tagName(layout)); - if (backing_int_node.unwrap()) |arg| { - astgen.src_hasher.update(tree.getNodeSource(arg)); - } + astgen.src_hasher = .init(.{}); - var known_non_opv = false; - var known_comptime_only = false; - var any_comptime_fields = false; - var any_aligned_fields = false; - var any_default_inits = false; + var next_field_idx: u32 = 0; for (container_decl.ast.members) |member_node| { - var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { + var member = switch (try containerMember(&block_scope, &namespace.base, &wip_decls, member_node)) { .decl => continue, .field => |field| field, }; + const field_idx = next_field_idx; + next_field_idx += 1; astgen.src_hasher.update(tree.getNodeSource(member_node)); - const field_name = try astgen.identAsString(member.ast.main_token); member.convertToNonTupleLike(astgen.tree); assert(!member.ast.tuple_like); - wip_members.appendToField(@intFromEnum(field_name)); - - const type_expr = member.ast.type_expr.unwrap() orelse { - return astgen.failTok(member.ast.main_token, "struct field missing type", .{}); - }; - const field_type = try typeExpr(&block_scope, &namespace.base, type_expr); - const have_type_body = !block_scope.isEmpty(); - const have_align = member.ast.align_expr != .none; - const have_value = member.ast.value_expr != .none; - const is_comptime = member.comptime_token != null; + field_names.get(astgen)[field_idx] = @intFromEnum(try astgen.identAsString(member.ast.main_token)); - if (is_comptime) { - switch (layout) { - .@"packed", .@"extern" => return astgen.failTok(member.comptime_token.?, "{s} struct fields cannot be marked comptime", .{@tagName(layout)}), - .auto => any_comptime_fields = true, - } - } else { - known_non_opv = known_non_opv or - nodeImpliesMoreThanOnePossibleValue(tree, type_expr); - known_comptime_only = known_comptime_only or - nodeImpliesComptimeOnly(tree, type_expr); - } - wip_members.nextField(bits_per_field, .{ have_align, have_value, is_comptime, have_type_body }); - - if (have_type_body) { + { + const type_node = member.ast.type_expr.unwrap() orelse { + return astgen.failTok(member.ast.main_token, "struct field missing type", .{}); + }; + const type_ref = try typeExpr(&block_scope, &namespace.base, type_node); if (!block_scope.endsWithNoReturn()) { - _ = try block_scope.addBreak(.break_inline, decl_inst, field_type); + _ = try block_scope.addBreak(.break_inline, decl_inst, type_ref); } - const body = block_scope.instructionsSlice(); - const old_scratch_len = astgen.scratch.items.len; - try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); - appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); - wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len)); + const body_len = try scratch.appendBodyWithFixups(block_scope.instructionsSlice()); + field_type_body_lens.get(astgen)[field_idx] = body_len; block_scope.instructions.items.len = block_scope.instructions_top; - } else { - wip_members.appendToField(@intFromEnum(field_type)); } - if (member.ast.align_expr.unwrap()) |align_expr| { + if (member.ast.align_expr.unwrap()) |align_node| { if (layout == .@"packed") { - return astgen.failNode(align_expr, "unable to override alignment of packed struct fields", .{}); + return astgen.failNode(align_node, "unable to override alignment of packed struct fields", .{}); } - any_aligned_fields = true; - const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, align_expr); + const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, align_node); if (!block_scope.endsWithNoReturn()) { _ = try block_scope.addBreak(.break_inline, decl_inst, align_ref); } - const body = block_scope.instructionsSlice(); - const old_scratch_len = astgen.scratch.items.len; - try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); - appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); - wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len)); + const body_len = try scratch.appendBodyWithFixups(block_scope.instructionsSlice()); + field_align_body_lens.?.get(astgen)[field_idx] = body_len; block_scope.instructions.items.len = block_scope.instructions_top; + } else if (field_align_body_lens) |lens| { + lens.get(astgen)[field_idx] = 0; } - if (member.ast.value_expr.unwrap()) |value_expr| { - any_default_inits = true; - - // The decl_inst is used as here so that we can easily reconstruct a mapping - // between it and the field type when the fields inits are analyzed. - const ri: ResultInfo = .{ .rl = if (field_type == .none) .none else .{ .coerced_ty = decl_inst.toRef() } }; - - const default_inst = try expr(&block_scope, &namespace.base, ri, value_expr); + if (member.ast.value_expr.unwrap()) |default_node| { + const ri: ResultInfo = .{ .rl = .{ .coerced_ty = decl_inst.toRef() } }; + const default_ref = try expr(&block_scope, &namespace.base, ri, default_node); if (!block_scope.endsWithNoReturn()) { - _ = try block_scope.addBreak(.break_inline, decl_inst, default_inst); + _ = try block_scope.addBreak(.break_inline, decl_inst, default_ref); } - const body = block_scope.instructionsSlice(); - const old_scratch_len = astgen.scratch.items.len; - try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); - appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); - wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len)); + const body_len = try scratch.appendBodyWithFixups(block_scope.instructionsSlice()); + field_default_body_lens.?.get(astgen)[field_idx] = body_len; block_scope.instructions.items.len = block_scope.instructions_top; - } else if (member.comptime_token) |comptime_token| { - return astgen.failTok(comptime_token, "comptime field without default initialization value", .{}); + } else if (field_default_body_lens) |lens| { + lens.get(astgen)[field_idx] = 0; + } + + if (member.comptime_token) |comptime_token| { + switch (layout) { + .@"packed", .@"extern" => return astgen.failTok(comptime_token, "{s} struct fields cannot be marked comptime", .{@tagName(layout)}), + .auto => {}, + } + if (member.ast.value_expr == .none) { + return astgen.failTok(comptime_token, "comptime field without default initialization value", .{}); + } + const mask = @as(u32, 1) << @intCast(field_idx % 32); + field_comptime_bits.?.get(astgen)[field_idx / 32] |= mask; } } + assert(next_field_idx == scan_result.fields_len); + wip_decls.finish(); var fields_hash: std.zig.SrcHash = undefined; astgen.src_hasher.final(&fields_hash); try gz.setStruct(decl_inst, .{ .src_node = node, + .name_strat = name_strat, .layout = layout, - .captures_len = @intCast(namespace.captures.count()), - .fields_len = field_count, - .decls_len = decl_count, - .has_backing_int = backing_int_ref != .none, - .known_non_opv = known_non_opv, - .known_comptime_only = known_comptime_only, - .any_comptime_fields = any_comptime_fields, - .any_default_inits = any_default_inits, - .any_aligned_fields = any_aligned_fields, + .backing_int_type_body_len = backing_int_type_body_len, + .decls_len = scan_result.decls_len, + .fields_len = scan_result.fields_len, + .any_field_aligns = scan_result.any_field_aligns, + .any_field_defaults = scan_result.any_field_values, + .any_comptime_fields = scan_result.any_comptime_fields, .fields_hash = fields_hash, - .name_strat = name_strat, + .captures = namespace.captures.keys(), + .capture_names = namespace.captures.values(), + .remaining = scratch.all().get(astgen), }); - wip_members.finishBits(bits_per_field); - const decls_slice = wip_members.declsSlice(); - const fields_slice = wip_members.fieldsSlice(); - const bodies_slice = astgen.scratch.items[bodies_start..]; - try astgen.extra.ensureUnusedCapacity(gpa, backing_int_body_len + 2 + - decls_slice.len + namespace.captures.count() * 2 + fields_slice.len + bodies_slice.len); - astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys())); - astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values())); - if (backing_int_ref != .none) { - astgen.extra.appendAssumeCapacity(@intCast(backing_int_body_len)); - if (backing_int_body_len == 0) { - astgen.extra.appendAssumeCapacity(@intFromEnum(backing_int_ref)); - } else { - astgen.extra.appendSliceAssumeCapacity(astgen.scratch.items[scratch_top..][0..backing_int_body_len]); - } - } - astgen.extra.appendSliceAssumeCapacity(decls_slice); - astgen.extra.appendSliceAssumeCapacity(fields_slice); - astgen.extra.appendSliceAssumeCapacity(bodies_slice); - block_scope.unstack(); return decl_inst.toRef(); } @@ -5281,11 +5205,29 @@ fn unionDeclInner( auto_enum_tok: ?Ast.TokenIndex, name_strat: Zir.Inst.NameStrategy, ) InnerError!Zir.Inst.Ref { - const decl_inst = try gz.reserveInstructionIndex(); - const astgen = gz.astgen; const gpa = astgen.gpa; + const explicit_int_or_enum_tag = switch (layout) { + .auto => opt_arg_node != .none, + .@"extern" => if (opt_arg_node.unwrap()) |arg_node| { + return astgen.failNode(arg_node, "{s} union does not support enum tag type", .{@tagName(layout)}); + } else false, + .@"packed" => false, + }; + + if (auto_enum_tok) |t| { + if (layout != .auto) { + return astgen.failTok(t, "{s} union does not support enum tag type", .{@tagName(layout)}); + } + } + + const is_tagged = explicit_int_or_enum_tag or auto_enum_tok != null; + + astgen.advanceSourceCursorToNode(node); + + const decl_inst = try gz.reserveInstructionIndex(); + var namespace: Scope.Namespace = .{ .parent = scope, .node = node, @@ -5298,7 +5240,6 @@ fn unionDeclInner( // The union_decl instruction introduces a scope in which the decls of the union // are in scope, so that field types, alignments, and default value expressions // can refer to decls within the union itself. - astgen.advanceSourceCursorToNode(node); var block_scope: GenZir = .{ .parent = &namespace.base, .decl_node_index = node, @@ -5310,42 +5251,42 @@ fn unionDeclInner( }; defer block_scope.unstack(); - const decl_count = try astgen.scanContainer(&namespace, members, .@"union"); - const field_count: u32 = @intCast(members.len - decl_count); + const scan_result = try astgen.scanContainer(&namespace, members, .@"union"); - if (layout != .auto and (auto_enum_tok != null or opt_arg_node != .none)) { - if (opt_arg_node.unwrap()) |arg_node| { - return astgen.failNode(arg_node, "{s} union does not support enum tag type", .{@tagName(layout)}); - } else { - return astgen.failTok(auto_enum_tok.?, "{s} union does not support enum tag type", .{@tagName(layout)}); - } - } + var scratch: Scratch = .init(astgen); + defer scratch.reset(); - const arg_inst: Zir.Inst.Ref = if (opt_arg_node.unwrap()) |arg_node| - try typeExpr(&block_scope, &namespace.base, arg_node) - else - .none; + // Replicate the structure of the ZIR trailing data in `scratch` + var wip_decls: WipDecls = try .init(&scratch, scan_result.decls_len); + const field_names = try scratch.addSlice(scan_result.fields_len); + const field_type_body_lens = try scratch.addSlice(scan_result.fields_len); + const field_align_body_lens = try scratch.addOptionalSlice(scan_result.any_field_aligns, scan_result.fields_len); + const field_value_body_lens = try scratch.addOptionalSlice(scan_result.any_field_values, scan_result.fields_len); - const bits_per_field = 4; - const max_field_size = 4; - var any_aligned_fields = false; - var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size); - defer wip_members.deinit(); + // Before any field bodies comes the tag/backing type, if specified. + const arg_type_body_len: ?u32 = if (opt_arg_node.unwrap()) |arg_node| len: { + const type_ref = try typeExpr(&block_scope, &namespace.base, arg_node); + if (!block_scope.endsWithNoReturn()) { + _ = try block_scope.addBreak(.break_inline, decl_inst, type_ref); + } + const body_len = try scratch.appendBodyWithFixups(block_scope.instructionsSlice()); + block_scope.instructions.items.len = block_scope.instructions_top; + break :len body_len; + } else null; const old_hasher = astgen.src_hasher; defer astgen.src_hasher = old_hasher; - astgen.src_hasher = std.zig.SrcHasher.init(.{}); - astgen.src_hasher.update(@tagName(layout)); - astgen.src_hasher.update(&.{@intFromBool(auto_enum_tok != null)}); - if (opt_arg_node.unwrap()) |arg_node| { - astgen.src_hasher.update(astgen.tree.getNodeSource(arg_node)); - } + astgen.src_hasher = .init(.{}); + var next_field_idx: u32 = 0; for (members) |member_node| { - var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { + var member = switch (try containerMember(&block_scope, &namespace.base, &wip_decls, member_node)) { .decl => continue, .field => |field| field, }; + const field_idx = next_field_idx; + next_field_idx += 1; + astgen.src_hasher.update(astgen.tree.getNodeSource(member_node)); member.convertToNonTupleLike(astgen.tree); if (member.ast.tuple_like) { @@ -5355,97 +5296,91 @@ fn unionDeclInner( return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{}); } - const field_name = try astgen.identAsString(member.ast.main_token); - wip_members.appendToField(@intFromEnum(field_name)); - - const have_type = member.ast.type_expr != .none; - const have_align = member.ast.align_expr != .none; - const have_value = member.ast.value_expr != .none; - const unused = false; - wip_members.nextField(bits_per_field, .{ have_type, have_align, have_value, unused }); + field_names.get(astgen)[field_idx] = @intFromEnum(try astgen.identAsString(member.ast.main_token)); - if (member.ast.type_expr.unwrap()) |type_expr| { - const field_type = try typeExpr(&block_scope, &namespace.base, type_expr); - wip_members.appendToField(@intFromEnum(field_type)); - } else if (arg_inst == .none and auto_enum_tok == null) { + if (member.ast.type_expr.unwrap()) |type_node| { + const type_ref = try typeExpr(&block_scope, &namespace.base, type_node); + if (!block_scope.endsWithNoReturn()) { + _ = try block_scope.addBreak(.break_inline, decl_inst, type_ref); + } + const body_len = try scratch.appendBodyWithFixups(block_scope.instructionsSlice()); + field_type_body_lens.get(astgen)[field_idx] = body_len; + block_scope.instructions.items.len = block_scope.instructions_top; + } else if (!is_tagged) { return astgen.failNode(member_node, "union field missing type", .{}); + } else { + field_type_body_lens.get(astgen)[field_idx] = 0; } - if (member.ast.align_expr.unwrap()) |align_expr| { + + if (member.ast.align_expr.unwrap()) |align_node| { if (layout == .@"packed") { - return astgen.failNode(align_expr, "unable to override alignment of packed union fields", .{}); + return astgen.failNode(align_node, "unable to override alignment of packed union fields", .{}); } - const align_inst = try expr(&block_scope, &block_scope.base, coerced_align_ri, align_expr); - wip_members.appendToField(@intFromEnum(align_inst)); - any_aligned_fields = true; - } - if (member.ast.value_expr.unwrap()) |value_expr| { - if (arg_inst == .none) { - return astgen.failNodeNotes( - node, - "explicitly valued tagged union missing integer tag type", - .{}, - &[_]u32{ - try astgen.errNoteNode( - value_expr, - "tag value specified here", - .{}, - ), - }, - ); + const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, align_node); + if (!block_scope.endsWithNoReturn()) { + _ = try block_scope.addBreak(.break_inline, decl_inst, align_ref); } - if (auto_enum_tok == null) { - return astgen.failNodeNotes( - node, - "explicitly valued tagged union requires inferred enum tag type", - .{}, - &[_]u32{ - try astgen.errNoteNode( - value_expr, - "tag value specified here", - .{}, - ), - }, - ); + const body_len = try scratch.appendBodyWithFixups(block_scope.instructionsSlice()); + field_align_body_lens.?.get(astgen)[field_idx] = body_len; + block_scope.instructions.items.len = block_scope.instructions_top; + } else if (field_align_body_lens) |lens| { + lens.get(astgen)[field_idx] = 0; + } + + if (member.ast.value_expr.unwrap()) |value_node| { + if (!explicit_int_or_enum_tag) return astgen.failNodeNotes( + node, + "explicitly valued tagged union missing integer tag type", + .{}, + &.{try astgen.errNoteNode(value_node, "tag value specified here", .{})}, + ); + if (auto_enum_tok == null) return astgen.failNodeNotes( + node, + "explicitly valued tagged union requires inferred enum tag type", + .{}, + &.{try astgen.errNoteNode(value_node, "tag value specified here", .{})}, + ); + const ri: ResultInfo = .{ .rl = .{ .coerced_ty = decl_inst.toRef() } }; + const value_ref = try expr(&block_scope, &namespace.base, ri, value_node); + if (!block_scope.endsWithNoReturn()) { + _ = try block_scope.addBreak(.break_inline, decl_inst, value_ref); } - const tag_value = try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = arg_inst } }, value_expr); - wip_members.appendToField(@intFromEnum(tag_value)); + const body_len = try scratch.appendBodyWithFixups(block_scope.instructionsSlice()); + field_value_body_lens.?.get(astgen)[field_idx] = body_len; + block_scope.instructions.items.len = block_scope.instructions_top; + } else if (field_value_body_lens) |lens| { + lens.get(astgen)[field_idx] = 0; } } + assert(next_field_idx == scan_result.fields_len); + wip_decls.finish(); var fields_hash: std.zig.SrcHash = undefined; astgen.src_hasher.final(&fields_hash); - if (!block_scope.isEmpty()) { - _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); - } - - const body = block_scope.instructionsSlice(); - const body_len = astgen.countBodyLenAfterFixups(body); - try gz.setUnion(decl_inst, .{ .src_node = node, - .layout = layout, - .tag_type = arg_inst, - .captures_len = @intCast(namespace.captures.count()), - .body_len = body_len, - .fields_len = field_count, - .decls_len = decl_count, - .auto_enum_tag = auto_enum_tok != null, - .any_aligned_fields = any_aligned_fields, - .fields_hash = fields_hash, .name_strat = name_strat, + .kind = switch (layout) { + .auto => if (auto_enum_tok == null) l: { + break :l if (opt_arg_node == .none) .auto else .tagged_explicit; + } else l: { + break :l if (opt_arg_node == .none) .tagged_enum else .tagged_enum_explicit; + }, + .@"extern" => .@"extern", + .@"packed" => if (opt_arg_node != .none) .packed_explicit else .@"packed", + }, + .arg_type_body_len = arg_type_body_len, + .decls_len = scan_result.decls_len, + .fields_len = scan_result.fields_len, + .any_field_aligns = scan_result.any_field_aligns, + .any_field_values = scan_result.any_field_values, + .fields_hash = fields_hash, + .captures = namespace.captures.keys(), + .capture_names = namespace.captures.values(), + .remaining = scratch.all().get(astgen), }); - wip_members.finishBits(bits_per_field); - const decls_slice = wip_members.declsSlice(); - const fields_slice = wip_members.fieldsSlice(); - try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() * 2 + decls_slice.len + body_len + fields_slice.len); - astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys())); - astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values())); - astgen.extra.appendSliceAssumeCapacity(decls_slice); - astgen.appendBodyWithFixups(body); - astgen.extra.appendSliceAssumeCapacity(fields_slice); - block_scope.unstack(); return decl_inst.toRef(); } @@ -5494,103 +5429,8 @@ fn containerDecl( if (container_decl.layout_token) |t| { return astgen.failTok(t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{}); } - // Count total fields as well as how many have explicitly provided tag values. - const counts = blk: { - var values: usize = 0; - var total_fields: usize = 0; - var decls: usize = 0; - var opt_nonexhaustive_node: Ast.Node.OptionalIndex = .none; - var nonfinal_nonexhaustive = false; - for (container_decl.ast.members) |member_node| { - var member = tree.fullContainerField(member_node) orelse { - decls += 1; - continue; - }; - member.convertToNonTupleLike(astgen.tree); - if (member.ast.tuple_like) { - return astgen.failTok(member.ast.main_token, "enum field missing name", .{}); - } - if (member.comptime_token) |comptime_token| { - return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{}); - } - if (member.ast.type_expr.unwrap()) |type_expr| { - return astgen.failNodeNotes( - type_expr, - "enum fields do not have types", - .{}, - &[_]u32{ - try astgen.errNoteNode( - node, - "consider 'union(enum)' here to make it a tagged union", - .{}, - ), - }, - ); - } - if (member.ast.align_expr.unwrap()) |align_expr| { - return astgen.failNode(align_expr, "enum fields cannot be aligned", .{}); - } - const name_token = member.ast.main_token; - if (mem.eql(u8, tree.tokenSlice(name_token), "_")) { - if (opt_nonexhaustive_node.unwrap()) |nonexhaustive_node| { - return astgen.failNodeNotes( - member_node, - "redundant non-exhaustive enum mark", - .{}, - &[_]u32{ - try astgen.errNoteNode( - nonexhaustive_node, - "other mark here", - .{}, - ), - }, - ); - } - opt_nonexhaustive_node = member_node.toOptional(); - if (member.ast.value_expr.unwrap()) |value_expr| { - return astgen.failNode(value_expr, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{}); - } - continue; - } else if (opt_nonexhaustive_node != .none) { - nonfinal_nonexhaustive = true; - } - total_fields += 1; - if (member.ast.value_expr.unwrap()) |value_expr| { - if (container_decl.ast.arg == .none) { - return astgen.failNode(value_expr, "value assigned to enum tag with inferred tag type", .{}); - } - values += 1; - } - } - if (nonfinal_nonexhaustive) { - return astgen.failNode(opt_nonexhaustive_node.unwrap().?, "'_' field of non-exhaustive enum must be last", .{}); - } - break :blk .{ - .total_fields = total_fields, - .values = values, - .decls = decls, - .nonexhaustive_node = opt_nonexhaustive_node, - }; - }; - if (counts.nonexhaustive_node != .none and container_decl.ast.arg == .none) { - const nonexhaustive_node = counts.nonexhaustive_node.unwrap().?; - return astgen.failNodeNotes( - node, - "non-exhaustive enum missing integer tag type", - .{}, - &[_]u32{ - try astgen.errNoteNode( - nonexhaustive_node, - "marked non-exhaustive here", - .{}, - ), - }, - ); - } - // In this case we must generate ZIR code for the tag values, similar to - // how structs are handled above. - const nonexhaustive = counts.nonexhaustive_node != .none; + astgen.advanceSourceCursorToNode(node); const decl_inst = try gz.reserveInstructionIndex(); @@ -5605,7 +5445,6 @@ fn containerDecl( // The enum_decl instruction introduces a scope in which the decls of the enum // are in scope, so that tag values can refer to decls within the enum itself. - astgen.advanceSourceCursorToNode(node); var block_scope: GenZir = .{ .parent = &namespace.base, .decl_node_index = node, @@ -5617,104 +5456,127 @@ fn containerDecl( }; defer block_scope.unstack(); - _ = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"enum"); - namespace.base.tag = .namespace; + const scan_result = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"enum"); + // The name `_` is not actually a field; it marks a non-exhaustive enum. + const fields_len: u32 = scan_result.fields_len - @intFromBool(scan_result.has_underscore_field); - const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg.unwrap()) |arg| - try comptimeExpr(&block_scope, &namespace.base, coerced_type_ri, arg, .type) - else - .none; + var scratch: Scratch = .init(astgen); + defer scratch.reset(); - const bits_per_field = 1; - const max_field_size = 2; - var wip_members = try WipMembers.init(gpa, &astgen.scratch, @intCast(counts.decls), @intCast(counts.total_fields), bits_per_field, max_field_size); - defer wip_members.deinit(); + // Replicate the structure of the ZIR trailing data in `scratch` + var wip_decls: WipDecls = try .init(&scratch, scan_result.decls_len); + const field_names = try scratch.addSlice(fields_len); + const field_value_body_lens = try scratch.addOptionalSlice(scan_result.any_field_values, fields_len); + + // Before any field bodies comes the tag type, if specified. + const tag_type_body_len: ?u32 = if (container_decl.ast.arg.unwrap()) |tag_type_node| len: { + const type_ref = try typeExpr(&block_scope, &namespace.base, tag_type_node); + if (!block_scope.endsWithNoReturn()) { + _ = try block_scope.addBreak(.break_inline, decl_inst, type_ref); + } + const body_len = try scratch.appendBodyWithFixups(block_scope.instructionsSlice()); + block_scope.instructions.items.len = block_scope.instructions_top; + break :len body_len; + } else null; const old_hasher = astgen.src_hasher; defer astgen.src_hasher = old_hasher; - astgen.src_hasher = std.zig.SrcHasher.init(.{}); - if (container_decl.ast.arg.unwrap()) |arg| { - astgen.src_hasher.update(tree.getNodeSource(arg)); - } - astgen.src_hasher.update(&.{@intFromBool(nonexhaustive)}); + astgen.src_hasher = .init(.{}); + var next_field_idx: u32 = 0; + var opt_nonexhaustive_node: Ast.Node.OptionalIndex = .none; for (container_decl.ast.members) |member_node| { - if (member_node.toOptional() == counts.nonexhaustive_node) - continue; - astgen.src_hasher.update(tree.getNodeSource(member_node)); - var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { + var member = switch (try containerMember(&block_scope, &namespace.base, &wip_decls, member_node)) { .decl => continue, .field => |field| field, }; member.convertToNonTupleLike(astgen.tree); - assert(member.comptime_token == null); - assert(member.ast.type_expr == .none); - assert(member.ast.align_expr == .none); + if (member.ast.tuple_like) return astgen.failTok(member.ast.main_token, "enum field missing name", .{}); + if (member.comptime_token) |t| return astgen.failTok(t, "enum fields cannot be marked comptime", .{}); + if (member.ast.type_expr.unwrap()) |type_node| { + return astgen.failNodeNotes(type_node, "enum fields do not have types", .{}, &.{ + try astgen.errNoteNode(node, "consider 'union(enum)' here to make it a tagged union", .{}), + }); + } + if (member.ast.align_expr.unwrap()) |n| return astgen.failNode(n, "enum fields cannot be aligned", .{}); + if (mem.eql(u8, tree.tokenSlice(member.ast.main_token), "_")) { + // non-exhaustive mark + assert(scan_result.has_underscore_field); + if (opt_nonexhaustive_node.unwrap()) |prev_node| { + return astgen.failNodeNotes(member_node, "redundant non-exhaustive enum mark", .{}, &.{ + try astgen.errNoteNode(prev_node, "other mark here", .{}), + }); + } + if (member.ast.value_expr.unwrap()) |value_node| { + return astgen.failNode(value_node, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{}); + } + if (next_field_idx != fields_len) { + return astgen.failNode(member_node, "'_' field of non-exhaustive enum must be last", .{}); + } + if (tag_type_body_len == null) { + return astgen.failNodeNotes(node, "non-exhaustive enum missing integer tag type", .{}, &.{ + try astgen.errNoteNode(member_node, "marked non-exhaustive here", .{}), + }); + } + opt_nonexhaustive_node = member_node.toOptional(); + continue; + } - const field_name = try astgen.identAsString(member.ast.main_token); - wip_members.appendToField(@intFromEnum(field_name)); + // This is a real field rather than a non-exhaustive mark. + const field_idx = next_field_idx; + next_field_idx += 1; - const have_value = member.ast.value_expr != .none; - wip_members.nextField(bits_per_field, .{have_value}); + astgen.src_hasher.update(tree.getNodeSource(member_node)); - if (member.ast.value_expr.unwrap()) |value_expr| { - if (arg_inst == .none) { - return astgen.failNodeNotes( - node, - "explicitly valued enum missing integer tag type", - .{}, - &[_]u32{ - try astgen.errNoteNode( - value_expr, - "tag value specified here", - .{}, - ), - }, - ); + field_names.get(astgen)[field_idx] = @intFromEnum(try astgen.identAsString(member.ast.main_token)); + + if (member.ast.value_expr.unwrap()) |value_node| { + if (tag_type_body_len == null) { + return astgen.failNodeNotes(node, "explicitly valued enum missing integer tag type", .{}, &.{ + try astgen.errNoteNode(value_node, "tag value specified here", .{}), + }); + } + const val_ri: ResultInfo = .{ .rl = .{ .coerced_ty = decl_inst.toRef() } }; + const value_ref = try expr(&block_scope, &namespace.base, val_ri, value_node); + if (!block_scope.endsWithNoReturn()) { + _ = try block_scope.addBreak(.break_inline, decl_inst, value_ref); } - const tag_value_inst = try expr(&block_scope, &namespace.base, .{ .rl = .{ .ty = arg_inst } }, value_expr); - wip_members.appendToField(@intFromEnum(tag_value_inst)); + const body_len = try scratch.appendBodyWithFixups(block_scope.instructionsSlice()); + field_value_body_lens.?.get(astgen)[field_idx] = body_len; + block_scope.instructions.items.len = block_scope.instructions_top; + } else if (field_value_body_lens) |lens| { + lens.get(astgen)[field_idx] = 0; } } - - if (!block_scope.isEmpty()) { - _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); - } + assert(scan_result.has_underscore_field == (opt_nonexhaustive_node != .none)); + assert(next_field_idx == fields_len); + wip_decls.finish(); var fields_hash: std.zig.SrcHash = undefined; astgen.src_hasher.final(&fields_hash); - const body = block_scope.instructionsSlice(); - const body_len = astgen.countBodyLenAfterFixups(body); - try gz.setEnum(decl_inst, .{ .src_node = node, - .nonexhaustive = nonexhaustive, - .tag_type = arg_inst, - .captures_len = @intCast(namespace.captures.count()), - .body_len = body_len, - .fields_len = @intCast(counts.total_fields), - .decls_len = @intCast(counts.decls), - .fields_hash = fields_hash, .name_strat = name_strat, + .tag_type_body_len = tag_type_body_len, + .nonexhaustive = scan_result.has_underscore_field, + .decls_len = scan_result.decls_len, + .fields_len = fields_len, + .any_field_values = scan_result.any_field_values, + .fields_hash = fields_hash, + .captures = namespace.captures.keys(), + .capture_names = namespace.captures.values(), + .remaining = scratch.all().get(astgen), }); - wip_members.finishBits(bits_per_field); - const decls_slice = wip_members.declsSlice(); - const fields_slice = wip_members.fieldsSlice(); - try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() * 2 + decls_slice.len + body_len + fields_slice.len); - astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys())); - astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values())); - astgen.extra.appendSliceAssumeCapacity(decls_slice); - astgen.appendBodyWithFixups(body); - astgen.extra.appendSliceAssumeCapacity(fields_slice); - block_scope.unstack(); return rvalue(gz, ri, decl_inst.toRef(), node); }, .keyword_opaque => { assert(container_decl.ast.arg == .none); + astgen.advanceSourceCursorToNode(node); + const decl_inst = try gz.reserveInstructionIndex(); var namespace: Scope.Namespace = .{ @@ -5726,7 +5588,6 @@ fn containerDecl( }; defer namespace.deinit(gpa); - astgen.advanceSourceCursorToNode(node); var block_scope: GenZir = .{ .parent = &namespace.base, .decl_node_index = node, @@ -5738,36 +5599,34 @@ fn containerDecl( }; defer block_scope.unstack(); - const decl_count = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"opaque"); + const scan_result = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"opaque"); - var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, 0, 0, 0); - defer wip_members.deinit(); + var scratch: Scratch = .init(astgen); + defer scratch.reset(); + var wip_decls: WipDecls = try .init(&scratch, scan_result.decls_len); if (container_decl.layout_token) |layout_token| { return astgen.failTok(layout_token, "opaque types do not support 'packed' or 'extern'", .{}); } for (container_decl.ast.members) |member_node| { - const res = try containerMember(&block_scope, &namespace.base, &wip_members, member_node); - if (res == .field) { - return astgen.failNode(member_node, "opaque types cannot have fields", .{}); + switch (try containerMember(&block_scope, &namespace.base, &wip_decls, member_node)) { + .decl => {}, + .field => return astgen.failNode(member_node, "opaque types cannot have fields", .{}), } } + wip_decls.finish(); + try gz.setOpaque(decl_inst, .{ .src_node = node, - .captures_len = @intCast(namespace.captures.count()), - .decls_len = decl_count, .name_strat = name_strat, + .decls_len = scan_result.decls_len, + .captures = namespace.captures.keys(), + .capture_names = namespace.captures.values(), + .decls = @ptrCast(scratch.all().get(astgen)), }); - wip_members.finishBits(0); - const decls_slice = wip_members.declsSlice(); - try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() * 2 + decls_slice.len); - astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys())); - astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values())); - astgen.extra.appendSliceAssumeCapacity(decls_slice); - block_scope.unstack(); return rvalue(gz, ri, decl_inst.toRef(), node); }, @@ -5780,7 +5639,7 @@ const ContainerMemberResult = union(enum) { decl, field: Ast.full.ContainerField fn containerMember( gz: *GenZir, scope: *Scope, - wip_members: *WipMembers, + wip_decls: *WipDecls, member_node: Ast.Node.Index, ) InnerError!ContainerMemberResult { const astgen = gz.astgen; @@ -5805,13 +5664,13 @@ fn containerMember( else .none; - const prev_decl_index = wip_members.decl_index; - astgen.fnDecl(gz, scope, wip_members, member_node, body, full) catch |err| switch (err) { + const prev_decl_index = wip_decls.index; + astgen.fnDecl(gz, scope, wip_decls, member_node, body, full) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => { - wip_members.decl_index = prev_decl_index; + wip_decls.index = prev_decl_index; try addFailedDeclaration( - wip_members, + wip_decls, gz, .@"const", try astgen.identAsString(full.name_token.?), @@ -5828,13 +5687,13 @@ fn containerMember( .aligned_var_decl, => { const full = tree.fullVarDecl(member_node).?; - const prev_decl_index = wip_members.decl_index; - astgen.globalVarDecl(gz, scope, wip_members, member_node, full) catch |err| switch (err) { + const prev_decl_index = wip_decls.index; + astgen.globalVarDecl(gz, scope, wip_decls, member_node, full) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => { - wip_members.decl_index = prev_decl_index; + wip_decls.index = prev_decl_index; try addFailedDeclaration( - wip_members, + wip_decls, gz, .@"const", // doesn't really matter try astgen.identAsString(full.ast.mut_token + 1), @@ -5846,13 +5705,13 @@ fn containerMember( }, .@"comptime" => { - const prev_decl_index = wip_members.decl_index; - astgen.comptimeDecl(gz, scope, wip_members, member_node) catch |err| switch (err) { + const prev_decl_index = wip_decls.index; + astgen.comptimeDecl(gz, scope, wip_decls, member_node) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => { - wip_members.decl_index = prev_decl_index; + wip_decls.index = prev_decl_index; try addFailedDeclaration( - wip_members, + wip_decls, gz, .@"comptime", .empty, @@ -5863,16 +5722,16 @@ fn containerMember( }; }, .test_decl => { - const prev_decl_index = wip_members.decl_index; + const prev_decl_index = wip_decls.index; // We need to have *some* decl here so that the decl count matches what's expected. // Since it doesn't strictly matter *what* this is, let's save ourselves the trouble // of duplicating the test name logic, and just assume this is an unnamed test. - astgen.testDecl(gz, scope, wip_members, member_node) catch |err| switch (err) { + astgen.testDecl(gz, scope, wip_decls, member_node) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => { - wip_members.decl_index = prev_decl_index; + wip_decls.index = prev_decl_index; try addFailedDeclaration( - wip_members, + wip_decls, gz, .unnamed_test, .empty, @@ -10619,482 +10478,6 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev } } -/// Returns `true` if it is known the type expression has more than one possible value; -/// `false` otherwise. -fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.Index) bool { - var node = start_node; - while (true) { - switch (tree.nodeTag(node)) { - .root, - .test_decl, - .switch_case, - .switch_case_inline, - .switch_case_one, - .switch_case_inline_one, - .container_field_init, - .container_field_align, - .container_field, - .asm_output, - .asm_input, - .global_var_decl, - .local_var_decl, - .simple_var_decl, - .aligned_var_decl, - => unreachable, - - .@"return", - .@"break", - .@"continue", - .bit_not, - .bool_not, - .@"defer", - .@"errdefer", - .address_of, - .negation, - .negation_wrap, - .@"resume", - .array_type, - .@"suspend", - .fn_decl, - .anyframe_literal, - .number_literal, - .enum_literal, - .string_literal, - .multiline_string_literal, - .char_literal, - .unreachable_literal, - .error_set_decl, - .container_decl, - .container_decl_trailing, - .container_decl_two, - .container_decl_two_trailing, - .container_decl_arg, - .container_decl_arg_trailing, - .tagged_union, - .tagged_union_trailing, - .tagged_union_two, - .tagged_union_two_trailing, - .tagged_union_enum_tag, - .tagged_union_enum_tag_trailing, - .@"asm", - .asm_simple, - .add, - .add_wrap, - .add_sat, - .array_cat, - .array_mult, - .assign, - .assign_destructure, - .assign_bit_and, - .assign_bit_or, - .assign_shl, - .assign_shl_sat, - .assign_shr, - .assign_bit_xor, - .assign_div, - .assign_sub, - .assign_sub_wrap, - .assign_sub_sat, - .assign_mod, - .assign_add, - .assign_add_wrap, - .assign_add_sat, - .assign_mul, - .assign_mul_wrap, - .assign_mul_sat, - .bang_equal, - .bit_and, - .bit_or, - .shl, - .shl_sat, - .shr, - .bit_xor, - .bool_and, - .bool_or, - .div, - .equal_equal, - .error_union, - .greater_or_equal, - .greater_than, - .less_or_equal, - .less_than, - .merge_error_sets, - .mod, - .mul, - .mul_wrap, - .mul_sat, - .switch_range, - .for_range, - .field_access, - .sub, - .sub_wrap, - .sub_sat, - .slice, - .slice_open, - .slice_sentinel, - .deref, - .array_access, - .error_value, - .while_simple, - .while_cont, - .for_simple, - .if_simple, - .@"catch", - .@"orelse", - .array_init_one, - .array_init_one_comma, - .array_init_dot_two, - .array_init_dot_two_comma, - .array_init_dot, - .array_init_dot_comma, - .array_init, - .array_init_comma, - .struct_init_one, - .struct_init_one_comma, - .struct_init_dot_two, - .struct_init_dot_two_comma, - .struct_init_dot, - .struct_init_dot_comma, - .struct_init, - .struct_init_comma, - .@"while", - .@"if", - .@"for", - .@"switch", - .switch_comma, - .call_one, - .call_one_comma, - .call, - .call_comma, - .block_two, - .block_two_semicolon, - .block, - .block_semicolon, - .builtin_call, - .builtin_call_comma, - .builtin_call_two, - .builtin_call_two_comma, - // these are function bodies, not pointers - .fn_proto_simple, - .fn_proto_multi, - .fn_proto_one, - .fn_proto, - => return false, - - // Forward the question to the LHS sub-expression. - .@"try", - .@"comptime", - .@"nosuspend", - => node = tree.nodeData(node).node, - .grouped_expression, - .unwrap_optional, - => node = tree.nodeData(node).node_and_token[0], - - .ptr_type_aligned, - .ptr_type_sentinel, - .ptr_type, - .ptr_type_bit_range, - .optional_type, - .anyframe_type, - .array_type_sentinel, - => return true, - - .identifier => { - const ident_bytes = tree.tokenSlice(tree.nodeMainToken(node)); - if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) { - .anyerror_type, - .anyframe_type, - .anyopaque_type, - .bool_type, - .c_int_type, - .c_long_type, - .c_longdouble_type, - .c_longlong_type, - .c_char_type, - .c_short_type, - .c_uint_type, - .c_ulong_type, - .c_ulonglong_type, - .c_ushort_type, - .comptime_float_type, - .comptime_int_type, - .f16_type, - .f32_type, - .f64_type, - .f80_type, - .f128_type, - .i16_type, - .i32_type, - .i64_type, - .i128_type, - .i8_type, - .isize_type, - .type_type, - .u16_type, - .u29_type, - .u32_type, - .u64_type, - .u128_type, - .u1_type, - .u8_type, - .usize_type, - => return true, - - .void_type, - .bool_false, - .bool_true, - .null_value, - .undef, - .noreturn_type, - => return false, - - else => unreachable, // that's all the values from `primitives`. - } else { - return false; - } - }, - } - } -} - -/// Returns `true` if it is known the expression is a type that cannot be used at runtime; -/// `false` otherwise. -fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { - var node = start_node; - while (true) { - switch (tree.nodeTag(node)) { - .root, - .test_decl, - .switch_case, - .switch_case_inline, - .switch_case_one, - .switch_case_inline_one, - .container_field_init, - .container_field_align, - .container_field, - .asm_output, - .asm_input, - .global_var_decl, - .local_var_decl, - .simple_var_decl, - .aligned_var_decl, - => unreachable, - - .@"return", - .@"break", - .@"continue", - .bit_not, - .bool_not, - .@"defer", - .@"errdefer", - .address_of, - .negation, - .negation_wrap, - .@"resume", - .array_type, - .@"suspend", - .fn_decl, - .anyframe_literal, - .number_literal, - .enum_literal, - .string_literal, - .multiline_string_literal, - .char_literal, - .unreachable_literal, - .error_set_decl, - .container_decl, - .container_decl_trailing, - .container_decl_two, - .container_decl_two_trailing, - .container_decl_arg, - .container_decl_arg_trailing, - .tagged_union, - .tagged_union_trailing, - .tagged_union_two, - .tagged_union_two_trailing, - .tagged_union_enum_tag, - .tagged_union_enum_tag_trailing, - .@"asm", - .asm_simple, - .add, - .add_wrap, - .add_sat, - .array_cat, - .array_mult, - .assign, - .assign_destructure, - .assign_bit_and, - .assign_bit_or, - .assign_shl, - .assign_shl_sat, - .assign_shr, - .assign_bit_xor, - .assign_div, - .assign_sub, - .assign_sub_wrap, - .assign_sub_sat, - .assign_mod, - .assign_add, - .assign_add_wrap, - .assign_add_sat, - .assign_mul, - .assign_mul_wrap, - .assign_mul_sat, - .bang_equal, - .bit_and, - .bit_or, - .shl, - .shl_sat, - .shr, - .bit_xor, - .bool_and, - .bool_or, - .div, - .equal_equal, - .error_union, - .greater_or_equal, - .greater_than, - .less_or_equal, - .less_than, - .merge_error_sets, - .mod, - .mul, - .mul_wrap, - .mul_sat, - .switch_range, - .for_range, - .field_access, - .sub, - .sub_wrap, - .sub_sat, - .slice, - .slice_open, - .slice_sentinel, - .deref, - .array_access, - .error_value, - .while_simple, - .while_cont, - .for_simple, - .if_simple, - .@"catch", - .@"orelse", - .array_init_one, - .array_init_one_comma, - .array_init_dot_two, - .array_init_dot_two_comma, - .array_init_dot, - .array_init_dot_comma, - .array_init, - .array_init_comma, - .struct_init_one, - .struct_init_one_comma, - .struct_init_dot_two, - .struct_init_dot_two_comma, - .struct_init_dot, - .struct_init_dot_comma, - .struct_init, - .struct_init_comma, - .@"while", - .@"if", - .@"for", - .@"switch", - .switch_comma, - .call_one, - .call_one_comma, - .call, - .call_comma, - .block_two, - .block_two_semicolon, - .block, - .block_semicolon, - .builtin_call, - .builtin_call_comma, - .builtin_call_two, - .builtin_call_two_comma, - .ptr_type_aligned, - .ptr_type_sentinel, - .ptr_type, - .ptr_type_bit_range, - .optional_type, - .anyframe_type, - .array_type_sentinel, - => return false, - - // these are function bodies, not pointers - .fn_proto_simple, - .fn_proto_multi, - .fn_proto_one, - .fn_proto, - => return true, - - // Forward the question to the LHS sub-expression. - .@"try", - .@"comptime", - .@"nosuspend", - => node = tree.nodeData(node).node, - .grouped_expression, - .unwrap_optional, - => node = tree.nodeData(node).node_and_token[0], - - .identifier => { - const ident_bytes = tree.tokenSlice(tree.nodeMainToken(node)); - if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) { - .anyerror_type, - .anyframe_type, - .anyopaque_type, - .bool_type, - .c_int_type, - .c_long_type, - .c_longdouble_type, - .c_longlong_type, - .c_char_type, - .c_short_type, - .c_uint_type, - .c_ulong_type, - .c_ulonglong_type, - .c_ushort_type, - .f16_type, - .f32_type, - .f64_type, - .f80_type, - .f128_type, - .i16_type, - .i32_type, - .i64_type, - .i128_type, - .i8_type, - .isize_type, - .u16_type, - .u29_type, - .u32_type, - .u64_type, - .u128_type, - .u1_type, - .u8_type, - .usize_type, - .void_type, - .bool_false, - .bool_true, - .null_value, - .undef, - .noreturn_type, - => return false, - - .comptime_float_type, - .comptime_int_type, - .type_type, - => return true, - - else => unreachable, // that's all the values from `primitives`. - } else { - return false; - } - }, - } - } -} - /// Applies `rl` semantics to `result`. Expressions which do not do their own handling of /// result locations must call this function on their result. /// As an example, if `ri.rl` is `.ptr`, it will write the result to the pointer. @@ -13044,18 +12427,19 @@ const GenZir = struct { fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct { src_node: Ast.Node.Index, - captures_len: u32, - fields_len: u32, - decls_len: u32, - has_backing_int: bool, + name_strat: Zir.Inst.NameStrategy, layout: std.builtin.Type.ContainerLayout, - known_non_opv: bool, - known_comptime_only: bool, + backing_int_type_body_len: ?u32, + decls_len: u32, + fields_len: u32, + any_field_aligns: bool, + any_field_defaults: bool, any_comptime_fields: bool, - any_default_inits: bool, - any_aligned_fields: bool, fields_hash: std.zig.SrcHash, - name_strat: Zir.Inst.NameStrategy, + captures: []const Zir.Inst.Capture, + capture_names: []const Zir.NullTerminatedString, + /// The trailing declaration list, field information, and body instructions. + remaining: []const u32, }) !void { const astgen = gz.astgen; const gpa = astgen.gpa; @@ -13063,9 +12447,16 @@ const GenZir = struct { // Node .root is valid for the root `struct_decl` of a file! assert(args.src_node != .root or gz.parent.tag == .top); + const captures_len: u32 = @intCast(args.captures.len); + assert(args.capture_names.len == captures_len); + const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash); - try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len + 3); + try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len + + 4 + // `captures_len`, `decls_len`, `fields_len`, `backing_int_type_body_len` + captures_len * 2 + // `capture`, `capture_name` + args.remaining.len); + const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.StructDecl{ .fields_hash_0 = fields_hash_arr[0], .fields_hash_1 = fields_hash_arr[1], @@ -13075,31 +12466,28 @@ const GenZir = struct { .src_node = args.src_node, }); - if (args.captures_len != 0) { - astgen.extra.appendAssumeCapacity(args.captures_len); - } - if (args.fields_len != 0) { - astgen.extra.appendAssumeCapacity(args.fields_len); - } - if (args.decls_len != 0) { - astgen.extra.appendAssumeCapacity(args.decls_len); - } + if (captures_len != 0) astgen.extra.appendAssumeCapacity(captures_len); + if (args.decls_len != 0) astgen.extra.appendAssumeCapacity(args.decls_len); + if (args.fields_len != 0) astgen.extra.appendAssumeCapacity(args.fields_len); + if (args.backing_int_type_body_len) |n| astgen.extra.appendAssumeCapacity(n); + astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.captures)); + astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.capture_names)); + astgen.extra.appendSliceAssumeCapacity(args.remaining); + astgen.instructions.set(@intFromEnum(inst), .{ .tag = .extended, .data = .{ .extended = .{ .opcode = .struct_decl, .small = @bitCast(Zir.Inst.StructDecl.Small{ - .has_captures_len = args.captures_len != 0, - .has_fields_len = args.fields_len != 0, + .has_captures_len = captures_len != 0, .has_decls_len = args.decls_len != 0, - .has_backing_int = args.has_backing_int, - .known_non_opv = args.known_non_opv, - .known_comptime_only = args.known_comptime_only, + .has_fields_len = args.fields_len != 0, .name_strategy = args.name_strat, .layout = args.layout, + .has_backing_int_type = args.backing_int_type_body_len != null, + .any_field_aligns = args.any_field_aligns, + .any_field_defaults = args.any_field_defaults, .any_comptime_fields = args.any_comptime_fields, - .any_default_inits = args.any_default_inits, - .any_aligned_fields = args.any_aligned_fields, }), .operand = payload_index, } }, @@ -13108,25 +12496,34 @@ const GenZir = struct { fn setUnion(gz: *GenZir, inst: Zir.Inst.Index, args: struct { src_node: Ast.Node.Index, - tag_type: Zir.Inst.Ref, - captures_len: u32, - body_len: u32, - fields_len: u32, + name_strat: Zir.Inst.NameStrategy, + kind: Zir.Inst.UnionDecl.Kind, + arg_type_body_len: ?u32, decls_len: u32, - layout: std.builtin.Type.ContainerLayout, - auto_enum_tag: bool, - any_aligned_fields: bool, + fields_len: u32, + any_field_aligns: bool, + any_field_values: bool, fields_hash: std.zig.SrcHash, - name_strat: Zir.Inst.NameStrategy, + captures: []const Zir.Inst.Capture, + capture_names: []const Zir.NullTerminatedString, + /// The trailing declaration list, field information, and body instructions. + remaining: []const u32, }) !void { const astgen = gz.astgen; const gpa = astgen.gpa; assert(args.src_node != .root); + const captures_len: u32 = @intCast(args.captures.len); + assert(args.capture_names.len == captures_len); + const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash); - try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.UnionDecl).@"struct".fields.len + 5); + try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.UnionDecl).@"struct".fields.len + + 4 + // `captures_len`, `decls_len`, `fields_len`, `arg_type_body_len` + captures_len * 2 + // `capture`, `capture_name` + args.remaining.len); + const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.UnionDecl{ .fields_hash_0 = fields_hash_arr[0], .fields_hash_1 = fields_hash_arr[1], @@ -13136,35 +12533,30 @@ const GenZir = struct { .src_node = args.src_node, }); - if (args.tag_type != .none) { - astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type)); - } - if (args.captures_len != 0) { - astgen.extra.appendAssumeCapacity(args.captures_len); - } - if (args.body_len != 0) { - astgen.extra.appendAssumeCapacity(args.body_len); - } - if (args.fields_len != 0) { - astgen.extra.appendAssumeCapacity(args.fields_len); - } - if (args.decls_len != 0) { - astgen.extra.appendAssumeCapacity(args.decls_len); + if (captures_len != 0) astgen.extra.appendAssumeCapacity(captures_len); + if (args.decls_len != 0) astgen.extra.appendAssumeCapacity(args.decls_len); + if (args.fields_len != 0) astgen.extra.appendAssumeCapacity(args.fields_len); + if (args.kind.hasArgType()) { + astgen.extra.appendAssumeCapacity(args.arg_type_body_len.?); + } else { + assert(args.arg_type_body_len == null); } + astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.captures)); + astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.capture_names)); + astgen.extra.appendSliceAssumeCapacity(args.remaining); + astgen.instructions.set(@intFromEnum(inst), .{ .tag = .extended, .data = .{ .extended = .{ .opcode = .union_decl, .small = @bitCast(Zir.Inst.UnionDecl.Small{ - .has_tag_type = args.tag_type != .none, - .has_captures_len = args.captures_len != 0, - .has_body_len = args.body_len != 0, - .has_fields_len = args.fields_len != 0, + .has_captures_len = captures_len != 0, .has_decls_len = args.decls_len != 0, + .has_fields_len = args.fields_len != 0, .name_strategy = args.name_strat, - .layout = args.layout, - .auto_enum_tag = args.auto_enum_tag, - .any_aligned_fields = args.any_aligned_fields, + .kind = args.kind, + .any_field_aligns = args.any_field_aligns, + .any_field_values = args.any_field_values, }), .operand = payload_index, } }, @@ -13173,23 +12565,33 @@ const GenZir = struct { fn setEnum(gz: *GenZir, inst: Zir.Inst.Index, args: struct { src_node: Ast.Node.Index, - tag_type: Zir.Inst.Ref, - captures_len: u32, - body_len: u32, - fields_len: u32, - decls_len: u32, + name_strat: Zir.Inst.NameStrategy, + tag_type_body_len: ?u32, nonexhaustive: bool, + decls_len: u32, + fields_len: u32, + any_field_values: bool, fields_hash: std.zig.SrcHash, - name_strat: Zir.Inst.NameStrategy, + captures: []const Zir.Inst.Capture, + capture_names: []const Zir.NullTerminatedString, + /// The trailing declaration list, field information, and body instructions. + remaining: []const u32, }) !void { const astgen = gz.astgen; const gpa = astgen.gpa; assert(args.src_node != .root); + const captures_len: u32 = @intCast(args.captures.len); + assert(args.capture_names.len == captures_len); + const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash); - try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.EnumDecl).@"struct".fields.len + 5); + try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.EnumDecl).@"struct".fields.len + + 4 + // `captures_len`, `decls_len`, `fields_len`, `tag_type_body_len` + captures_len * 2 + // `capture`, `capture_name` + args.remaining.len); + const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.EnumDecl{ .fields_hash_0 = fields_hash_arr[0], .fields_hash_1 = fields_hash_arr[1], @@ -13199,33 +12601,26 @@ const GenZir = struct { .src_node = args.src_node, }); - if (args.tag_type != .none) { - astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type)); - } - if (args.captures_len != 0) { - astgen.extra.appendAssumeCapacity(args.captures_len); - } - if (args.body_len != 0) { - astgen.extra.appendAssumeCapacity(args.body_len); - } - if (args.fields_len != 0) { - astgen.extra.appendAssumeCapacity(args.fields_len); - } - if (args.decls_len != 0) { - astgen.extra.appendAssumeCapacity(args.decls_len); - } + if (captures_len != 0) astgen.extra.appendAssumeCapacity(captures_len); + if (args.decls_len != 0) astgen.extra.appendAssumeCapacity(args.decls_len); + if (args.fields_len != 0) astgen.extra.appendAssumeCapacity(args.fields_len); + if (args.tag_type_body_len) |n| astgen.extra.appendAssumeCapacity(n); + astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.captures)); + astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.capture_names)); + astgen.extra.appendSliceAssumeCapacity(args.remaining); + astgen.instructions.set(@intFromEnum(inst), .{ .tag = .extended, .data = .{ .extended = .{ .opcode = .enum_decl, .small = @bitCast(Zir.Inst.EnumDecl.Small{ - .has_tag_type = args.tag_type != .none, - .has_captures_len = args.captures_len != 0, - .has_body_len = args.body_len != 0, - .has_fields_len = args.fields_len != 0, + .has_captures_len = captures_len != 0, .has_decls_len = args.decls_len != 0, + .has_fields_len = args.fields_len != 0, .name_strategy = args.name_strat, + .has_tag_type = args.tag_type_body_len != null, .nonexhaustive = args.nonexhaustive, + .any_field_values = args.any_field_values, }), .operand = payload_index, } }, @@ -13234,33 +12629,41 @@ const GenZir = struct { fn setOpaque(gz: *GenZir, inst: Zir.Inst.Index, args: struct { src_node: Ast.Node.Index, - captures_len: u32, - decls_len: u32, name_strat: Zir.Inst.NameStrategy, + decls_len: u32, + captures: []const Zir.Inst.Capture, + capture_names: []const Zir.NullTerminatedString, + decls: []const Zir.Inst.Index, }) !void { const astgen = gz.astgen; const gpa = astgen.gpa; assert(args.src_node != .root); - try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.OpaqueDecl).@"struct".fields.len + 2); + const captures_len: u32 = @intCast(args.captures.len); + assert(args.capture_names.len == captures_len); + + try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.OpaqueDecl).@"struct".fields.len + + 2 + // `captures_len`, `decls_len` + captures_len * 2 + // `capture`, `capture_name` + args.decls.len); + const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.OpaqueDecl{ .src_line = astgen.source_line, .src_node = args.src_node, }); + if (captures_len != 0) astgen.extra.appendAssumeCapacity(captures_len); + if (args.decls_len != 0) astgen.extra.appendAssumeCapacity(args.decls_len); + astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.captures)); + astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.capture_names)); + astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.decls)); - if (args.captures_len != 0) { - astgen.extra.appendAssumeCapacity(args.captures_len); - } - if (args.decls_len != 0) { - astgen.extra.appendAssumeCapacity(args.decls_len); - } astgen.instructions.set(@intFromEnum(inst), .{ .tag = .extended, .data = .{ .extended = .{ .opcode = .opaque_decl, .small = @bitCast(Zir.Inst.OpaqueDecl.Small{ - .has_captures_len = args.captures_len != 0, + .has_captures_len = captures_len != 0, .has_decls_len = args.decls_len != 0, .name_strategy = args.name_strat, }), @@ -13484,14 +12887,24 @@ fn restoreSourceCursor(astgen: *AstGen, cursor: SourceCursor) void { astgen.source_column = cursor.column; } +const ScanContainerResult = struct { + /// Includes unnamed declarations (e.g. `comptime` decls) + decls_len: u32, + fields_len: u32, + any_field_aligns: bool, + any_field_values: bool, + any_comptime_fields: bool, + /// Whether there is a field named `_` (indicating a non-exhaustive enum) + has_underscore_field: bool, +}; + /// Detects name conflicts for decls and fields, and populates `namespace.decls` with all named declarations. -/// Returns the number of declarations in the namespace, including unnamed declarations (e.g. `comptime` decls). fn scanContainer( astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.Node.Index, container_kind: enum { @"struct", @"union", @"enum", @"opaque" }, -) !u32 { +) !ScanContainerResult { const gpa = astgen.gpa; const tree = astgen.tree; @@ -13521,6 +12934,10 @@ fn scanContainer( var any_duplicates = false; var decl_count: u32 = 0; + var any_field_aligns = false; + var any_field_values = false; + var any_comptime_fields = false; + var has_underscore_field = false; for (members) |member_node| { const Kind = enum { decl, field }; const kind: Kind, const name_token = switch (tree.nodeTag(member_node)) { @@ -13533,6 +12950,10 @@ fn scanContainer( .@"struct", .@"opaque" => {}, .@"union", .@"enum" => full.convertToNonTupleLike(astgen.tree), } + if (full.ast.align_expr != .none) any_field_aligns = true; + if (full.ast.value_expr != .none) any_field_values = true; + if (full.comptime_token != null) any_comptime_fields = true; + if (mem.eql(u8, tree.tokenSlice(full.ast.main_token), "_")) has_underscore_field = true; if (full.ast.tuple_like) continue; break :blk .{ .field, full.ast.main_token }; }, @@ -13698,7 +13119,14 @@ fn scanContainer( if (!any_duplicates) { if (any_invalid_declarations) return error.AnalysisFail; - return decl_count; + return .{ + .decls_len = decl_count, + .fields_len = @intCast(members.len - decl_count), + .any_field_aligns = any_field_aligns, + .any_field_values = any_field_values, + .any_comptime_fields = any_comptime_fields, + .has_underscore_field = has_underscore_field, + }; } for (names.keys(), names.values()) |name, first| { @@ -13954,7 +13382,7 @@ const DeclarationName = union(enum) { }; fn addFailedDeclaration( - wip_members: *WipMembers, + wip_decls: *WipDecls, gz: *GenZir, kind: Zir.Inst.Declaration.Unwrapped.Kind, name: Zir.NullTerminatedString, @@ -13962,7 +13390,7 @@ fn addFailedDeclaration( is_pub: bool, ) !void { const decl_inst = try gz.makeDeclaration(src_node); - wip_members.nextDecl(decl_inst); + wip_decls.nextDecl(decl_inst); var dummy_gz = gz.makeSubBlock(&gz.base); diff --git a/lib/std/zig/ErrorBundle.zig b/lib/std/zig/ErrorBundle.zig @@ -243,12 +243,14 @@ fn renderErrorMessage( } try t.setColor(.reset); if (src.data.source_line != 0 and options.include_source_line) { + try w.splatByteAll(' ', indent); const line = eb.nullTerminatedString(src.data.source_line); for (line) |b| switch (b) { '\t' => try w.writeByte(' '), else => try w.writeByte(b), }; try w.writeByte('\n'); + try w.splatByteAll(' ', indent); // TODO basic unicode code point monospace width const before_caret = src.data.span_main - src.data.span_start; // -1 since span.main includes the caret @@ -267,11 +269,13 @@ fn renderErrorMessage( if (src.data.reference_trace_len > 0 and options.include_reference_trace) { try t.setColor(.reset); try t.setColor(.dim); + try w.splatByteAll(' ', indent); try w.print("referenced by:\n", .{}); var ref_index = src.end; for (0..src.data.reference_trace_len) |_| { const ref_trace = eb.extraData(ReferenceTrace, ref_index); ref_index = ref_trace.end; + try w.splatByteAll(' ', indent); if (ref_trace.data.src_loc != .none) { const ref_src = eb.getSourceLocation(ref_trace.data.src_loc); try w.print(" {s}: {s}:{d}:{d}\n", .{ @@ -340,9 +344,9 @@ pub const Wip = struct { pub fn init(wip: *Wip, gpa: Allocator) !void { wip.* = .{ .gpa = gpa, - .string_bytes = .{}, - .extra = .{}, - .root_list = .{}, + .string_bytes = .empty, + .extra = .empty, + .root_list = .empty, }; // So that 0 can be used to indicate a null string. @@ -371,9 +375,9 @@ pub const Wip = struct { wip.deinit(); wip.* = .{ .gpa = gpa, - .string_bytes = .{}, - .extra = .{}, - .root_list = .{}, + .string_bytes = .empty, + .extra = .empty, + .root_list = .empty, }; return empty; } diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig @@ -2443,7 +2443,7 @@ pub const Inst = struct { has_align: bool, has_addrspace: bool, has_bit_range: bool, - _: u1 = undefined, + _: u1 = 0, }, size: std.builtin.Type.Pointer.Size, /// Index into extra. See `PtrType`. @@ -2668,7 +2668,7 @@ pub const Inst = struct { has_ret_ty_body: bool, has_any_noalias: bool, ret_ty_is_generic: bool, - _: u23 = undefined, + _: u23 = 0, }; }; @@ -3134,7 +3134,7 @@ pub const Inst = struct { pub const Flags = packed struct { is_nosuspend: bool, ensure_result_used: bool, - _: u30 = undefined, + _: u30 = 0, comptime { if (@sizeOf(Flags) != 4 or @bitSizeOf(Flags) != 32) @@ -3462,33 +3462,21 @@ pub const Inst = struct { }; /// Trailing: - /// 0. captures_len: u32 // if has_captures_len - /// 1. fields_len: u32, // if has_fields_len - /// 2. decls_len: u32, // if has_decls_len - /// 3. capture: Capture // for every captures_len - /// 4. capture_name: NullTerminatedString // for every captures_len - /// 5. backing_int_body_len: u32, // if has_backing_int - /// 6. backing_int_ref: Ref, // if has_backing_int and backing_int_body_len is 0 - /// 7. backing_int_body_inst: Inst, // if has_backing_int and backing_int_body_len is > 0 - /// 8. decl: Index, // for every decls_len; points to a `declaration` instruction - /// 9. flags: u32 // for every 8 fields - /// - sets of 4 bits: - /// 0b000X: whether corresponding field has an align expression - /// 0b00X0: whether corresponding field has a default expression - /// 0b0X00: whether corresponding field is comptime - /// 0bX000: whether corresponding field has a type expression - /// 10. fields: { // for every fields_len - /// field_name: u32, - /// field_type: Ref, // if corresponding bit is not set. none means anytype. - /// field_type_body_len: u32, // if corresponding bit is set - /// align_body_len: u32, // if corresponding bit is set - /// init_body_len: u32, // if corresponding bit is set - /// } - /// 11. bodies: { // for every fields_len - /// field_type_body_inst: Inst, // for each field_type_body_len - /// align_body_inst: Inst, // for each align_body_len - /// init_body_inst: Inst, // for each init_body_len - /// } + /// 0. captures_len: u32 // if `has_captures_len` + /// 1. decls_len: u32, // if `has_decls_len` + /// 2. fields_len: u32, // if `has_fields_len` + /// 3. backing_int_body_len: u32 // if `has_backing_int` + /// 4. capture: Capture // for every `captures_len` + /// 5. capture_name: NullTerminatedString // for every `captures_len` + /// 6. decl: Index, // for every `decls_len`; points to a `declaration` instruction + /// 7. field_name: NullTerminatedString // for every `fields_len` + /// 8. field_type_body_len: u32 // for every `fields_len` + /// 9. field_align_body_len: u32 // for every `fields_len` if `any_field_aligns` + /// 10. field_default_body_len: u32 // for every `fields_len` if `any_field_defaults` + /// 11. field_comptime_bits: u32 // one bit per `fields_len` if `any_comptime_fields` + /// // LSB is first field, minimum number of `u32` needed + /// 12. backing_int_body_inst: Inst.Index // for each `backing_int_body_len` + /// 13. body_inst: Inst.Index // type body, then align body, then default body, for each field pub const StructDecl = struct { // These fields should be concatenated and reinterpreted as a `std.zig.SrcHash`. // This hash contains the source of all fields, and any specified attributes (`extern`, backing type, etc). @@ -3500,19 +3488,18 @@ pub const Inst = struct { /// This node provides a new absolute baseline node for all instructions within this struct. src_node: Ast.Node.Index, - pub const Small = packed struct { + pub const Small = packed struct(u16) { has_captures_len: bool, - has_fields_len: bool, has_decls_len: bool, - has_backing_int: bool, - known_non_opv: bool, - known_comptime_only: bool, + has_fields_len: bool, name_strategy: NameStrategy, layout: std.builtin.Type.ContainerLayout, - any_default_inits: bool, + /// Always `false` if `layout != .@"packed"`. + has_backing_int_type: bool, + any_field_aligns: bool, + any_field_defaults: bool, any_comptime_fields: bool, - any_aligned_fields: bool, - _: u3 = undefined, + _: u5 = 0, }; }; @@ -3633,21 +3620,17 @@ pub const Inst = struct { }; /// Trailing: - /// 0. tag_type: Ref, // if has_tag_type - /// 1. captures_len: u32, // if has_captures_len - /// 2. body_len: u32, // if has_body_len - /// 3. fields_len: u32, // if has_fields_len - /// 4. decls_len: u32, // if has_decls_len - /// 5. capture: Capture // for every captures_len - /// 6. capture_name: NullTerminatedString // for every captures_len - /// 7. decl: Index, // for every decls_len; points to a `declaration` instruction - /// 8. inst: Index // for every body_len - /// 9. has_bits: u32 // for every 32 fields - /// - the bit is whether corresponding field has an value expression - /// 10. fields: { // for every fields_len - /// field_name: u32, - /// value: Ref, // if corresponding bit is set - /// } + /// 0. captures_len: u32, // if has_captures_len + /// 1. decls_len: u32, // if has_decls_len + /// 2. fields_len: u32, // if has_fields_len + /// 3. tag_type_body_len: u32, // if has_tag_type + /// 4. capture: Capture // for every `captures_len` + /// 5. capture_name: NullTerminatedString // for every `captures_len` + /// 6. decl: Index, // for every `decls_len`; points to a `declaration` instruction + /// 7. field_name: NullTerminatedString // for every `fields_len` + /// 8. field_value_body_len: u32 // for every `fields_len` if `any_field_values` + /// 9. tag_type_body_inst: Inst.Index // for each `tag_type_body_len` + /// 10. body_inst: Inst.Index // value body for each field pub const EnumDecl = struct { // These fields should be concatenated and reinterpreted as a `std.zig.SrcHash`. // This hash contains the source of all fields, and the backing type if specified. @@ -3659,40 +3642,32 @@ pub const Inst = struct { /// This node provides a new absolute baseline node for all instructions within this struct. src_node: Ast.Node.Index, - pub const Small = packed struct { - has_tag_type: bool, + pub const Small = packed struct(u16) { has_captures_len: bool, - has_body_len: bool, - has_fields_len: bool, has_decls_len: bool, + has_fields_len: bool, name_strategy: NameStrategy, + has_tag_type: bool, nonexhaustive: bool, - _: u8 = undefined, + any_field_values: bool, + _: u8 = 0, }; }; /// Trailing: - /// 0. tag_type: Ref, // if has_tag_type - /// 1. captures_len: u32 // if has_captures_len - /// 2. body_len: u32, // if has_body_len - /// 3. fields_len: u32, // if has_fields_len - /// 4. decls_len: u32, // if has_decls_len - /// 5. capture: Capture // for every captures_len - /// 6. capture_name: NullTerminatedString // for every captures_len - /// 7. decl: Index, // for every decls_len; points to a `declaration` instruction - /// 8. inst: Index // for every body_len - /// 9. has_bits: u32 // for every 8 fields - /// - sets of 4 bits: - /// 0b000X: whether corresponding field has a type expression - /// 0b00X0: whether corresponding field has a align expression - /// 0b0X00: whether corresponding field has a tag value expression - /// 0bX000: unused - /// 10. fields: { // for every fields_len - /// field_name: NullTerminatedString, // null terminated string index - /// field_type: Ref, // if corresponding bit is set - /// align: Ref, // if corresponding bit is set - /// tag_value: Ref, // if corresponding bit is set - /// } + /// 0. captures_len: u32 // if `has_captures_len` + /// 1. decls_len: u32, // if `has_decls_len` + /// 2. fields_len: u32, // if `has_fields_len` + /// 3. arg_type_body_len: u32, // if `kind.hasArgType()` + /// 4. capture: Capture // for every `captures_len` + /// 5. capture_name: NullTerminatedString // for every `captures_len` + /// 6. decl: Index, // for every `decls_len`; points to a `declaration` instruction + /// 7. field_name: NullTerminatedString // for every `fields_len` + /// 8. field_type_body_len: u32 // for every `fields_len` + /// 9 . field_align_body_len: u32 // for every `fields_len` if `any_field_aligns` + /// 10. field_value_body_len: u32 // for every `fields_len` if `any_field_values` + /// 11. arg_type_body_inst: Inst.Index // for each `arg_type_body_len` + /// 12. body_inst: Inst.Index // type body, then align body, then value body, for each field pub const UnionDecl = struct { // These fields should be concatenated and reinterpreted as a `std.zig.SrcHash`. // This hash contains the source of all fields, and any specified attributes (`extern` etc). @@ -3704,23 +3679,47 @@ pub const Inst = struct { /// This node provides a new absolute baseline node for all instructions within this struct. src_node: Ast.Node.Index, - pub const Small = packed struct { - has_tag_type: bool, + pub const Small = packed struct(u16) { has_captures_len: bool, - has_body_len: bool, - has_fields_len: bool, has_decls_len: bool, + has_fields_len: bool, name_strategy: NameStrategy, - layout: std.builtin.Type.ContainerLayout, - /// has_tag_type | auto_enum_tag | result - /// ------------------------------------- - /// false | false | union { } - /// false | true | union(enum) { } - /// true | true | union(enum(T)) { } - /// true | false | union(T) { } - auto_enum_tag: bool, - any_aligned_fields: bool, - _: u5 = undefined, + kind: Kind, + any_field_aligns: bool, + any_field_values: bool, + _: u6 = 0, + }; + + pub const Kind = enum(u3) { + /// `union` + auto, + /// `union(T)` + tagged_explicit, + /// `union(enum)` + tagged_enum, + /// `union(enum(T))` + tagged_enum_explicit, + /// `extern union` + @"extern", + /// `packed union` + @"packed", + /// `packed union(T)` + packed_explicit, + + pub fn hasArgType(k: Kind) bool { + return switch (k) { + .auto, .tagged_enum, .@"extern", .@"packed" => false, + .tagged_explicit, .tagged_enum_explicit, .packed_explicit => true, + }; + } + + pub fn layout(k: Kind) std.builtin.Type.ContainerLayout { + return switch (k) { + .auto, .tagged_explicit, .tagged_enum, .tagged_enum_explicit => .auto, + .@"extern" => .@"extern", + .@"packed", .packed_explicit => .@"packed", + }; + } }; }; @@ -3735,11 +3734,11 @@ pub const Inst = struct { /// This node provides a new absolute baseline node for all instructions within this struct. src_node: Ast.Node.Index, - pub const Small = packed struct { + pub const Small = packed struct(u16) { has_captures_len: bool, has_decls_len: bool, name_strategy: NameStrategy, - _: u12 = undefined, + _: u12 = 0, }; }; @@ -3904,12 +3903,12 @@ pub const Inst = struct { pub const AllocExtended = struct { src_node: Ast.Node.Offset, - pub const Small = packed struct { + pub const Small = packed struct(u16) { has_type: bool, has_align: bool, is_const: bool, is_comptime: bool, - _: u12 = undefined, + _: u12 = 0, }; }; @@ -4012,135 +4011,6 @@ pub const Inst = struct { }; }; -pub const DeclIterator = struct { - extra_index: u32, - decls_remaining: u32, - zir: Zir, - - pub fn next(it: *DeclIterator) ?Inst.Index { - if (it.decls_remaining == 0) return null; - const decl_inst: Zir.Inst.Index = @enumFromInt(it.zir.extra[it.extra_index]); - it.extra_index += 1; - it.decls_remaining -= 1; - assert(it.zir.instructions.items(.tag)[@intFromEnum(decl_inst)] == .declaration); - return decl_inst; - } -}; - -pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator { - const inst = zir.instructions.get(@intFromEnum(decl_inst)); - assert(inst.tag == .extended); - const extended = inst.data.extended; - switch (extended.opcode) { - .struct_decl => { - const small: Inst.StructDecl.Small = @bitCast(extended.small); - var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.StructDecl).@"struct".fields.len); - const captures_len = if (small.has_captures_len) captures_len: { - const captures_len = zir.extra[extra_index]; - extra_index += 1; - break :captures_len captures_len; - } else 0; - extra_index += @intFromBool(small.has_fields_len); - const decls_len = if (small.has_decls_len) decls_len: { - const decls_len = zir.extra[extra_index]; - extra_index += 1; - break :decls_len decls_len; - } else 0; - - extra_index += captures_len * 2; - - if (small.has_backing_int) { - const backing_int_body_len = zir.extra[extra_index]; - extra_index += 1; // backing_int_body_len - if (backing_int_body_len == 0) { - extra_index += 1; // backing_int_ref - } else { - extra_index += backing_int_body_len; // backing_int_body_inst - } - } - - return .{ - .extra_index = extra_index, - .decls_remaining = decls_len, - .zir = zir, - }; - }, - .enum_decl => { - const small: Inst.EnumDecl.Small = @bitCast(extended.small); - var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.EnumDecl).@"struct".fields.len); - extra_index += @intFromBool(small.has_tag_type); - const captures_len = if (small.has_captures_len) captures_len: { - const captures_len = zir.extra[extra_index]; - extra_index += 1; - break :captures_len captures_len; - } else 0; - extra_index += @intFromBool(small.has_body_len); - extra_index += @intFromBool(small.has_fields_len); - const decls_len = if (small.has_decls_len) decls_len: { - const decls_len = zir.extra[extra_index]; - extra_index += 1; - break :decls_len decls_len; - } else 0; - - extra_index += captures_len * 2; - - return .{ - .extra_index = extra_index, - .decls_remaining = decls_len, - .zir = zir, - }; - }, - .union_decl => { - const small: Inst.UnionDecl.Small = @bitCast(extended.small); - var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.UnionDecl).@"struct".fields.len); - extra_index += @intFromBool(small.has_tag_type); - const captures_len = if (small.has_captures_len) captures_len: { - const captures_len = zir.extra[extra_index]; - extra_index += 1; - break :captures_len captures_len; - } else 0; - extra_index += @intFromBool(small.has_body_len); - extra_index += @intFromBool(small.has_fields_len); - const decls_len = if (small.has_decls_len) decls_len: { - const decls_len = zir.extra[extra_index]; - extra_index += 1; - break :decls_len decls_len; - } else 0; - - extra_index += captures_len * 2; - - return .{ - .extra_index = extra_index, - .decls_remaining = decls_len, - .zir = zir, - }; - }, - .opaque_decl => { - const small: Inst.OpaqueDecl.Small = @bitCast(extended.small); - var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.OpaqueDecl).@"struct".fields.len); - const decls_len = if (small.has_decls_len) decls_len: { - const decls_len = zir.extra[extra_index]; - extra_index += 1; - break :decls_len decls_len; - } else 0; - const captures_len = if (small.has_captures_len) captures_len: { - const captures_len = zir.extra[extra_index]; - extra_index += 1; - break :captures_len captures_len; - } else 0; - - extra_index += captures_len * 2; - - return .{ - .extra_index = extra_index, - .decls_remaining = decls_len, - .zir = zir, - }; - }, - else => unreachable, - } -} - /// `DeclContents` contains all "interesting" instructions found within a declaration by `findTrackable`. /// These instructions are partitioned into a few different sets, since this makes ZIR instruction mapping /// more effective. @@ -4524,7 +4394,7 @@ fn findTrackableInner( try zir.findTrackableBody(gpa, contents, defers, body); }, - // Reifications and opaque declarations need tracking, but have no body. + // Reifications and opaque declarations need tracking, but have no bodies. .reify_enum, .reify_struct, .reify_union, @@ -4535,150 +4405,37 @@ fn findTrackableInner( .struct_decl => { try contents.explicit_types.append(gpa, inst); - const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); - const extra = zir.extraData(Zir.Inst.StructDecl, extended.operand); - var extra_index = extra.end; - const captures_len = if (small.has_captures_len) blk: { - const captures_len = zir.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - const fields_len = if (small.has_fields_len) blk: { - const fields_len = zir.extra[extra_index]; - extra_index += 1; - break :blk fields_len; - } else 0; - const decls_len = if (small.has_decls_len) blk: { - const decls_len = zir.extra[extra_index]; - extra_index += 1; - break :blk decls_len; - } else 0; - extra_index += captures_len * 2; - if (small.has_backing_int) { - const backing_int_body_len = zir.extra[extra_index]; - extra_index += 1; - if (backing_int_body_len == 0) { - extra_index += 1; // backing_int_ref - } else { - const body = zir.bodySlice(extra_index, backing_int_body_len); - extra_index += backing_int_body_len; - try zir.findTrackableBody(gpa, contents, defers, body); - } + const struct_decl = zir.getStructDecl(inst); + var it = struct_decl.iterateFields(); + while (it.next()) |field| { + try zir.findTrackableBody(gpa, contents, defers, field.type_body); + if (field.align_body) |b| try zir.findTrackableBody(gpa, contents, defers, b); + if (field.default_body) |b| try zir.findTrackableBody(gpa, contents, defers, b); } - extra_index += decls_len; - - // This ZIR is structured in a slightly awkward way, so we have to split up the iteration. - // `extra_index` iterates `flags` (bags of bits). - // `fields_extra_index` iterates `fields`. - // We accumulate the total length of bodies into `total_bodies_len`. This is sufficient because - // the bodies are packed together in `extra` and we only need to traverse their instructions (we - // don't really care about the structure). - - const bits_per_field = 4; - const fields_per_u32 = 32 / bits_per_field; - const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; - var cur_bit_bag: u32 = undefined; - - var fields_extra_index = extra_index + bit_bags_count; - var total_bodies_len: u32 = 0; - - for (0..fields_len) |field_i| { - if (field_i % fields_per_u32 == 0) { - cur_bit_bag = zir.extra[extra_index]; - extra_index += 1; - } - - const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - const has_init = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 2; // also skip `is_comptime`; we don't care - const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - - fields_extra_index += 1; // field_name - - if (has_type_body) { - const field_type_body_len = zir.extra[fields_extra_index]; - total_bodies_len += field_type_body_len; - } - fields_extra_index += 1; // field_type or field_type_body_len - - if (has_align) { - const align_body_len = zir.extra[fields_extra_index]; - fields_extra_index += 1; - total_bodies_len += align_body_len; - } - - if (has_init) { - const init_body_len = zir.extra[fields_extra_index]; - fields_extra_index += 1; - total_bodies_len += init_body_len; - } - } - - // Now, `fields_extra_index` points to `bodies`. Let's treat this as one big body. - const merged_bodies = zir.bodySlice(fields_extra_index, total_bodies_len); - try zir.findTrackableBody(gpa, contents, defers, merged_bodies); }, - // Union declarations need tracking and have a body. + // Union declarations need tracking and have bodies. .union_decl => { try contents.explicit_types.append(gpa, inst); - const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); - const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand); - var extra_index = extra.end; - extra_index += @intFromBool(small.has_tag_type); - const captures_len = if (small.has_captures_len) blk: { - const captures_len = zir.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - const body_len = if (small.has_body_len) blk: { - const body_len = zir.extra[extra_index]; - extra_index += 1; - break :blk body_len; - } else 0; - extra_index += @intFromBool(small.has_fields_len); - const decls_len = if (small.has_decls_len) blk: { - const decls_len = zir.extra[extra_index]; - extra_index += 1; - break :blk decls_len; - } else 0; - extra_index += captures_len * 2; - extra_index += decls_len; - const body = zir.bodySlice(extra_index, body_len); - try zir.findTrackableBody(gpa, contents, defers, body); + const union_decl = zir.getUnionDecl(inst); + var it = union_decl.iterateFields(); + while (it.next()) |field| { + if (field.type_body) |b| try zir.findTrackableBody(gpa, contents, defers, b); + if (field.align_body) |b| try zir.findTrackableBody(gpa, contents, defers, b); + if (field.value_body) |b| try zir.findTrackableBody(gpa, contents, defers, b); + } }, - // Enum declarations need tracking and have a body. + // Enum declarations need tracking and have bodies. .enum_decl => { try contents.explicit_types.append(gpa, inst); - const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small); - const extra = zir.extraData(Zir.Inst.EnumDecl, extended.operand); - var extra_index = extra.end; - extra_index += @intFromBool(small.has_tag_type); - const captures_len = if (small.has_captures_len) blk: { - const captures_len = zir.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - const body_len = if (small.has_body_len) blk: { - const body_len = zir.extra[extra_index]; - extra_index += 1; - break :blk body_len; - } else 0; - extra_index += @intFromBool(small.has_fields_len); - const decls_len = if (small.has_decls_len) blk: { - const decls_len = zir.extra[extra_index]; - extra_index += 1; - break :blk decls_len; - } else 0; - extra_index += captures_len * 2; - extra_index += decls_len; - const body = zir.bodySlice(extra_index, body_len); - try zir.findTrackableBody(gpa, contents, defers, body); + const enum_decl = zir.getEnumDecl(inst); + var it = enum_decl.iterateFields(); + while (it.next()) |field| { + if (field.value_body) |b| try zir.findTrackableBody(gpa, contents, defers, b); + } }, } }, @@ -5481,34 +5238,455 @@ pub fn assertTrackable(zir: Zir, inst_idx: Zir.Inst.Index) void { } } -pub fn typeCapturesLen(zir: Zir, type_decl: Inst.Index) u32 { +pub fn typeDecls(zir: Zir, type_decl: Inst.Index) []const Zir.Inst.Index { const inst = zir.instructions.get(@intFromEnum(type_decl)); assert(inst.tag == .extended); - switch (inst.data.extended.opcode) { - .struct_decl => { - const small: Inst.StructDecl.Small = @bitCast(inst.data.extended.small); - if (!small.has_captures_len) return 0; - const extra = zir.extraData(Inst.StructDecl, inst.data.extended.operand); - return zir.extra[extra.end]; - }, - .union_decl => { - const small: Inst.UnionDecl.Small = @bitCast(inst.data.extended.small); - if (!small.has_captures_len) return 0; - const extra = zir.extraData(Inst.UnionDecl, inst.data.extended.operand); - return zir.extra[extra.end + @intFromBool(small.has_tag_type)]; - }, - .enum_decl => { - const small: Inst.EnumDecl.Small = @bitCast(inst.data.extended.small); - if (!small.has_captures_len) return 0; - const extra = zir.extraData(Inst.EnumDecl, inst.data.extended.operand); - return zir.extra[extra.end + @intFromBool(small.has_tag_type)]; - }, - .opaque_decl => { - const small: Inst.OpaqueDecl.Small = @bitCast(inst.data.extended.small); - if (!small.has_captures_len) return 0; - const extra = zir.extraData(Inst.OpaqueDecl, inst.data.extended.operand); - return zir.extra[extra.end]; - }, + return switch (inst.data.extended.opcode) { + .struct_decl => zir.getStructDecl(type_decl).decls, + .union_decl => zir.getUnionDecl(type_decl).decls, + .enum_decl => zir.getEnumDecl(type_decl).decls, + .opaque_decl => zir.getOpaqueDecl(type_decl).decls, else => unreachable, + }; +} + +pub fn getStructDecl(zir: *const Zir, struct_decl: Inst.Index) UnwrappedStructDecl { + const inst_data = zir.instructions.get(@intFromEnum(struct_decl)); + assert(inst_data.tag == .extended); + assert(inst_data.data.extended.opcode == .struct_decl); + const small: Inst.StructDecl.Small = @bitCast(inst_data.data.extended.small); + const extra = zir.extraData(Inst.StructDecl, inst_data.data.extended.operand); + var extra_index = extra.end; + const captures_len: u32 = if (small.has_captures_len) blk: { + const captures_len = zir.extra[extra_index]; + extra_index += 1; + break :blk captures_len; + } else 0; + const decls_len: u32 = if (small.has_decls_len) blk: { + const decls_len = zir.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; + const fields_len: u32 = if (small.has_fields_len) blk: { + const fields_len = zir.extra[extra_index]; + extra_index += 1; + break :blk fields_len; + } else 0; + const backing_int_type_body_len: u32 = if (small.has_backing_int_type) len: { + const body_len = zir.extra[extra_index]; + extra_index += 1; + break :len body_len; + } else 0; + const captures: []const Inst.Capture = @ptrCast(zir.extra[extra_index..][0..captures_len]); + extra_index += captures_len; + const capture_names: []const NullTerminatedString = @ptrCast(zir.extra[extra_index..][0..captures_len]); + extra_index += captures_len; + const decls: []const Inst.Index = @ptrCast(zir.extra[extra_index..][0..decls_len]); + extra_index += decls_len; + const field_names: []const NullTerminatedString = @ptrCast(zir.extra[extra_index..][0..fields_len]); + extra_index += fields_len; + const field_type_body_lens: []const u32 = @ptrCast(zir.extra[extra_index..][0..fields_len]); + extra_index += fields_len; + const field_align_body_lens: ?[]const u32 = if (small.any_field_aligns) lens: { + const lens = zir.extra[extra_index..][0..fields_len]; + extra_index += fields_len; + break :lens @ptrCast(lens); + } else null; + const field_default_body_lens: ?[]const u32 = if (small.any_field_defaults) lens: { + const lens = zir.extra[extra_index..][0..fields_len]; + extra_index += fields_len; + break :lens @ptrCast(lens); + } else null; + const field_comptime_bits: ?[]const u32 = if (small.any_comptime_fields) bits: { + const bits_len = std.math.divCeil(u32, fields_len, 32) catch unreachable; + const bits = zir.extra[extra_index..][0..bits_len]; + extra_index += bits_len; + break :bits bits; + } else null; + const backing_int_type_body: ?[]const Zir.Inst.Index = switch (backing_int_type_body_len) { + 0 => null, + else => |n| zir.bodySlice(extra_index, n), + }; + extra_index += backing_int_type_body_len; + const field_bodies_overlong: []const Inst.Index = @ptrCast(zir.extra[extra_index..]); + return .{ + .src_line = extra.data.src_line, + .src_node = extra.data.src_node, + .name_strategy = small.name_strategy, + .captures = captures, + .capture_names = capture_names, + .decls = decls, + .layout = small.layout, + .backing_int_type_body = backing_int_type_body, + .field_names = field_names, + .field_type_body_lens = field_type_body_lens, + .field_align_body_lens = field_align_body_lens, + .field_default_body_lens = field_default_body_lens, + .field_comptime_bits = field_comptime_bits, + .field_bodies_overlong = field_bodies_overlong, + }; +} +pub const UnwrappedStructDecl = struct { + src_line: u32, + src_node: Ast.Node.Index, + name_strategy: Inst.NameStrategy, + + captures: []const Inst.Capture, + capture_names: []const NullTerminatedString, + + decls: []const Inst.Index, + + layout: std.builtin.Type.ContainerLayout, + backing_int_type_body: ?[]const Inst.Index, + + field_names: []const NullTerminatedString, + field_type_body_lens: []const u32, + field_align_body_lens: ?[]const u32, + field_default_body_lens: ?[]const u32, + field_comptime_bits: ?[]const u32, + field_bodies_overlong: []const Inst.Index, + + pub fn iterateFields(struct_decl: UnwrappedStructDecl) FieldIterator { + return .{ + .next_idx = 0, + .names = struct_decl.field_names, + .type_body_lens = struct_decl.field_type_body_lens, + .align_body_lens = struct_decl.field_align_body_lens, + .default_body_lens = struct_decl.field_default_body_lens, + .comptime_bits = struct_decl.field_comptime_bits, + .bodies_overlong = struct_decl.field_bodies_overlong, + }; } + + pub const FieldIterator = struct { + next_idx: u32, + names: []const NullTerminatedString, + type_body_lens: []const u32, + align_body_lens: ?[]const u32, + default_body_lens: ?[]const u32, + comptime_bits: ?[]const u32, + bodies_overlong: []const Inst.Index, + pub const Field = struct { + idx: u32, + name: NullTerminatedString, + type_body: []const Inst.Index, + align_body: ?[]const Inst.Index, + default_body: ?[]const Inst.Index, + is_comptime: bool, + }; + pub fn next(it: *FieldIterator) ?Field { + const idx = it.next_idx; + if (idx == it.names.len) return null; + it.next_idx += 1; + return .{ + .idx = idx, + .name = it.names[idx], + .type_body = it.body(it.type_body_lens[idx]).?, + .align_body = it.body(if (it.align_body_lens) |l| l[idx] else 0), + .default_body = it.body(if (it.default_body_lens) |l| l[idx] else 0), + .is_comptime = ct: { + const bits = it.comptime_bits orelse break :ct false; + const big = bits[idx / 32]; + const shifted = big >> @intCast(idx % 32); + break :ct @as(u1, @truncate(shifted)) == 1; + }, + }; + } + fn body(it: *FieldIterator, len: u32) ?[]const Inst.Index { + if (len == 0) return null; + const b = it.bodies_overlong[0..len]; + it.bodies_overlong = it.bodies_overlong[len..]; + return b; + } + }; +}; + +pub fn getUnionDecl(zir: *const Zir, union_decl: Inst.Index) UnwrappedUnionDecl { + const inst_data = zir.instructions.get(@intFromEnum(union_decl)); + assert(inst_data.tag == .extended); + assert(inst_data.data.extended.opcode == .union_decl); + const small: Inst.UnionDecl.Small = @bitCast(inst_data.data.extended.small); + const extra = zir.extraData(Inst.UnionDecl, inst_data.data.extended.operand); + var extra_index = extra.end; + const captures_len: u32 = if (small.has_captures_len) blk: { + const captures_len = zir.extra[extra_index]; + extra_index += 1; + break :blk captures_len; + } else 0; + const decls_len: u32 = if (small.has_decls_len) blk: { + const decls_len = zir.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; + const fields_len: u32 = if (small.has_fields_len) blk: { + const fields_len = zir.extra[extra_index]; + extra_index += 1; + break :blk fields_len; + } else 0; + const arg_type_body_len: u32 = if (small.kind.hasArgType()) len: { + const body_len = zir.extra[extra_index]; + extra_index += 1; + break :len body_len; + } else 0; + const captures: []const Inst.Capture = @ptrCast(zir.extra[extra_index..][0..captures_len]); + extra_index += captures_len; + const capture_names: []const NullTerminatedString = @ptrCast(zir.extra[extra_index..][0..captures_len]); + extra_index += captures_len; + const decls: []const Inst.Index = @ptrCast(zir.extra[extra_index..][0..decls_len]); + extra_index += decls_len; + const field_names: []const NullTerminatedString = @ptrCast(zir.extra[extra_index..][0..fields_len]); + extra_index += fields_len; + const field_type_body_lens: []const u32 = @ptrCast(zir.extra[extra_index..][0..fields_len]); + extra_index += fields_len; + const field_align_body_lens: ?[]const u32 = if (small.any_field_aligns) lens: { + const lens = zir.extra[extra_index..][0..fields_len]; + extra_index += fields_len; + break :lens @ptrCast(lens); + } else null; + const field_value_body_lens: ?[]const u32 = if (small.any_field_values) lens: { + const lens = zir.extra[extra_index..][0..fields_len]; + extra_index += fields_len; + break :lens @ptrCast(lens); + } else null; + const arg_type_body: ?[]const Zir.Inst.Index = switch (arg_type_body_len) { + 0 => null, + else => |n| zir.bodySlice(extra_index, n), + }; + extra_index += arg_type_body_len; + const field_bodies_overlong: []const Inst.Index = @ptrCast(zir.extra[extra_index..]); + return .{ + .src_line = extra.data.src_line, + .src_node = extra.data.src_node, + .name_strategy = small.name_strategy, + .captures = captures, + .capture_names = capture_names, + .decls = decls, + .kind = small.kind, + .arg_type_body = arg_type_body, + .field_names = field_names, + .field_type_body_lens = field_type_body_lens, + .field_align_body_lens = field_align_body_lens, + .field_value_body_lens = field_value_body_lens, + .field_bodies_overlong = field_bodies_overlong, + }; } +pub const UnwrappedUnionDecl = struct { + src_line: u32, + src_node: Ast.Node.Index, + name_strategy: Inst.NameStrategy, + + captures: []const Inst.Capture, + capture_names: []const NullTerminatedString, + + decls: []const Inst.Index, + + kind: Inst.UnionDecl.Kind, + arg_type_body: ?[]const Inst.Index, + + field_names: []const NullTerminatedString, + field_type_body_lens: []const u32, + field_align_body_lens: ?[]const u32, + field_value_body_lens: ?[]const u32, + field_bodies_overlong: []const Inst.Index, + + pub fn iterateFields(union_decl: UnwrappedUnionDecl) FieldIterator { + return .{ + .next_idx = 0, + .names = union_decl.field_names, + .type_body_lens = union_decl.field_type_body_lens, + .align_body_lens = union_decl.field_align_body_lens, + .value_body_lens = union_decl.field_value_body_lens, + .bodies_overlong = union_decl.field_bodies_overlong, + }; + } + + pub const FieldIterator = struct { + next_idx: u32, + names: []const NullTerminatedString, + type_body_lens: []const u32, + align_body_lens: ?[]const u32, + value_body_lens: ?[]const u32, + bodies_overlong: []const Inst.Index, + pub const Field = struct { + idx: u32, + name: NullTerminatedString, + type_body: ?[]const Inst.Index, + align_body: ?[]const Inst.Index, + value_body: ?[]const Inst.Index, + }; + pub fn next(it: *FieldIterator) ?Field { + const idx = it.next_idx; + if (idx == it.names.len) return null; + it.next_idx += 1; + return .{ + .idx = idx, + .name = it.names[idx], + .type_body = it.body(it.type_body_lens[idx]), + .align_body = it.body(if (it.align_body_lens) |l| l[idx] else 0), + .value_body = it.body(if (it.value_body_lens) |l| l[idx] else 0), + }; + } + fn body(it: *FieldIterator, len: u32) ?[]const Inst.Index { + if (len == 0) return null; + const b = it.bodies_overlong[0..len]; + it.bodies_overlong = it.bodies_overlong[len..]; + return b; + } + }; +}; + +pub fn getEnumDecl(zir: *const Zir, enum_decl: Inst.Index) UnwrappedEnumDecl { + const inst_data = zir.instructions.get(@intFromEnum(enum_decl)); + assert(inst_data.tag == .extended); + assert(inst_data.data.extended.opcode == .enum_decl); + const small: Inst.EnumDecl.Small = @bitCast(inst_data.data.extended.small); + const extra = zir.extraData(Inst.EnumDecl, inst_data.data.extended.operand); + var extra_index = extra.end; + const captures_len: u32 = if (small.has_captures_len) blk: { + const captures_len = zir.extra[extra_index]; + extra_index += 1; + break :blk captures_len; + } else 0; + const decls_len: u32 = if (small.has_decls_len) blk: { + const decls_len = zir.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; + const fields_len: u32 = if (small.has_fields_len) blk: { + const fields_len = zir.extra[extra_index]; + extra_index += 1; + break :blk fields_len; + } else 0; + const tag_type_body_len: u32 = if (small.has_tag_type) len: { + const body_len = zir.extra[extra_index]; + extra_index += 1; + break :len body_len; + } else 0; + const captures: []const Inst.Capture = @ptrCast(zir.extra[extra_index..][0..captures_len]); + extra_index += captures_len; + const capture_names: []const NullTerminatedString = @ptrCast(zir.extra[extra_index..][0..captures_len]); + extra_index += captures_len; + const decls: []const Inst.Index = @ptrCast(zir.extra[extra_index..][0..decls_len]); + extra_index += decls_len; + const field_names: []const NullTerminatedString = @ptrCast(zir.extra[extra_index..][0..fields_len]); + extra_index += fields_len; + const field_value_body_lens: ?[]const u32 = if (small.any_field_values) lens: { + const lens = zir.extra[extra_index..][0..fields_len]; + extra_index += fields_len; + break :lens @ptrCast(lens); + } else null; + const tag_type_body: ?[]const Zir.Inst.Index = switch (tag_type_body_len) { + 0 => null, + else => |n| zir.bodySlice(extra_index, n), + }; + extra_index += tag_type_body_len; + const field_bodies_overlong: []const Inst.Index = @ptrCast(zir.extra[extra_index..]); + return .{ + .src_line = extra.data.src_line, + .src_node = extra.data.src_node, + .name_strategy = small.name_strategy, + .captures = captures, + .capture_names = capture_names, + .decls = decls, + .tag_type_body = tag_type_body, + .nonexhaustive = small.nonexhaustive, + .field_names = field_names, + .field_value_body_lens = field_value_body_lens, + .field_bodies_overlong = field_bodies_overlong, + }; +} +pub const UnwrappedEnumDecl = struct { + src_line: u32, + src_node: Ast.Node.Index, + name_strategy: Inst.NameStrategy, + + captures: []const Inst.Capture, + capture_names: []const NullTerminatedString, + + decls: []const Inst.Index, + + tag_type_body: ?[]const Inst.Index, + nonexhaustive: bool, + + field_names: []const NullTerminatedString, + field_value_body_lens: ?[]const u32, + field_bodies_overlong: []const Inst.Index, + + pub fn iterateFields(enum_decl: UnwrappedEnumDecl) FieldIterator { + return .{ + .next_idx = 0, + .names = enum_decl.field_names, + .value_body_lens = enum_decl.field_value_body_lens, + .bodies_overlong = enum_decl.field_bodies_overlong, + }; + } + + pub const FieldIterator = struct { + next_idx: u32, + names: []const NullTerminatedString, + value_body_lens: ?[]const u32, + bodies_overlong: []const Inst.Index, + pub const Field = struct { + idx: u32, + name: NullTerminatedString, + value_body: ?[]const Inst.Index, + }; + pub fn next(it: *FieldIterator) ?Field { + const idx = it.next_idx; + if (idx == it.names.len) return null; + it.next_idx += 1; + return .{ + .idx = idx, + .name = it.names[idx], + .value_body = it.body(if (it.value_body_lens) |l| l[idx] else 0), + }; + } + fn body(it: *FieldIterator, len: u32) ?[]const Inst.Index { + if (len == 0) return null; + const b = it.bodies_overlong[0..len]; + it.bodies_overlong = it.bodies_overlong[len..]; + return b; + } + }; +}; + +pub fn getOpaqueDecl(zir: *const Zir, opaque_decl: Inst.Index) UnwrappedOpaqueDecl { + const inst_data = zir.instructions.get(@intFromEnum(opaque_decl)); + assert(inst_data.tag == .extended); + assert(inst_data.data.extended.opcode == .opaque_decl); + const small: Inst.OpaqueDecl.Small = @bitCast(inst_data.data.extended.small); + const extra = zir.extraData(Inst.OpaqueDecl, inst_data.data.extended.operand); + var extra_index = extra.end; + const captures_len: u32 = if (small.has_captures_len) blk: { + const captures_len = zir.extra[extra_index]; + extra_index += 1; + break :blk captures_len; + } else 0; + const decls_len: u32 = if (small.has_decls_len) blk: { + const decls_len = zir.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; + const captures: []const Inst.Capture = @ptrCast(zir.extra[extra_index..][0..captures_len]); + extra_index += captures_len; + const capture_names: []const NullTerminatedString = @ptrCast(zir.extra[extra_index..][0..captures_len]); + extra_index += captures_len; + const decls: []const Inst.Index = @ptrCast(zir.extra[extra_index..][0..decls_len]); + extra_index += decls_len; + return .{ + .src_line = extra.data.src_line, + .src_node = extra.data.src_node, + .name_strategy = small.name_strategy, + .captures = captures, + .capture_names = capture_names, + .decls = decls, + }; +} +pub const UnwrappedOpaqueDecl = struct { + src_line: u32, + src_node: Ast.Node.Index, + name_strategy: Inst.NameStrategy, + captures: []const Inst.Capture, + capture_names: []const NullTerminatedString, + decls: []const Inst.Index, +}; diff --git a/lib/std/zig/llvm/BitcodeReader.zig b/lib/std/zig/llvm/BitcodeReader.zig @@ -34,8 +34,8 @@ pub const Block = struct { const default: Info = .{ .block_name = &.{}, - .record_names = .{}, - .abbrevs = .{ .abbrevs = .{} }, + .record_names = .empty, + .abbrevs = .{ .abbrevs = .empty }, }; const set_bid_id: u32 = 1; @@ -109,8 +109,8 @@ pub fn init(allocator: std.mem.Allocator, options: InitOptions) BitcodeReader { .keep_names = options.keep_names, .bit_buffer = 0, .bit_offset = 0, - .stack = .{}, - .block_info = .{}, + .stack = .empty, + .block_info = .empty, }; } @@ -278,7 +278,7 @@ fn startBlock(bc: *BitcodeReader, block_id: ?u32, new_abbrev_len: u6) !void { state.* = .{ .block_id = block_id, .abbrev_id_width = new_abbrev_len, - .abbrevs = .{ .abbrevs = .{} }, + .abbrevs = .{ .abbrevs = .empty }, }; try state.abbrevs.abbrevs.ensureTotalCapacity( bc.allocator, diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig @@ -7,6 +7,7 @@ const Allocator = std.mem.Allocator; const assert = std.debug.assert; const DW = std.dwarf; const log = std.log.scoped(.llvm); +const maxInt = std.math.maxInt; const Writer = std.Io.Writer; const bitcode_writer = @import("bitcode_writer.zig"); @@ -55,6 +56,8 @@ constant_items: std.MultiArrayList(Constant.Item), constant_extra: std.ArrayList(u32), constant_limbs: std.ArrayList(std.math.big.Limb), +alignment_forward_references: std.ArrayList(Alignment), + metadata_map: std.AutoArrayHashMapUnmanaged(void, void), metadata_items: std.MultiArrayList(Metadata.Item), metadata_extra: std.ArrayList(u32), @@ -85,7 +88,7 @@ pub const Options = struct { }; pub const String = enum(u32) { - none = std.math.maxInt(u31), + none = maxInt(u31), empty, _, @@ -245,7 +248,7 @@ pub const Type = enum(u32) { ptr, @"ptr addrspace(4)", - none = std.math.maxInt(u32), + none = maxInt(u32), _, pub const ptr_amdgpu_constant = @@ -941,7 +944,7 @@ pub const Attribute = union(Kind) { inalloca: Type, sret: Type, elementtype: Type, - @"align": Alignment, + @"align": Alignment.Lazy, @"noalias", nocapture, nofree, @@ -956,7 +959,7 @@ pub const Attribute = union(Kind) { immarg, noundef, nofpclass: FpClass, - alignstack: Alignment, + alignstack: Alignment.Lazy, allocalign, allocptr, readnone, @@ -964,7 +967,7 @@ pub const Attribute = union(Kind) { writeonly, // Function Attributes - //alignstack: Alignment, + //alignstack: Alignment.Lazy, allockind: AllocKind, allocsize: AllocSize, alwaysinline, @@ -1145,7 +1148,7 @@ pub const Attribute = union(Kind) { return @unionInit(Attribute, field.name, switch (field.type) { void => {}, u32 => storage.value, - Alignment, String, Type, UwTable => @enumFromInt(storage.value), + Alignment.Lazy, String, Type, UwTable => @enumFromInt(storage.value), AllocKind, AllocSize, FpClass, Memory, VScaleRange => @bitCast(storage.value), else => @compileError("bad payload type: " ++ field.name ++ ": " ++ @typeName(field.type)), @@ -1246,7 +1249,7 @@ pub const Attribute = union(Kind) { .sret, .elementtype, => |ty| try w.print(" {s}({f})", .{ @tagName(attribute), ty.fmt(data.builder, .percent) }), - .@"align" => |alignment| try w.print("{f}", .{alignment.fmt(" ")}), + .@"align" => |alignment| try w.print("{f}", .{alignment.resolve(data.builder).fmt(" ")}), .dereferenceable, .dereferenceable_or_null, => |size| try w.print(" {s}({d})", .{ @tagName(attribute), size }), @@ -1270,7 +1273,7 @@ pub const Attribute = union(Kind) { }, .alignstack => |alignment| { try w.print(" {t}", .{attribute}); - const alignment_bytes = alignment.toByteUnits() orelse return; + const alignment_bytes = alignment.resolve(data.builder).toByteUnits() orelse return; if (data.flags.pound) { try w.print("={d}", .{alignment_bytes}); } else { @@ -1435,8 +1438,8 @@ pub const Attribute = union(Kind) { //sanitize_memtag, sanitize_address_dyninit = 102, - string = std.math.maxInt(u31), - none = std.math.maxInt(u32), + string = maxInt(u31), + none = maxInt(u32), _, pub const len = @typeInfo(Kind).@"enum".fields.len - 2; @@ -1516,12 +1519,12 @@ pub const Attribute = union(Kind) { elem_size: u16, num_elems: u16, - pub const none = std.math.maxInt(u16); + pub const none = maxInt(u16); fn toLlvm(self: AllocSize) packed struct(u64) { num_elems: u32, elem_size: u32 } { return .{ .num_elems = switch (self.num_elems) { else => self.num_elems, - none => std.math.maxInt(u32), + none => maxInt(u32), }, .elem_size = self.elem_size }; } }; @@ -1577,7 +1580,7 @@ pub const Attribute = union(Kind) { inline else => |value, tag| .{ .kind = @as(Kind, self), .value = switch (@TypeOf(value)) { void => 0, u32 => value, - Alignment, String, Type, UwTable => @intFromEnum(value), + Alignment.Lazy, String, Type, UwTable => @intFromEnum(value), AllocKind, AllocSize, FpClass, Memory, VScaleRange => @bitCast(value), else => @compileError("bad payload type: " ++ @tagName(tag) ++ @typeName(@TypeOf(value))), } }, @@ -1627,7 +1630,7 @@ pub const FunctionAttributes = enum(u32) { const params_index = 2; pub const Wip = struct { - maps: Maps = .{}, + maps: Maps = .empty, const Map = std.AutoArrayHashMapUnmanaged(Attribute.Kind, Attribute.Index); const Maps = std.ArrayList(Map); @@ -2017,9 +2020,32 @@ pub const ExternallyInitialized = enum { }; pub const Alignment = enum(u6) { - default = std.math.maxInt(u6), + default = maxInt(u6), _, + pub const Lazy = enum(u32) { + /// Values which fit in a `u6` are already-resolved `Alignment` values. Other values are + /// indices into `Builder.alignment_forward_references`, offset by `maxInt(u6)`. + _, + + pub fn wrap(a: Alignment) Lazy { + return @enumFromInt(@intFromEnum(a)); + } + pub fn resolve(l: Lazy, b: *const Builder) Alignment { + return switch (@intFromEnum(l)) { + 0...maxInt(u6) => |raw| @enumFromInt(raw), + else => |offset_index| b.alignment_forward_references.items[offset_index - maxInt(u6)], + }; + } + + fn fromFwdRefIndex(index: usize) Lazy { + return @enumFromInt(index + maxInt(u6)); + } + fn toFwdRefIndex(l: Lazy) usize { + return @intFromEnum(l) - maxInt(u6); + } + }; + pub fn fromByteUnits(bytes: u64) Alignment { if (bytes == 0) return .default; assert(std.math.isPowerOfTwo(bytes)); @@ -2028,11 +2054,17 @@ pub const Alignment = enum(u6) { } pub fn toByteUnits(self: Alignment) ?u64 { - return if (self == .default) null else @as(u64, 1) << @intFromEnum(self); + return switch (self) { + .default => null, + else => @as(u64, 1) << @intFromEnum(self), + }; } pub fn toLlvm(self: Alignment) u6 { - return if (self == .default) 0 else (@intFromEnum(self) + 1); + return switch (self) { + .default => 0, + else => @intFromEnum(self) + 1, + }; } pub const Prefixed = struct { @@ -2180,7 +2212,7 @@ pub const CallConv = enum(u10) { }; pub const StrtabString = enum(u32) { - none = std.math.maxInt(u31), + none = maxInt(u31), empty, _, @@ -2308,7 +2340,7 @@ pub const Global = struct { }, pub const Index = enum(u32) { - none = std.math.maxInt(u32), + none = maxInt(u32), _, pub fn unwrap(self: Index, builder: *const Builder) Index { @@ -2478,7 +2510,7 @@ pub const Alias = struct { aliasee: Constant = .no_init, pub const Index = enum(u32) { - none = std.math.maxInt(u32), + none = maxInt(u32), _, pub fn ptr(self: Index, builder: *Builder) *Alias { @@ -2530,7 +2562,7 @@ pub const Variable = struct { alignment: Alignment = .default, pub const Index = enum(u32) { - none = std.math.maxInt(u32), + none = maxInt(u32), _, pub fn ptr(self: Index, builder: *Builder) *Variable { @@ -3949,7 +3981,7 @@ pub const Intrinsic = enum { .params = &.{ .{ .kind = .{ .type = Type.ptr_amdgpu_constant }, - .attrs = &.{.{ .@"align" = Builder.Alignment.fromByteUnits(4) }}, + .attrs = &.{.{ .@"align" = .wrap(.fromByteUnits(4)) }}, }, }, .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, @@ -4048,7 +4080,7 @@ pub const Function = struct { section: String = .none, alignment: Alignment = .default, blocks: []const Block = &.{}, - instructions: std.MultiArrayList(Instruction) = .{}, + instructions: std.MultiArrayList(Instruction) = .empty, names: [*]const String = &[0]String{}, value_indices: [*]const u32 = &[0]u32{}, strip: bool, @@ -4057,7 +4089,7 @@ pub const Function = struct { extra: []const u32 = &.{}, pub const Index = enum(u32) { - none = std.math.maxInt(u32), + none = maxInt(u32), _, pub fn ptr(self: Index, builder: *Builder) *Function { @@ -4411,7 +4443,7 @@ pub const Function = struct { }; pub const Index = enum(u32) { - none = std.math.maxInt(u31), + none = maxInt(u31), _, pub fn name(self: Instruction.Index, function: *const Function) String { @@ -5007,7 +5039,7 @@ pub const Function = struct { fsub = 12, fmax = 13, fmin = 14, - none = std.math.maxInt(u5), + none = maxInt(u5), }; }; @@ -5222,13 +5254,13 @@ pub const WipFunction = struct { .prev_debug_location = .no_location, .debug_location = .no_location, .cursor = undefined, - .blocks = .{}, - .instructions = .{}, - .names = .{}, + .blocks = .empty, + .instructions = .empty, + .names = .empty, .strip = options.strip, - .debug_locations = .{}, - .debug_values = .{}, - .extra = .{}, + .debug_locations = .empty, + .debug_values = .empty, + .extra = .empty, }; errdefer self.deinit(); @@ -5265,7 +5297,7 @@ pub const WipFunction = struct { self.blocks.appendAssumeCapacity(.{ .name = final_name, .incoming = incoming, - .instructions = .{}, + .instructions = .empty, }); return index; } @@ -6132,8 +6164,8 @@ pub const WipFunction = struct { kind: MemoryAccessKind, @"inline": bool, ) Allocator.Error!Instruction.Index { - var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = dst_align })}; - var src_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = src_align })}; + var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = .wrap(dst_align) })}; + var src_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = .wrap(src_align) })}; const value = try self.callIntrinsic( .normal, try self.builder.fnAttrs(&.{ @@ -6162,8 +6194,8 @@ pub const WipFunction = struct { len: Value, kind: MemoryAccessKind, ) Allocator.Error!Instruction.Index { - var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = dst_align })}; - var src_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = src_align })}; + var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = .wrap(dst_align) })}; + var src_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = .wrap(src_align) })}; const value = try self.callIntrinsic( .normal, try self.builder.fnAttrs(&.{ @@ -6192,7 +6224,7 @@ pub const WipFunction = struct { kind: MemoryAccessKind, @"inline": bool, ) Allocator.Error!Instruction.Index { - var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = dst_align })}; + var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = .wrap(dst_align) })}; const value = try self.callIntrinsic( .normal, try self.builder.fnAttrs(&.{ .none, .none, try self.builder.attrs(&dst_attrs) }), @@ -6325,7 +6357,7 @@ pub const WipFunction = struct { function.blocks = &.{}; gpa.free(function.names[0..function.instructions.len]); function.debug_locations.deinit(gpa); - function.debug_locations = .{}; + function.debug_locations = .empty; gpa.free(function.debug_values); function.debug_values = &.{}; gpa.free(function.extra); @@ -7329,7 +7361,7 @@ pub const Constant = enum(u32) { //indices: [info.indices_len]Constant, pub const Kind = enum { normal, inbounds }; - pub const InRangeIndex = enum(u16) { none = std.math.maxInt(u16), _ }; + pub const InRangeIndex = enum(u16) { none = maxInt(u16), _ }; pub const Info = packed struct(u32) { indices_len: u16, inrange: InRangeIndex }; }; @@ -7579,7 +7611,7 @@ pub const Constant = enum(u32) { string: [ (std.math.big.int.Const{ .limbs = &([1]std.math.big.Limb{ - std.math.maxInt(std.math.big.Limb), + maxInt(std.math.big.Limb), } ** expected_limbs), .positive = false, }).sizeInBaseUpperBound(10) @@ -7643,7 +7675,7 @@ pub const Constant = enum(u32) { std.math.minInt(Exponent64), else => @as(Exponent64, repr.exponent) + (std.math.floatExponentMax(f64) - std.math.floatExponentMax(f32)), - std.math.maxInt(Exponent32) => std.math.maxInt(Exponent64), + maxInt(Exponent32) => maxInt(Exponent64), }, .sign = repr.sign, }))}); @@ -7820,7 +7852,7 @@ pub const Constant = enum(u32) { }; pub const Value = enum(u32) { - none = std.math.maxInt(u31), + none = maxInt(u31), false = first_constant + @intFromEnum(Constant.false), true = first_constant + @intFromEnum(Constant.true), @"0" = first_constant + @intFromEnum(Constant.@"0"), @@ -8021,6 +8053,7 @@ pub const Metadata = packed struct(u32) { composite_vector_type, derived_pointer_type, derived_member_type, + derived_typedef_type, subroutine_type, enumerator_unsigned, enumerator_signed_positive, @@ -8064,6 +8097,7 @@ pub const Metadata = packed struct(u32) { .composite_vector_type, .derived_pointer_type, .derived_member_type, + .derived_typedef_type, .subroutine_type, .enumerator_unsigned, .enumerator_signed_positive, @@ -8391,7 +8425,7 @@ pub const Metadata = packed struct(u32) { map: std.AutoArrayHashMapUnmanaged(union(enum) { metadata: Metadata, debug_location: DebugLocation.Location, - }, void) = .{}, + }, void) = .empty, const FormatData = struct { formatter: *Formatter, @@ -8649,52 +8683,54 @@ pub fn init(options: Options) Allocator.Error!Builder { .source_filename = .none, .data_layout = .none, .target_triple = .none, - .module_asm = .{}, + .module_asm = .empty, - .string_map = .{}, - .string_indices = .{}, - .string_bytes = .{}, + .string_map = .empty, + .string_indices = .empty, + .string_bytes = .empty, - .types = .{}, + .types = .empty, .next_unnamed_type = @enumFromInt(0), - .next_unique_type_id = .{}, - .type_map = .{}, - .type_items = .{}, - .type_extra = .{}, + .next_unique_type_id = .empty, + .type_map = .empty, + .type_items = .empty, + .type_extra = .empty, - .attributes = .{}, - .attributes_map = .{}, - .attributes_indices = .{}, - .attributes_extra = .{}, + .attributes = .empty, + .attributes_map = .empty, + .attributes_indices = .empty, + .attributes_extra = .empty, - .function_attributes_set = .{}, + .function_attributes_set = .empty, - .globals = .{}, + .globals = .empty, .next_unnamed_global = @enumFromInt(0), .next_replaced_global = .none, - .next_unique_global_id = .{}, - .aliases = .{}, - .variables = .{}, - .functions = .{}, - - .strtab_string_map = .{}, - .strtab_string_indices = .{}, - .strtab_string_bytes = .{}, - - .constant_map = .{}, - .constant_items = .{}, - .constant_extra = .{}, - .constant_limbs = .{}, - - .metadata_map = .{}, - .metadata_items = .{}, - .metadata_extra = .{}, - .metadata_limbs = .{}, - .metadata_forward_references = .{}, - .metadata_named = .{}, - .metadata_string_map = .{}, - .metadata_string_indices = .{}, - .metadata_string_bytes = .{}, + .next_unique_global_id = .empty, + .aliases = .empty, + .variables = .empty, + .functions = .empty, + + .strtab_string_map = .empty, + .strtab_string_indices = .empty, + .strtab_string_bytes = .empty, + + .constant_map = .empty, + .constant_items = .empty, + .constant_extra = .empty, + .constant_limbs = .empty, + + .alignment_forward_references = .empty, + + .metadata_map = .empty, + .metadata_items = .empty, + .metadata_extra = .empty, + .metadata_limbs = .empty, + .metadata_forward_references = .empty, + .metadata_named = .empty, + .metadata_string_map = .empty, + .metadata_string_indices = .empty, + .metadata_string_bytes = .empty, }; errdefer self.deinit(); @@ -8798,51 +8834,55 @@ pub fn clearAndFree(self: *Builder) void { } pub fn deinit(self: *Builder) void { - self.module_asm.deinit(self.gpa); + const gpa = self.gpa; - self.string_map.deinit(self.gpa); - self.string_indices.deinit(self.gpa); - self.string_bytes.deinit(self.gpa); + self.module_asm.deinit(gpa); - self.types.deinit(self.gpa); - self.next_unique_type_id.deinit(self.gpa); - self.type_map.deinit(self.gpa); - self.type_items.deinit(self.gpa); - self.type_extra.deinit(self.gpa); + self.string_map.deinit(gpa); + self.string_indices.deinit(gpa); + self.string_bytes.deinit(gpa); - self.attributes.deinit(self.gpa); - self.attributes_map.deinit(self.gpa); - self.attributes_indices.deinit(self.gpa); - self.attributes_extra.deinit(self.gpa); + self.types.deinit(gpa); + self.next_unique_type_id.deinit(gpa); + self.type_map.deinit(gpa); + self.type_items.deinit(gpa); + self.type_extra.deinit(gpa); - self.function_attributes_set.deinit(self.gpa); + self.attributes.deinit(gpa); + self.attributes_map.deinit(gpa); + self.attributes_indices.deinit(gpa); + self.attributes_extra.deinit(gpa); - self.globals.deinit(self.gpa); - self.next_unique_global_id.deinit(self.gpa); - self.aliases.deinit(self.gpa); - self.variables.deinit(self.gpa); - for (self.functions.items) |*function| function.deinit(self.gpa); - self.functions.deinit(self.gpa); + self.function_attributes_set.deinit(gpa); + + self.globals.deinit(gpa); + self.next_unique_global_id.deinit(gpa); + self.aliases.deinit(gpa); + self.variables.deinit(gpa); + for (self.functions.items) |*function| function.deinit(gpa); + self.functions.deinit(gpa); + + self.strtab_string_map.deinit(gpa); + self.strtab_string_indices.deinit(gpa); + self.strtab_string_bytes.deinit(gpa); - self.strtab_string_map.deinit(self.gpa); - self.strtab_string_indices.deinit(self.gpa); - self.strtab_string_bytes.deinit(self.gpa); + self.constant_map.deinit(gpa); + self.constant_items.deinit(gpa); + self.constant_extra.deinit(gpa); + self.constant_limbs.deinit(gpa); - self.constant_map.deinit(self.gpa); - self.constant_items.deinit(self.gpa); - self.constant_extra.deinit(self.gpa); - self.constant_limbs.deinit(self.gpa); + self.alignment_forward_references.deinit(gpa); - self.metadata_map.deinit(self.gpa); - self.metadata_items.deinit(self.gpa); - self.metadata_extra.deinit(self.gpa); - self.metadata_limbs.deinit(self.gpa); - self.metadata_forward_references.deinit(self.gpa); - self.metadata_named.deinit(self.gpa); + self.metadata_map.deinit(gpa); + self.metadata_items.deinit(gpa); + self.metadata_extra.deinit(gpa); + self.metadata_limbs.deinit(gpa); + self.metadata_forward_references.deinit(gpa); + self.metadata_named.deinit(gpa); - self.metadata_string_map.deinit(self.gpa); - self.metadata_string_indices.deinit(self.gpa); - self.metadata_string_bytes.deinit(self.gpa); + self.metadata_string_map.deinit(gpa); + self.metadata_string_indices.deinit(gpa); + self.metadata_string_bytes.deinit(gpa); self.* = undefined; } @@ -8960,7 +9000,7 @@ pub fn structType( pub fn opaqueType(self: *Builder, name: String) Allocator.Error!Type { try self.string_map.ensureUnusedCapacity(self.gpa, 1); if (name.slice(self)) |id| { - const count: usize = comptime std.fmt.count("{d}", .{std.math.maxInt(u32)}); + const count: usize = comptime std.fmt.count("{d}", .{maxInt(u32)}); try self.string_bytes.ensureUnusedCapacity(self.gpa, id.len + count); } try self.string_indices.ensureUnusedCapacity(self.gpa, 1); @@ -9576,6 +9616,21 @@ pub fn asmValue( return (try self.asmConst(ty, info, assembly, constraints)).toValue(); } +/// The initial "resolved" value of the forward reference is `Alignment.default`. +pub fn alignmentForwardReference(b: *Builder) Allocator.Error!Alignment.Lazy { + const index = b.alignment_forward_references.items.len; + try b.alignment_forward_references.append(b.gpa, .default); + return .fromFwdRefIndex(index); +} + +/// Updates the "resolved" value of the alignment forward reference `fwd_ref` to `value`. +/// +/// Asserts that `fwd_ref` is a forward reference, as opposed to a resolved alignment value. +pub fn resolveAlignmentForwardReference(b: *Builder, fwd_ref: Alignment.Lazy, value: Alignment) void { + const index = fwd_ref.toFwdRefIndex(); + b.alignment_forward_references.items[index] = value; +} + pub fn dump(b: *Builder, io: Io) void { var buffer: [4000]u8 = undefined; const stderr: Io.File = .stderr(); @@ -10463,15 +10518,18 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void }, .derived_pointer_type, .derived_member_type, + .derived_typedef_type, => |kind| { const extra = self.metadataExtraData(Metadata.DerivedType, metadata_item.data); try metadata_formatter.specialized(.@"!", .DIDerivedType, .{ .tag = @as(enum { DW_TAG_pointer_type, DW_TAG_member, + DW_TAG_typedef, }, switch (kind) { .derived_pointer_type => .DW_TAG_pointer_type, .derived_member_type => .DW_TAG_member, + .derived_typedef_type => .DW_TAG_typedef, else => unreachable, }), .name = extra.name, @@ -10510,7 +10568,7 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void string: [ (std.math.big.int.Const{ .limbs = &([1]std.math.big.Limb{ - std.math.maxInt(std.math.big.Limb), + maxInt(std.math.big.Limb), } ** expected_limbs), .positive = false, }).sizeInBaseUpperBound(10) @@ -10660,7 +10718,7 @@ fn printEscapedString(slice: []const u8, quotes: QuoteBehavior, w: *Writer) Writ fn ensureUnusedGlobalCapacity(self: *Builder, name: StrtabString) Allocator.Error!void { try self.strtab_string_map.ensureUnusedCapacity(self.gpa, 1); if (name.slice(self)) |id| { - const count: usize = comptime std.fmt.count("{d}", .{std.math.maxInt(u32)}); + const count: usize = comptime std.fmt.count("{d}", .{maxInt(u32)}); try self.strtab_string_bytes.ensureUnusedCapacity(self.gpa, id.len + count); } try self.strtab_string_indices.ensureUnusedCapacity(self.gpa, 1); @@ -12069,7 +12127,7 @@ pub fn trailingMetadataStringAssumeCapacity(self: *Builder) Metadata.String { const start = self.metadata_string_indices.getLast(); const bytes: []const u8 = self.metadata_string_bytes.items[start..]; assert(bytes.len > 0); - const gop = self.metadata_string_map.getOrPutAssumeCapacityAdapted(bytes, String.Adapter{ .builder = self }); + const gop = self.metadata_string_map.getOrPutAssumeCapacityAdapted(bytes, Metadata.String.Adapter{ .builder = self }); if (gop.found_existing) { self.metadata_string_bytes.shrinkRetainingCapacity(start); } else { @@ -12360,6 +12418,30 @@ pub fn debugMemberType( ); } +pub fn debugTypedefType( + self: *Builder, + name: ?Metadata.String, + file: ?Metadata, + scope: ?Metadata, + line: u32, + underlying_type: ?Metadata, + size_in_bits: u64, + align_in_bits: u64, + offset_in_bits: u64, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.DerivedType, 0); + return self.debugTypedefTypeAssumeCapacity( + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + offset_in_bits, + ); +} + pub fn debugSubroutineType(self: *Builder, types_tuple: ?Metadata) Allocator.Error!Metadata { try self.ensureUnusedMetadataCapacity(1, Metadata.SubroutineType, 0); return self.debugSubroutineTypeAssumeCapacity(types_tuple); @@ -12467,11 +12549,12 @@ pub fn metadataConstant(self: *Builder, value: Constant) Allocator.Error!Metadat return self.metadataConstantAssumeCapacity(value); } +/// Resolves the given forward reference to the given value (which is not itself a forward +/// reference). If the forward reference is already resolved, its target is replaced. pub fn resolveDebugForwardReference(self: *Builder, fwd_ref: Metadata, value: Metadata) void { assert(fwd_ref.kind == .forward); - const resolved = &self.metadata_forward_references.items[fwd_ref.index]; - assert(resolved.is_none); - resolved.* = value.toOptional(); + assert(value.kind != .forward); + self.metadata_forward_references.items[fwd_ref.index] = value.toOptional(); } fn metadataSimpleAssumeCapacity(self: *Builder, tag: Metadata.Tag, value: anytype) Metadata { @@ -12874,6 +12957,33 @@ fn debugMemberTypeAssumeCapacity( }); } +fn debugTypedefTypeAssumeCapacity( + self: *Builder, + name: ?Metadata.String, + file: ?Metadata, + scope: ?Metadata, + line: u32, + underlying_type: ?Metadata, + size_in_bits: u64, + align_in_bits: u64, + offset_in_bits: u64, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.derived_typedef_type, Metadata.DerivedType{ + .name = .wrap(name), + .file = .wrap(file), + .scope = .wrap(scope), + .line = line, + .underlying_type = .wrap(underlying_type), + .size_in_bits_lo = @truncate(size_in_bits), + .size_in_bits_hi = @truncate(size_in_bits >> 32), + .align_in_bits_lo = @truncate(align_in_bits), + .align_in_bits_hi = @truncate(align_in_bits >> 32), + .offset_in_bits_lo = @truncate(offset_in_bits), + .offset_in_bits_hi = @truncate(offset_in_bits >> 32), + }); +} + fn debugSubroutineTypeAssumeCapacity(self: *Builder, types_tuple: ?Metadata) Metadata { assert(!self.strip); return self.metadataSimpleAssumeCapacity(.subroutine_type, Metadata.SubroutineType{ @@ -13461,7 +13571,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator, producer: Producer) bitco try record.ensureUnusedCapacity(self.gpa, 3); record.appendAssumeCapacity(1); record.appendAssumeCapacity(@intFromEnum(kind)); - record.appendAssumeCapacity(alignment.toByteUnits() orelse 0); + record.appendAssumeCapacity(alignment.resolve(self).toByteUnits() orelse 0); }, .dereferenceable, .dereferenceable_or_null, @@ -14222,12 +14332,14 @@ pub fn toBitcode(self: *Builder, allocator: Allocator, producer: Producer) bitco }, .derived_pointer_type, .derived_member_type, + .derived_typedef_type, => |kind| { const extra = self.metadataExtraData(Metadata.DerivedType, data); try metadata_block.writeAbbrevAdapted(MetadataBlock.DerivedType{ .tag = switch (kind) { .derived_pointer_type => DW.TAG.pointer_type, .derived_member_type => DW.TAG.member, + .derived_typedef_type => DW.TAG.typedef, else => unreachable, }, .name = extra.name, diff --git a/lib/std/zig/target.zig b/lib/std/zig/target.zig @@ -503,8 +503,7 @@ pub fn intByteSize(target: *const std.Target, bits: u16) u16 { pub fn intAlignment(target: *const std.Target, bits: u16) u16 { return switch (target.cpu.arch) { .x86 => switch (bits) { - 0 => 0, - 1...8 => 1, + 0...8 => 1, 9...16 => 2, 17...32 => 4, 33...64 => switch (target.os.tag) { @@ -514,17 +513,19 @@ pub fn intAlignment(target: *const std.Target, bits: u16) u16 { else => 16, }, .x86_64 => switch (bits) { - 0 => 0, - 1...8 => 1, + 0...8 => 1, 9...16 => 2, 17...32 => 4, 33...64 => 8, else => 16, }, - else => return @min( - std.math.ceilPowerOfTwoPromote(u16, @as(u16, @intCast((@as(u17, bits) + 7) / 8))), - target.cMaxIntAlignment(), - ), + else => switch (bits) { + 0 => 1, + else => @min( + std.math.ceilPowerOfTwoPromote(u16, @intCast((@as(u17, bits) + 7) / 8)), + target.cMaxIntAlignment(), + ), + }, }; } diff --git a/lib/std/zon/Serializer.zig b/lib/std/zon/Serializer.zig @@ -793,9 +793,11 @@ test checkValueDepth { try expectValueDepthEquals(2, @as(?u32, 1)); try expectValueDepthEquals(1, @as(?u32, null)); try expectValueDepthEquals(1, null); - try expectValueDepthEquals(2, &1); try expectValueDepthEquals(3, &@as(?u32, 1)); + // The pointer drops the implicit comptime-ness, so we need to specify 'comptime' here + try comptime expectValueDepthEquals(2, &1); + const Union = union(enum) { x: u32, y: struct { x: u32 }, diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig @@ -591,7 +591,7 @@ const Parser = struct { if (pointer.child == u8 and pointer.is_const and (pointer.sentinel() == null or pointer.sentinel() == 0) and - pointer.alignment == 1) + (pointer.alignment == null or pointer.alignment == 1)) { if (opt) { return self.failNode(node, "expected optional string"); @@ -717,7 +717,7 @@ const Parser = struct { pointer.size != .slice or !pointer.is_const or (pointer.sentinel() != null and pointer.sentinel() != 0) or - pointer.alignment != 1) + (pointer.alignment != null and pointer.alignment != 1)) { return error.WrongType; } @@ -742,7 +742,7 @@ const Parser = struct { const slice = try self.gpa.allocWithOptions( pointer.child, nodes.len, - .fromByteUnits(pointer.alignment), + .fromByteUnitsOptional(pointer.alignment), pointer.sentinel(), ); errdefer self.gpa.free(slice); diff --git a/lib/zig.h b/lib/zig.h @@ -151,6 +151,14 @@ #define zig_has_attribute(attribute) 0 #endif +#if __STDC_VERSION__ >= 201112L +#define zig_static_assert(cond, msg) _Static_assert(cond, msg) +#elif zig_has_attribute(unused) +#define zig_static_assert(cond, _) typedef char zig_expand_concat(zig_static_assert_fail_, __LINE__)[!!(cond)] __attribute__((unused)) +#else +#define zig_static_assert(cond, _) typedef char zig_expand_concat(zig_static_assert_fail_, __LINE__)[!!(cond)] +#endif + #if __STDC_VERSION__ >= 202311L #define zig_threadlocal thread_local #elif __STDC_VERSION__ >= 201112L @@ -259,7 +267,7 @@ #endif #if zig_has_attribute(packed) || defined(zig_tinyc) -#define zig_packed(definition) __attribute__((packed)) definition +#define zig_packed(definition) definition __attribute__((packed)) #elif defined(zig_msvc) #define zig_packed(definition) __pragma(pack(1)) definition __pragma(pack()) #else diff --git a/src/Air.zig b/src/Air.zig @@ -14,7 +14,6 @@ const Type = @import("Type.zig"); const Value = @import("Value.zig"); const Zcu = @import("Zcu.zig"); const print = @import("Air/print.zig"); -const types_resolved = @import("Air/types_resolved.zig"); pub const Legalize = @import("Air/Legalize.zig"); pub const Liveness = @import("Air/Liveness.zig"); @@ -173,8 +172,8 @@ pub const Inst = struct { /// outside the provenance of the operand, the result is undefined. /// /// Uses the `ty_pl` field. Payload is `Bin`. The lhs is the pointer, - /// rhs is the offset. Result type is the same as lhs. The operand may - /// be a slice. + /// rhs is the offset. Result type is the same as lhs. The operand type's + /// pointer size may be `.slice`, `.many`, or `.c`. ptr_add, /// Subtract an offset, in element type units, from a pointer, /// returning a new pointer. Element type may not be zero bits. @@ -183,8 +182,8 @@ pub const Inst = struct { /// outside the provenance of the operand, the result is undefined. /// /// Uses the `ty_pl` field. Payload is `Bin`. The lhs is the pointer, - /// rhs is the offset. Result type is the same as lhs. The operand may - /// be a slice. + /// rhs is the offset. Result type is the same as lhs. The operand type's + /// pointer size may be `.slice`, `.many`, or `.c`. ptr_sub, /// Given two operands which can be floats, integers, or vectors, returns the /// greater of the operands. For vectors it operates element-wise. @@ -693,6 +692,7 @@ pub const Inst = struct { /// Uses the `ty_pl` field with payload `Bin`. slice_elem_ptr, /// Given a pointer value, and element index, return the element value at that index. + /// The pointer size is either `.c` or `.many`. /// Result type is the element type of the pointer operand. /// Uses the `bin_op` field. ptr_elem_val, @@ -2440,9 +2440,6 @@ pub fn unwrapShuffleTwo(air: *const Air, zcu: *const Zcu, inst_index: Inst.Index }; } -pub const typesFullyResolved = types_resolved.typesFullyResolved; -pub const typeFullyResolved = types_resolved.checkType; -pub const valFullyResolved = types_resolved.checkVal; pub const legalize = Legalize.legalize; pub const write = print.write; pub const writeInst = print.writeInst; diff --git a/src/Air/Liveness.zig b/src/Air/Liveness.zig @@ -153,8 +153,8 @@ pub fn analyze(zcu: *Zcu, air: Air, intern_pool: *InternPool) Allocator.Error!Li usize, (air.instructions.len * bpi + @bitSizeOf(usize) - 1) / @bitSizeOf(usize), ), - .extra = .{}, - .special = .{}, + .extra = .empty, + .special = .empty, .intern_pool = intern_pool, }; errdefer gpa.free(a.tomb_bits); @@ -175,7 +175,7 @@ pub fn analyze(zcu: *Zcu, air: Air, intern_pool: *InternPool) Allocator.Error!Li var data: LivenessPassData(.main_analysis) = .{}; defer data.deinit(gpa); data.old_extra = a.extra; - a.extra = .{}; + a.extra = .empty; try analyzeBody(&a, .main_analysis, &data, main_body); assert(data.live_set.count() == 0); } @@ -999,7 +999,7 @@ fn analyzeInstBlock( // If the block is noreturn, block deaths not only aren't useful, they're impossible to // find: there could be more stuff alive after the block than before it! - if (!a.intern_pool.isNoReturn(ty.toIntern())) { + if (!ty.isNoReturn(a.zcu)) { // The block kills the difference in the live sets const block_scope = data.block_scopes.get(inst).?; const num_deaths = data.live_set.count() - block_scope.live_set.count(); @@ -1360,7 +1360,7 @@ fn analyzeInstSwitchBr( const mirrored_deaths = try gpa.alloc(DeathList, ncases + 1); defer gpa.free(mirrored_deaths); - @memset(mirrored_deaths, .{}); + @memset(mirrored_deaths, .empty); defer for (mirrored_deaths) |*md| md.deinit(gpa); { diff --git a/src/Air/Liveness/Verify.zig b/src/Air/Liveness/Verify.zig @@ -465,7 +465,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { for (block_liveness.deaths) |death| try self.verifyDeath(inst, death); - if (ip.isNoReturn(block_ty.toIntern())) { + if (block_ty.isNoReturn(self.zcu)) { assert(!self.blocks.contains(inst)); } else { var live = if (self.blocks.fetchRemove(inst)) |kv| kv.value else { diff --git a/src/Air/print.zig b/src/Air/print.zig @@ -692,33 +692,23 @@ const Writer = struct { const zcu = w.pt.zcu; const ip = &zcu.intern_pool; - const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate; - const struct_type: Type = .fromInterned(aggregate.ty); - switch (aggregate.storage) { - .elems => |elems| for (elems, 0..) |elem, i| { - switch (elem) { - .bool_true => { - const clobber = struct_type.structFieldName(i, zcu).toSlice(ip).?; - assert(clobber.len != 0); - try s.writeAll(", ~{"); - try s.writeAll(clobber); - try s.writeAll("}"); - }, - .bool_false => continue, - else => unreachable, - } - }, - .repeated_elem => |elem| { - try s.writeAll(", "); - try s.writeAll(switch (elem) { - .bool_true => "<all clobbers>", - .bool_false => "<no clobbers>", - else => unreachable, - }); - }, - .bytes => |bytes| { - try s.print(", {x}", .{bytes}); - }, + const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers); + const clobbers_ty = clobbers_val.typeOf(zcu); + var clobbers_bigint_buf: Value.BigIntSpace = undefined; + const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu); + for (0..clobbers_ty.structFieldCount(zcu)) |field_index| { + assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type); + const limb_bits = @bitSizeOf(std.math.big.Limb); + if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false + switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) { + 0 => continue, // field is false + 1 => {}, // field is true + } + const clobber = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?; + assert(clobber.len != 0); + try s.writeAll(", ~{"); + try s.writeAll(clobber); + try s.writeAll("}"); } const asm_source = unwrapped_asm.source; try s.print(", \"{f}\"", .{std.zig.fmtString(asm_source)}); diff --git a/src/Air/types_resolved.zig b/src/Air/types_resolved.zig @@ -1,536 +0,0 @@ -const Air = @import("../Air.zig"); -const Zcu = @import("../Zcu.zig"); -const Type = @import("../Type.zig"); -const Value = @import("../Value.zig"); -const InternPool = @import("../InternPool.zig"); - -/// Given a body of AIR instructions, returns whether all type resolution necessary for codegen is complete. -/// If `false`, then type resolution must have failed, so codegen cannot proceed. -pub fn typesFullyResolved(air: Air, zcu: *Zcu) bool { - return checkBody(air, air.getMainBody(), zcu); -} - -fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool { - const tags = air.instructions.items(.tag); - const datas = air.instructions.items(.data); - - for (body) |inst| { - const data = datas[@intFromEnum(inst)]; - switch (tags[@intFromEnum(inst)]) { - .inferred_alloc, .inferred_alloc_comptime => unreachable, - - .arg => { - if (!checkType(data.arg.ty.toType(), zcu)) return false; - }, - - .add, - .add_safe, - .add_optimized, - .add_wrap, - .add_sat, - .sub, - .sub_safe, - .sub_optimized, - .sub_wrap, - .sub_sat, - .mul, - .mul_safe, - .mul_optimized, - .mul_wrap, - .mul_sat, - .div_float, - .div_float_optimized, - .div_trunc, - .div_trunc_optimized, - .div_floor, - .div_floor_optimized, - .div_exact, - .div_exact_optimized, - .rem, - .rem_optimized, - .mod, - .mod_optimized, - .max, - .min, - .bit_and, - .bit_or, - .shr, - .shr_exact, - .shl, - .shl_exact, - .shl_sat, - .xor, - .cmp_lt, - .cmp_lt_optimized, - .cmp_lte, - .cmp_lte_optimized, - .cmp_eq, - .cmp_eq_optimized, - .cmp_gte, - .cmp_gte_optimized, - .cmp_gt, - .cmp_gt_optimized, - .cmp_neq, - .cmp_neq_optimized, - .bool_and, - .bool_or, - .store, - .store_safe, - .set_union_tag, - .array_elem_val, - .slice_elem_val, - .ptr_elem_val, - .memset, - .memset_safe, - .memcpy, - .memmove, - .atomic_store_unordered, - .atomic_store_monotonic, - .atomic_store_release, - .atomic_store_seq_cst, - .legalize_vec_elem_val, - => { - if (!checkRef(data.bin_op.lhs, zcu)) return false; - if (!checkRef(data.bin_op.rhs, zcu)) return false; - }, - - .not, - .bitcast, - .clz, - .ctz, - .popcount, - .byte_swap, - .bit_reverse, - .abs, - .load, - .fptrunc, - .fpext, - .intcast, - .intcast_safe, - .trunc, - .optional_payload, - .optional_payload_ptr, - .optional_payload_ptr_set, - .wrap_optional, - .unwrap_errunion_payload, - .unwrap_errunion_err, - .unwrap_errunion_payload_ptr, - .unwrap_errunion_err_ptr, - .errunion_payload_ptr_set, - .wrap_errunion_payload, - .wrap_errunion_err, - .struct_field_ptr_index_0, - .struct_field_ptr_index_1, - .struct_field_ptr_index_2, - .struct_field_ptr_index_3, - .get_union_tag, - .slice_len, - .slice_ptr, - .ptr_slice_len_ptr, - .ptr_slice_ptr_ptr, - .array_to_slice, - .int_from_float, - .int_from_float_optimized, - .int_from_float_safe, - .int_from_float_optimized_safe, - .float_from_int, - .splat, - .error_set_has_value, - .addrspace_cast, - .c_va_arg, - .c_va_copy, - => { - if (!checkType(data.ty_op.ty.toType(), zcu)) return false; - if (!checkRef(data.ty_op.operand, zcu)) return false; - }, - - .alloc, - .ret_ptr, - .c_va_start, - => { - if (!checkType(data.ty, zcu)) return false; - }, - - .ptr_add, - .ptr_sub, - .add_with_overflow, - .sub_with_overflow, - .mul_with_overflow, - .shl_with_overflow, - .slice, - .slice_elem_ptr, - .ptr_elem_ptr, - => { - const bin = air.extraData(Air.Bin, data.ty_pl.payload).data; - if (!checkType(data.ty_pl.ty.toType(), zcu)) return false; - if (!checkRef(bin.lhs, zcu)) return false; - if (!checkRef(bin.rhs, zcu)) return false; - }, - - .block, - .loop, - => { - const block = air.unwrapBlock(inst); - if (!checkType(block.ty, zcu)) return false; - if (!checkBody( - air, - block.body, - zcu, - )) return false; - }, - - .dbg_inline_block => { - const block = air.unwrapDbgBlock(inst); - if (!checkType(block.ty, zcu)) return false; - if (!checkBody( - air, - block.body, - zcu, - )) return false; - }, - - .sqrt, - .sin, - .cos, - .tan, - .exp, - .exp2, - .log, - .log2, - .log10, - .floor, - .ceil, - .round, - .trunc_float, - .neg, - .neg_optimized, - .is_null, - .is_non_null, - .is_null_ptr, - .is_non_null_ptr, - .is_err, - .is_non_err, - .is_err_ptr, - .is_non_err_ptr, - .ret, - .ret_safe, - .ret_load, - .is_named_enum_value, - .tag_name, - .error_name, - .cmp_lt_errors_len, - .c_va_end, - .set_err_return_trace, - => { - if (!checkRef(data.un_op, zcu)) return false; - }, - - .br, .switch_dispatch => { - if (!checkRef(data.br.operand, zcu)) return false; - }, - - .cmp_vector, - .cmp_vector_optimized, - => { - const extra = air.extraData(Air.VectorCmp, data.ty_pl.payload).data; - if (!checkType(data.ty_pl.ty.toType(), zcu)) return false; - if (!checkRef(extra.lhs, zcu)) return false; - if (!checkRef(extra.rhs, zcu)) return false; - }, - - .reduce, - .reduce_optimized, - => { - if (!checkRef(data.reduce.operand, zcu)) return false; - }, - - .struct_field_ptr, - .struct_field_val, - => { - const extra = air.extraData(Air.StructField, data.ty_pl.payload).data; - if (!checkType(data.ty_pl.ty.toType(), zcu)) return false; - if (!checkRef(extra.struct_operand, zcu)) return false; - }, - - .shuffle_one => { - const unwrapped = air.unwrapShuffleOne(zcu, inst); - if (!checkType(unwrapped.result_ty, zcu)) return false; - if (!checkRef(unwrapped.operand, zcu)) return false; - for (unwrapped.mask) |m| switch (m.unwrap()) { - .elem => {}, - .value => |val| if (!checkVal(.fromInterned(val), zcu)) return false, - }; - }, - - .shuffle_two => { - const unwrapped = air.unwrapShuffleTwo(zcu, inst); - if (!checkType(unwrapped.result_ty, zcu)) return false; - if (!checkRef(unwrapped.operand_a, zcu)) return false; - if (!checkRef(unwrapped.operand_b, zcu)) return false; - // No values to check because there are no comptime-known values other than undef - }, - - .cmpxchg_weak, - .cmpxchg_strong, - => { - const extra = air.extraData(Air.Cmpxchg, data.ty_pl.payload).data; - if (!checkType(data.ty_pl.ty.toType(), zcu)) return false; - if (!checkRef(extra.ptr, zcu)) return false; - if (!checkRef(extra.expected_value, zcu)) return false; - if (!checkRef(extra.new_value, zcu)) return false; - }, - - .aggregate_init => { - const ty = data.ty_pl.ty.toType(); - const elems_len: usize = @intCast(ty.arrayLen(zcu)); - const elems: []const Air.Inst.Ref = @ptrCast(air.extra.items[data.ty_pl.payload..][0..elems_len]); - if (!checkType(ty, zcu)) return false; - if (ty.zigTypeTag(zcu) == .@"struct") { - for (elems, 0..) |elem, elem_idx| { - if (ty.structFieldIsComptime(elem_idx, zcu)) continue; - if (!checkRef(elem, zcu)) return false; - } - } else { - for (elems) |elem| { - if (!checkRef(elem, zcu)) return false; - } - } - }, - - .union_init => { - const extra = air.extraData(Air.UnionInit, data.ty_pl.payload).data; - if (!checkType(data.ty_pl.ty.toType(), zcu)) return false; - if (!checkRef(extra.init, zcu)) return false; - }, - - .field_parent_ptr => { - const extra = air.extraData(Air.FieldParentPtr, data.ty_pl.payload).data; - if (!checkType(data.ty_pl.ty.toType(), zcu)) return false; - if (!checkRef(extra.field_ptr, zcu)) return false; - }, - - .atomic_load => { - if (!checkRef(data.atomic_load.ptr, zcu)) return false; - }, - - .prefetch => { - if (!checkRef(data.prefetch.ptr, zcu)) return false; - }, - - .runtime_nav_ptr => { - if (!checkType(.fromInterned(data.ty_nav.ty), zcu)) return false; - }, - - .select, - .mul_add, - .legalize_vec_store_elem, - => { - const bin = air.extraData(Air.Bin, data.pl_op.payload).data; - if (!checkRef(data.pl_op.operand, zcu)) return false; - if (!checkRef(bin.lhs, zcu)) return false; - if (!checkRef(bin.rhs, zcu)) return false; - }, - - .atomic_rmw => { - const extra = air.extraData(Air.AtomicRmw, data.pl_op.payload).data; - if (!checkRef(data.pl_op.operand, zcu)) return false; - if (!checkRef(extra.operand, zcu)) return false; - }, - - .call, - .call_always_tail, - .call_never_tail, - .call_never_inline, - => { - const call = air.unwrapCall(inst); - const args = call.args; - if (!checkRef(call.callee, zcu)) return false; - for (args) |arg| if (!checkRef(arg, zcu)) return false; - }, - - .dbg_var_ptr, - .dbg_var_val, - .dbg_arg_inline, - => { - if (!checkRef(data.pl_op.operand, zcu)) return false; - }, - - .@"try", .try_cold => { - const unwrapped_try = air.unwrapTry(inst); - if (!checkRef(unwrapped_try.error_union, zcu)) return false; - if (!checkBody( - air, - unwrapped_try.else_body, - zcu, - )) return false; - }, - - .try_ptr, .try_ptr_cold => { - const unwrapped_try = air.unwrapTryPtr(inst); - if (!checkType(unwrapped_try.error_union_payload_ptr_ty.toType(), zcu)) return false; - if (!checkRef(unwrapped_try.error_union_ptr, zcu)) return false; - if (!checkBody( - air, - unwrapped_try.else_body, - zcu, - )) return false; - }, - - .cond_br => { - const cond_br = air.unwrapCondBr(inst); - if (!checkRef(cond_br.condition, zcu)) return false; - if (!checkBody( - air, - cond_br.then_body, - zcu, - )) return false; - if (!checkBody( - air, - cond_br.else_body, - zcu, - )) return false; - }, - - .switch_br, .loop_switch_br => { - const switch_br = air.unwrapSwitch(inst); - if (!checkRef(switch_br.operand, zcu)) return false; - var it = switch_br.iterateCases(); - while (it.next()) |case| { - for (case.items) |item| if (!checkRef(item, zcu)) return false; - for (case.ranges) |range| { - if (!checkRef(range[0], zcu)) return false; - if (!checkRef(range[1], zcu)) return false; - } - if (!checkBody(air, case.body, zcu)) return false; - } - if (!checkBody(air, it.elseBody(), zcu)) return false; - }, - - .assembly => { - const unwrapped_asm = air.unwrapAsm(inst); - if (!checkType(data.ty_pl.ty.toType(), zcu)) return false; - // Luckily, we only care about the inputs and outputs, so we don't have to do - // the whole null-terminated string dance. - const outputs = unwrapped_asm.outputs; - const inputs = unwrapped_asm.inputs; - - for (outputs) |output| if (output != .none and !checkRef(output, zcu)) return false; - for (inputs) |input| if (input != .none and !checkRef(input, zcu)) return false; - }, - - .legalize_compiler_rt_call => { - const rt_call = air.unwrapCompilerRtCall(inst); - const args = rt_call.args; - for (args) |arg| if (!checkRef(arg, zcu)) return false; - }, - - .trap, - .breakpoint, - .ret_addr, - .frame_addr, - .unreach, - .wasm_memory_size, - .wasm_memory_grow, - .work_item_id, - .work_group_size, - .work_group_id, - .dbg_stmt, - .dbg_empty_stmt, - .err_return_trace, - .save_err_return_trace_index, - .repeat, - => {}, - } - } - return true; -} - -fn checkRef(ref: Air.Inst.Ref, zcu: *Zcu) bool { - const ip_index = ref.toInterned() orelse { - // This operand refers back to a previous instruction. - // We have already checked that instruction's type. - // So, there's no need to check this operand's type. - return true; - }; - return checkVal(Value.fromInterned(ip_index), zcu); -} - -pub fn checkVal(val: Value, zcu: *Zcu) bool { - const ty = val.typeOf(zcu); - if (!checkType(ty, zcu)) return false; - if (val.isUndef(zcu)) return true; - if (ty.toIntern() == .type_type and !checkType(val.toType(), zcu)) return false; - // Check for lazy values - switch (zcu.intern_pool.indexToKey(val.toIntern())) { - .int => |int| switch (int.storage) { - .u64, .i64, .big_int => return true, - .lazy_align, .lazy_size => |ty_index| { - return checkType(Type.fromInterned(ty_index), zcu); - }, - }, - else => return true, - } -} - -pub fn checkType(ty: Type, zcu: *Zcu) bool { - const ip = &zcu.intern_pool; - if (ty.isGenericPoison()) return true; - return switch (ty.zigTypeTag(zcu)) { - .type, - .void, - .bool, - .noreturn, - .int, - .float, - .error_set, - .@"enum", - .@"opaque", - .vector, - // These types can appear due to some dummy instructions Sema introduces and expects to be omitted by Liveness. - // It's a little silly -- but fine, we'll return `true`. - .comptime_float, - .comptime_int, - .undefined, - .null, - .enum_literal, - => true, - - .frame, - .@"anyframe", - => @panic("TODO Air.types_resolved.checkType async frames"), - - .optional => checkType(ty.childType(zcu), zcu), - .error_union => checkType(ty.errorUnionPayload(zcu), zcu), - .pointer => checkType(ty.childType(zcu), zcu), - .array => checkType(ty.childType(zcu), zcu), - - .@"fn" => { - const info = zcu.typeToFunc(ty).?; - for (0..info.param_types.len) |i| { - const param_ty = info.param_types.get(ip)[i]; - if (!checkType(Type.fromInterned(param_ty), zcu)) return false; - } - return checkType(Type.fromInterned(info.return_type), zcu); - }, - .@"struct" => switch (ip.indexToKey(ty.toIntern())) { - .struct_type => { - const struct_obj = zcu.typeToStruct(ty).?; - return switch (struct_obj.layout) { - .@"packed" => struct_obj.backingIntTypeUnordered(ip) != .none, - .auto, .@"extern" => struct_obj.flagsUnordered(ip).fully_resolved, - }; - }, - .tuple_type => |tuple| { - for (0..tuple.types.len) |i| { - const field_is_comptime = tuple.values.get(ip)[i] != .none; - if (field_is_comptime) continue; - const field_ty = tuple.types.get(ip)[i]; - if (!checkType(Type.fromInterned(field_ty), zcu)) return false; - } - return true; - }, - else => unreachable, - }, - .@"union" => return zcu.typeToUnion(ty).?.flagsUnordered(ip).status == .fully_resolved, - }; -} diff --git a/src/Compilation.zig b/src/Compilation.zig @@ -21,7 +21,6 @@ const introspect = @import("introspect.zig"); const link = @import("link.zig"); const tracy = @import("tracy.zig"); const trace = tracy.trace; -const traceNamed = tracy.traceNamed; const build_options = @import("build_options"); const LibCInstallation = std.zig.LibCInstallation; const glibc = @import("libs/glibc.zig"); @@ -89,6 +88,9 @@ framework_dirs: []const []const u8, /// These are only for DLLs dependencies fulfilled by the `.def` files shipped /// with Zig. Static libraries are provided as `link.Input` values. windows_libs: std.StringArrayHashMapUnmanaged(void), +/// The number of items in `windows_libs` which we have already built. All items at or after this +/// index will be built in `performAllTheWork`. +windows_libs_num_done: u32, version: ?std.SemanticVersion, libc_installation: ?*const LibCInstallation, skip_linker_dependencies: bool, @@ -126,16 +128,6 @@ oneshot_prelink_tasks: std.ArrayList(link.PrelinkTask), /// work is queued or not. queued_jobs: QueuedJobs, -work_queues: [ - len: { - var len: usize = 0; - for (std.enums.values(Job.Tag)) |tag| { - len = @max(Job.stage(tag) + 1, len); - } - break :len len; - } -]std.Deque(Job), - /// These jobs are to invoke the Clang compiler to create an object file, which /// gets linked with the Compilation. c_object_work_queue: std.Deque(*CObject), @@ -962,65 +954,6 @@ pub const RcSourceFile = struct { extra_flags: []const []const u8 = &.{}, }; -const Job = union(enum) { - /// Given the generated AIR for a function, put it onto the code generation queue. - /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that - /// all types are resolved before the linker task is queued. - /// If the backend does not support `Zcu.Feature.separate_thread`, codegen and linking happen immediately. - /// Before queueing this `Job`, increase the estimated total item count for both - /// `comp.zcu.?.codegen_prog_node` and `comp.link_prog_node`. - codegen_func: struct { - func: InternPool.Index, - /// The AIR emitted from analyzing `func`; owned by this `Job` in `gpa`. - air: Air, - }, - /// Queue a `link.ZcuTask` to emit this non-function `Nav` into the output binary. - /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that - /// all types are resolved before the linker task is queued. - /// If the backend does not support `Zcu.Feature.separate_thread`, the task is run immediately. - /// Before queueing this `Job`, increase the estimated total item count for `comp.link_prog_node`. - link_nav: InternPool.Nav.Index, - /// Queue a `link.ZcuTask` to emit debug information for this container type. - /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that - /// all types are resolved before the linker task is queued. - /// If the backend does not support `Zcu.Feature.separate_thread`, the task is run immediately. - /// Before queueing this `Job`, increase the estimated total item count for `comp.link_prog_node`. - link_type: InternPool.Index, - /// Before queueing this `Job`, increase the estimated total item count for `comp.link_prog_node`. - update_line_number: InternPool.TrackedInst.Index, - /// The `AnalUnit`, which is *not* a `func`, must be semantically analyzed. - /// This may be its first time being analyzed, or it may be outdated. - /// If the unit is a test function, an `analyze_func` job will then be queued. - analyze_comptime_unit: InternPool.AnalUnit, - /// This function must be semantically analyzed. - /// This may be its first time being analyzed, or it may be outdated. - /// After analysis, a `codegen_func` job will be queued. - /// These must be separate jobs to ensure any needed type resolution occurs *before* codegen. - /// This job is separate from `analyze_comptime_unit` because it has a different priority. - analyze_func: InternPool.Index, - /// The main source file for the module needs to be analyzed. - analyze_mod: *Package.Module, - /// Fully resolve the given `struct` or `union` type. - resolve_type_fully: InternPool.Index, - - /// The value is the index into `windows_libs`. - windows_import_lib: usize, - - const Tag = @typeInfo(Job).@"union".tag_type.?; - fn stage(tag: Tag) usize { - return switch (tag) { - // Prioritize functions so that codegen can get to work on them on a - // separate thread, while Sema goes back to its own work. - .resolve_type_fully, .analyze_func, .codegen_func => 0, - else => 1, - }; - } - comptime { - // Job dependencies - assert(stage(.resolve_type_fully) <= stage(.codegen_func)); - } -}; - pub const CObject = struct { /// Relative to cwd. Owned by arena. src: CSourceFile, @@ -1412,7 +1345,6 @@ pub const MiscTask = enum { wasi_libc_crt_file, compiler_rt, libzigc, - analyze_mod, link_depfile, docs_copy, docs_wasm, @@ -2297,7 +2229,6 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, .root_mod = options.root_mod, .config = options.config, .dirs = options.dirs, - .work_queues = @splat(.empty), .c_object_work_queue = .empty, .win32_resource_work_queue = .empty, .c_source_files = options.c_source_files, @@ -2331,6 +2262,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, .root_name = root_name, .sysroot = sysroot, .windows_libs = .empty, + .windows_libs_num_done = 0, .version = options.version, .libc_installation = libc_dirs.libc_installation, .compiler_rt_strat = compiler_rt_strat, @@ -2693,16 +2625,6 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, } } - // Generate Windows import libs. - if (target.os.tag == .windows) { - const count = comp.windows_libs.count(); - for (0..count) |i| { - try comp.queueJob(.{ .windows_import_lib = i }); - } - // when integrating coff linker with prelink, the above `queueJob` will need to move - // to something in `dispatchPrelinkWork`, which must queue all prelink link tasks - // *before* we begin working on the main job queue. - } if (comp.wantBuildLibUnwindFromSource()) { comp.queued_jobs.libunwind = true; } @@ -2786,7 +2708,6 @@ pub fn destroy(comp: *Compilation) void { if (comp.zcu) |zcu| zcu.deinit(); comp.cache_use.deinit(io); - for (&comp.work_queues) |*work_queue| work_queue.deinit(gpa); comp.c_object_work_queue.deinit(gpa); comp.win32_resource_work_queue.deinit(gpa); @@ -3461,9 +3382,6 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel error.OutOfMemory, error.Canceled => |e| return e, }; } - if (comp.zcu) |zcu| { - try link.File.C.flushEmitH(zcu); - } } /// This function is called by the frontend before flush(). It communicates that @@ -3728,7 +3646,9 @@ const Header = extern struct { src_hash_deps_len: u32, nav_val_deps_len: u32, nav_ty_deps_len: u32, - interned_deps_len: u32, + type_layout_deps_len: u32, + struct_defaults_deps_len: u32, + func_ies_deps_len: u32, zon_file_deps_len: u32, embed_file_deps_len: u32, namespace_deps_len: u32, @@ -3776,7 +3696,9 @@ pub fn saveState(comp: *Compilation) !void { .src_hash_deps_len = @intCast(ip.src_hash_deps.count()), .nav_val_deps_len = @intCast(ip.nav_val_deps.count()), .nav_ty_deps_len = @intCast(ip.nav_ty_deps.count()), - .interned_deps_len = @intCast(ip.interned_deps.count()), + .type_layout_deps_len = @intCast(ip.type_layout_deps.count()), + .struct_defaults_deps_len = @intCast(ip.struct_defaults_deps.count()), + .func_ies_deps_len = @intCast(ip.func_ies_deps.count()), .zon_file_deps_len = @intCast(ip.zon_file_deps.count()), .embed_file_deps_len = @intCast(ip.embed_file_deps.count()), .namespace_deps_len = @intCast(ip.namespace_deps.count()), @@ -3800,7 +3722,7 @@ pub fn saveState(comp: *Compilation) !void { }, }); - try bufs.ensureTotalCapacityPrecise(22 + 9 * pt_headers.items.len); + try bufs.ensureTotalCapacityPrecise(26 + 9 * pt_headers.items.len); addBuf(&bufs, mem.asBytes(&header)); addBuf(&bufs, @ptrCast(pt_headers.items)); @@ -3810,8 +3732,12 @@ pub fn saveState(comp: *Compilation) !void { addBuf(&bufs, @ptrCast(ip.nav_val_deps.values())); addBuf(&bufs, @ptrCast(ip.nav_ty_deps.keys())); addBuf(&bufs, @ptrCast(ip.nav_ty_deps.values())); - addBuf(&bufs, @ptrCast(ip.interned_deps.keys())); - addBuf(&bufs, @ptrCast(ip.interned_deps.values())); + addBuf(&bufs, @ptrCast(ip.type_layout_deps.keys())); + addBuf(&bufs, @ptrCast(ip.type_layout_deps.values())); + addBuf(&bufs, @ptrCast(ip.struct_defaults_deps.keys())); + addBuf(&bufs, @ptrCast(ip.struct_defaults_deps.values())); + addBuf(&bufs, @ptrCast(ip.func_ies_deps.keys())); + addBuf(&bufs, @ptrCast(ip.func_ies_deps.values())); addBuf(&bufs, @ptrCast(ip.zon_file_deps.keys())); addBuf(&bufs, @ptrCast(ip.zon_file_deps.values())); addBuf(&bufs, @ptrCast(ip.embed_file_deps.keys())); @@ -4128,21 +4054,12 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle { const SortOrder = struct { zcu: *Zcu, errors: []const *Zcu.ErrorMsg, - read_err: *?ReadError, - const ReadError = struct { - file: *Zcu.File, - err: Zcu.File.GetSourceError, - }; pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool { - if (ctx.read_err.* != null) return lhs_index < rhs_index; - var bad_file: *Zcu.File = undefined; - return ctx.errors[lhs_index].src_loc.lessThan(ctx.errors[rhs_index].src_loc, ctx.zcu, &bad_file) catch |err| { - ctx.read_err.* = .{ - .file = bad_file, - .err = err, - }; - return lhs_index < rhs_index; - }; + return Zcu.ErrorMsg.order( + ctx.errors[lhs_index], + ctx.errors[rhs_index], + ctx.zcu, + ).compare(.lt); } }; @@ -4152,16 +4069,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle { var entries = try zcu.failed_analysis.entries.clone(gpa); errdefer entries.deinit(gpa); - var read_err: ?SortOrder.ReadError = null; entries.sort(SortOrder{ .zcu = zcu, .errors = entries.items(.value), - .read_err = &read_err, }); - if (read_err) |e| { - try unableToLoadZcuFile(zcu, &bundle, e.file, e.err); - break :zcu_errors; - } break :s entries.slice(); }; defer sorted_failed_analysis.deinit(gpa); @@ -4200,6 +4111,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle { } } } + try zcu.addDependencyLoopErrors(&bundle); for (zcu.failed_codegen.values()) |error_msg| { try addModuleErrorMsg(zcu, &bundle, error_msg.*, false); } @@ -4219,7 +4131,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle { .notes_len = 1, }); const notes_start = try bundle.reserveNotes(1); - bundle.extra.items[notes_start] = @intFromEnum(try bundle.addErrorMessage(.{ + bundle.extra.items[notes_start] = @intFromEnum(bundle.addErrorMessageAssumeCapacity(.{ .msg = try bundle.printString("use '--error-limit {d}' to increase limit", .{ actual_error_count, }), @@ -4241,10 +4153,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle { .notes_len = 2, }); const notes_start = try bundle.reserveNotes(2); - bundle.extra.items[notes_start + 0] = @intFromEnum(try bundle.addErrorMessage(.{ + bundle.extra.items[notes_start + 0] = @intFromEnum(bundle.addErrorMessageAssumeCapacity(.{ .msg = try bundle.addString("run 'zig libc -h' to learn about libc installations"), })); - bundle.extra.items[notes_start + 1] = @intFromEnum(try bundle.addErrorMessage(.{ + bundle.extra.items[notes_start + 1] = @intFromEnum(bundle.addErrorMessageAssumeCapacity(.{ .msg = try bundle.addString("run 'zig targets' to see the targets for which zig can always provide libc"), })); } @@ -4268,7 +4180,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle { if (!refs.contains(logging_unit)) continue; try messages.append(gpa, .{ .src_loc = compile_log.src(), - .msg = undefined, // populated later + .msg = "", // populated later, but must be valid for `sort` call below .notes = &.{}, // We actually clear this later for most of these, but we populate // this field for now to avoid having to allocate more data to track @@ -4281,33 +4193,11 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle { // Okay, there *are* referenced compile logs. Sort them into a consistent order. - { - const SortContext = struct { - zcu: *Zcu, - read_err: *?ReadError, - const ReadError = struct { - file: *Zcu.File, - err: Zcu.File.GetSourceError, - }; - fn lessThan(ctx: @This(), lhs: Zcu.ErrorMsg, rhs: Zcu.ErrorMsg) bool { - if (ctx.read_err.* != null) return false; - var bad_file: *Zcu.File = undefined; - return lhs.src_loc.lessThan(rhs.src_loc, ctx.zcu, &bad_file) catch |err| { - ctx.read_err.* = .{ - .file = bad_file, - .err = err, - }; - return false; - }; - } - }; - var read_err: ?SortContext.ReadError = null; - std.mem.sort(Zcu.ErrorMsg, messages.items, @as(SortContext, .{ .read_err = &read_err, .zcu = zcu }), SortContext.lessThan); - if (read_err) |e| { - try unableToLoadZcuFile(zcu, &bundle, e.file, e.err); - break :compile_log_text ""; + std.mem.sort(Zcu.ErrorMsg, messages.items, zcu, struct { + fn lessThan(zcu_inner: *Zcu, lhs: Zcu.ErrorMsg, rhs: Zcu.ErrorMsg) bool { + return Zcu.ErrorMsg.order(&lhs, &rhs, zcu_inner).compare(.lt); } - } + }.lessThan); var log_text: std.ArrayList(u8) = .empty; defer log_text.deinit(gpa); @@ -4331,6 +4221,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle { break :compile_log_text try log_text.toOwnedSlice(gpa); }; + defer gpa.free(compile_log_text); // TODO: eventually, this should be behind `std.debug.runtime_safety`. But right now, this is a // very common way for incremental compilation bugs to manifest, so let's always check it. @@ -4439,7 +4330,6 @@ pub fn addModuleErrorMsg( already_added_error: bool, ) Allocator.Error!void { const gpa = eb.gpa; - const ip = &zcu.intern_pool; const err_src_loc = module_err_msg.src_loc.upgrade(zcu); const err_source = err_src_loc.file_scope.getSource(zcu) catch |err| { return unableToLoadZcuFile(zcu, eb, err_src_loc.file_scope, err); @@ -4452,66 +4342,12 @@ pub fn addModuleErrorMsg( var ref_traces: std.ArrayList(ErrorBundle.ReferenceTrace) = .empty; defer ref_traces.deinit(gpa); - rt: { - const rt_root = module_err_msg.reference_trace_root.unwrap() orelse break :rt; - const max_references = zcu.comp.reference_trace orelse refs: { - if (already_added_error) break :rt; + if (module_err_msg.reference_trace_root.unwrap()) |root| { + const frame_limit: u32 = zcu.comp.reference_trace orelse refs: { + if (already_added_error) break :refs 0; break :refs default_reference_trace_len; }; - - const all_references = try zcu.resolveReferences(); - - var seen: std.AutoHashMapUnmanaged(InternPool.AnalUnit, void) = .empty; - defer seen.deinit(gpa); - - var referenced_by = rt_root; - while (all_references.get(referenced_by)) |maybe_ref| { - const ref = maybe_ref orelse break; - const gop = try seen.getOrPut(gpa, ref.referencer); - if (gop.found_existing) break; - if (ref_traces.items.len < max_references) { - var last_call_src = ref.src; - var opt_inline_frame = ref.inline_frame; - while (opt_inline_frame.unwrap()) |inline_frame| { - const f = inline_frame.ptr(zcu).*; - const func_nav = ip.indexToKey(f.callee).func.owner_nav; - const func_name = ip.getNav(func_nav).name.toSlice(ip); - addReferenceTraceFrame(zcu, eb, &ref_traces, func_name, last_call_src, true) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.AlreadyReported => { - // An incomplete reference trace isn't the end of the world; just cut it off. - break :rt; - }, - }; - last_call_src = f.call_src; - opt_inline_frame = f.parent; - } - const root_name: ?[]const u8 = switch (ref.referencer.unwrap()) { - .@"comptime" => "comptime", - .nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip), - .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip), - .func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip), - .memoized_state => null, - }; - if (root_name) |n| { - addReferenceTraceFrame(zcu, eb, &ref_traces, n, last_call_src, false) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.AlreadyReported => { - // An incomplete reference trace isn't the end of the world; just cut it off. - break :rt; - }, - }; - } - } - referenced_by = ref.referencer; - } - - if (seen.count() > ref_traces.items.len) { - try ref_traces.append(gpa, .{ - .decl_name = @intCast(seen.count() - ref_traces.items.len), - .src_loc = .none, - }); - } + try zcu.populateReferenceTrace(root, frame_limit, eb, &ref_traces); } const src_loc = try eb.addSourceLocation(.{ @@ -4576,43 +4412,10 @@ pub fn addModuleErrorMsg( const notes_start = try eb.reserveNotes(notes_len); for (notes_start.., notes.keys()) |i, note| { - eb.extra.items[i] = @intFromEnum(try eb.addErrorMessage(note)); + eb.extra.items[i] = @intFromEnum(eb.addErrorMessageAssumeCapacity(note)); } } -fn addReferenceTraceFrame( - zcu: *Zcu, - eb: *ErrorBundle.Wip, - ref_traces: *std.ArrayList(ErrorBundle.ReferenceTrace), - name: []const u8, - lazy_src: Zcu.LazySrcLoc, - inlined: bool, -) error{ OutOfMemory, AlreadyReported }!void { - const gpa = zcu.gpa; - const src = lazy_src.upgrade(zcu); - const source = src.file_scope.getSource(zcu) catch |err| { - try unableToLoadZcuFile(zcu, eb, src.file_scope, err); - return error.AlreadyReported; - }; - const span = src.span(zcu) catch |err| { - try unableToLoadZcuFile(zcu, eb, src.file_scope, err); - return error.AlreadyReported; - }; - const loc = std.zig.findLineColumn(source, span.main); - try ref_traces.append(gpa, .{ - .decl_name = try eb.printString("{s}{s}", .{ name, if (inlined) " [inlined]" else "" }), - .src_loc = try eb.addSourceLocation(.{ - .src_path = try eb.printString("{f}", .{src.file_scope.path.fmt(zcu.comp)}), - .span_start = span.start, - .span_main = span.main, - .span_end = span.end, - .line = @intCast(loc.line), - .column = @intCast(loc.column), - .source_line = 0, - }), - }); -} - fn addWholeFileError( zcu: *Zcu, eb: *ErrorBundle.Wip, @@ -4669,13 +4472,7 @@ fn performAllTheWork( comp: *Compilation, main_progress_node: std.Progress.Node, update_arena: Allocator, -) JobError!void { - defer if (comp.zcu) |zcu| { - zcu.codegen_task_pool.cancel(zcu); - // Regardless of errors, `comp.zcu` needs to update its generation number. - zcu.generation += 1; - }; - +) (Allocator.Error || Io.Cancelable)!void { const io = comp.io; // This is awkward: we don't want to start the timer until later, but we won't want to stop it @@ -4708,216 +4505,32 @@ fn performAllTheWork( misc_group.async(io, workerDocsWasm, .{ comp, main_progress_node }); } - if (comp.zcu) |zcu| { - const tracy_trace = traceNamed(@src(), "astgen"); - defer tracy_trace.end(); - - const zir_prog_node = main_progress_node.start("AST Lowering", 0); - defer zir_prog_node.end(); - - var timer = comp.startTimer(); - defer if (timer.finish(io)) |ns| { - comp.mutex.lockUncancelable(io); - defer comp.mutex.unlock(io); - comp.time_report.?.stats.real_ns_files = ns; - }; - - const gpa = comp.gpa; - - var astgen_group: Io.Group = .init; - defer astgen_group.cancel(io); - - // We cannot reference `zcu.import_table` after we spawn any `workerUpdateFile` jobs, - // because on single-threaded targets the worker will be run eagerly, meaning the - // `import_table` could be mutated, and not even holding `comp.mutex` will save us. So, - // build up a list of the files to update *before* we spawn any jobs. - var astgen_work_items: std.MultiArrayList(struct { - file_index: Zcu.File.Index, - file: *Zcu.File, - }) = .empty; - defer astgen_work_items.deinit(gpa); - // Not every item in `import_table` will need updating, because some are builtin.zig - // files. However, most will, so let's just reserve sufficient capacity upfront. - try astgen_work_items.ensureTotalCapacity(gpa, zcu.import_table.count()); - for (zcu.import_table.keys()) |file_index| { - const file = zcu.fileByIndex(file_index); - if (file.is_builtin) { - // This is a `builtin.zig`, so updating is redundant. However, we want to make - // sure the file contents are still correct on disk, since it can improve the - // debugging experience better. That job only needs `file`, so we can kick it - // off right now. - astgen_group.async(io, workerUpdateBuiltinFile, .{ comp, file }); - continue; - } - astgen_work_items.appendAssumeCapacity(.{ - .file_index = file_index, - .file = file, - }); - } - - // Now that we're not going to touch `zcu.import_table` again, we can spawn `workerUpdateFile` jobs. - for (astgen_work_items.items(.file_index), astgen_work_items.items(.file)) |file_index, file| { - astgen_group.async(io, workerUpdateFile, .{ - comp, file, file_index, zir_prog_node, &astgen_group, - }); - } - - // On the other hand, it's fine to directly iterate `zcu.embed_table.keys()` here - // because `workerUpdateEmbedFile` can't invalidate it. The different here is that one - // `@embedFile` can't trigger analysis of a new `@embedFile`! - for (0.., zcu.embed_table.keys()) |ef_index_usize, ef| { - const ef_index: Zcu.EmbedFile.Index = @enumFromInt(ef_index_usize); - astgen_group.async(io, workerUpdateEmbedFile, .{ - comp, ef_index, ef, - }); - } - - try astgen_group.await(io); - } - + defer if (comp.zcu) |zcu| zcu.codegen_task_pool.cancel(zcu); if (comp.zcu) |zcu| { const pt: Zcu.PerThread = .activate(zcu, .main); - defer pt.deactivate(); - - const gpa = zcu.gpa; - - // On an incremental update, a source file might become "dead", in that all imports of - // the file were removed. This could even change what module the file belongs to! As such, - // we do a traversal over the files, to figure out which ones are alive and the modules - // they belong to. - const any_fatal_files = try pt.computeAliveFiles(); - - // If the cache mode is `whole`, add every alive source file to the manifest. - switch (comp.cache_use) { - .whole => |whole| if (whole.cache_manifest) |man| { - for (zcu.alive_files.keys()) |file_index| { - const file = zcu.fileByIndex(file_index); - - switch (file.status) { - .never_loaded => unreachable, // AstGen tried to load it - .retryable_failure => continue, // the file cannot be read; this is a guaranteed error - .astgen_failure, .success => {}, // the file was read successfully - } - - const path = try file.path.toAbsolute(comp.dirs, gpa); - defer gpa.free(path); - - const result = res: { - try whole.cache_manifest_mutex.lock(io); - defer whole.cache_manifest_mutex.unlock(io); - if (file.source) |source| { - break :res man.addFilePostContents(path, source, file.stat); - } else { - break :res man.addFilePost(path); - } - }; - result catch |err| switch (err) { - error.OutOfMemory => |e| return e, - else => { - try pt.reportRetryableFileError(file_index, "unable to update cache: {s}", .{@errorName(err)}); - continue; - }, - }; - } - }, - .none, .incremental => {}, - } - - if (any_fatal_files or - zcu.multi_module_err != null or - zcu.failed_imports.items.len > 0 or - comp.alloc_failure_occurred) - { - // We give up right now! No updating of ZIR refs, no nothing. The idea is that this prevents - // us from invalidating lots of incremental dependencies due to files with e.g. parse errors. - // However, this means our analysis data is invalid, so we want to omit all analysis errors. - zcu.skip_analysis_this_update = true; - // Since we're skipping analysis, there are no ZCU link tasks. - comp.link_queue.finishZcuQueue(comp); - // Let other compilation work finish to collect as many errors as possible. - try misc_group.await(io); - comp.link_queue.wait(io); - return; - } - - if (comp.time_report) |*tr| { - tr.stats.n_reachable_files = @intCast(zcu.alive_files.count()); - } - - if (comp.config.incremental) { - const update_zir_refs_node = main_progress_node.start("Update ZIR References", 0); - defer update_zir_refs_node.end(); - try pt.updateZirRefs(); - } - try zcu.flushRetryableFailures(); - - // It's analysis time! Queue up our initial analysis. - for (zcu.analysisRoots()) |mod| { - try comp.queueJob(.{ .analyze_mod = mod }); - } - - zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0); - if (comp.bin_file != null) { - zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0); - } - // We increment `pending_codegen_jobs` so that it doesn't reach 0 until after analysis finishes. - // That prevents the "Code Generation" node from constantly disappearing and reappearing when - // we're probably going to analyze more functions at some point. - assert(zcu.pending_codegen_jobs.swap(1, .monotonic) == 0); // don't let this become 0 until analysis finishes - } - // When analysis ends, delete the progress nodes for "Semantic Analysis" and possibly "Code Generation". - defer if (comp.zcu) |zcu| { - zcu.sema_prog_node.end(); - zcu.sema_prog_node = .none; - if (zcu.pending_codegen_jobs.fetchSub(1, .monotonic) == 1) { - // Decremented to 0, so all done. - zcu.codegen_prog_node.end(); - zcu.codegen_prog_node = .none; - } - }; - - if (comp.zcu) |zcu| { - if (!zcu.backendSupportsFeature(.separate_thread)) { - // Close the ZCU task queue. Prelink may still be running, but the closed - // queue will cause the linker task to exit once prelink finishes. The - // closed queue also communicates to `enqueueZcu` that it should wait for - // the linker task to finish and then run ZCU tasks serially. - comp.link_queue.finishZcuQueue(comp); + defer { + pt.deactivate(); + // Regardless of errors, `comp.zcu` needs to update its generation number. + zcu.generation += 1; } + try pt.update(main_progress_node, &decl_work_timer); } - if (comp.zcu != null) { - // Start the timer for the "decls" part of the pipeline (Sema, CodeGen, link). - decl_work_timer = comp.startTimer(); - } + comp.link_queue.finishZcuQueue(comp); - work: while (true) { - for (&comp.work_queues) |*work_queue| if (work_queue.popFront()) |job| { - try processOneJob(.main, comp, job); - continue :work; + // This has to happen after the main semantic analysis loop because it is possible for Sema to + // call `addLinkLib` and hence add more items to `comp.windows_libs`. + for (comp.windows_libs.keys()[comp.windows_libs_num_done..]) |link_lib| { + mingw.buildImportLib(comp, link_lib) catch |err| { + // TODO Surface more error details. + comp.lockAndSetMiscFailure( + .windows_import_lib, + "unable to generate DLL import .lib file for {s}: {t}", + .{ link_lib, err }, + ); }; - if (comp.zcu) |zcu| { - // If there's no work queued, check if there's anything outdated - // which we need to work on, and queue it if so. - if (try zcu.findOutdatedToAnalyze()) |outdated| { - try comp.queueJob(switch (outdated.unwrap()) { - .func => |f| .{ .analyze_func = f }, - .memoized_state, - .@"comptime", - .nav_ty, - .nav_val, - .type, - => .{ .analyze_comptime_unit = outdated }, - }); - continue; - } - zcu.sema_prog_node.end(); - zcu.sema_prog_node = .none; - } - break; } - - comp.link_queue.finishZcuQueue(comp); + comp.windows_libs_num_done = @intCast(comp.windows_libs.count()); // Main thread work is all done, now just wait for all async work. try misc_group.await(io); @@ -5148,172 +4761,6 @@ fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node }; } -const JobError = Allocator.Error || Io.Cancelable; - -pub fn queueJob(comp: *Compilation, job: Job) !void { - try comp.work_queues[Job.stage(job)].pushBack(comp.gpa, job); -} - -pub fn queueJobs(comp: *Compilation, jobs: []const Job) !void { - for (jobs) |job| try comp.queueJob(job); -} - -fn processOneJob(tid: Zcu.PerThread.Id, comp: *Compilation, job: Job) JobError!void { - switch (job) { - .codegen_func => |func| { - const zcu = comp.zcu.?; - const gpa = zcu.gpa; - var owned_air: ?Air = func.air; - defer if (owned_air) |*air| air.deinit(gpa); - - if (!owned_air.?.typesFullyResolved(zcu)) { - // Type resolution failed in a way which affects this function. This is a transitive - // failure, but it doesn't need recording, because this function semantically depends - // on the failed type, so when it is changed the function is updated. - zcu.codegen_prog_node.completeOne(); - comp.link_prog_node.completeOne(); - return; - } - - // Some linkers need to refer to the AIR. In that case, the linker is not running - // concurrently, so we'll just keep ownership of the AIR for ourselves instead of - // letting the codegen job destroy it. - const disown_air = zcu.backendSupportsFeature(.separate_thread); - - // Begin the codegen task. If the codegen/link queue is backed up, this might - // block until the linker is able to process some tasks. - const codegen_task = try zcu.codegen_task_pool.start(zcu, func.func, &owned_air.?, disown_air); - if (disown_air) owned_air = null; - - try comp.link_queue.enqueueZcu(comp, tid, .{ .link_func = codegen_task }); - }, - .link_nav => |nav_index| { - const zcu = comp.zcu.?; - const nav = zcu.intern_pool.getNav(nav_index); - if (nav.analysis != null) { - const unit: InternPool.AnalUnit = .wrap(.{ .nav_val = nav_index }); - if (zcu.failed_analysis.contains(unit) or zcu.transitive_failed_analysis.contains(unit)) { - comp.link_prog_node.completeOne(); - return; - } - } - assert(nav.status == .fully_resolved); - if (!Air.valFullyResolved(zcu.navValue(nav_index), zcu)) { - // Type resolution failed in a way which affects this `Nav`. This is a transitive - // failure, but it doesn't need recording, because this `Nav` semantically depends - // on the failed type, so when it is changed the `Nav` will be updated. - comp.link_prog_node.completeOne(); - return; - } - try comp.link_queue.enqueueZcu(comp, tid, .{ .link_nav = nav_index }); - }, - .link_type => |ty| { - const zcu = comp.zcu.?; - if (zcu.failed_types.fetchSwapRemove(ty)) |*entry| entry.value.deinit(zcu.gpa); - if (!Air.typeFullyResolved(.fromInterned(ty), zcu)) { - // Type resolution failed in a way which affects this type. This is a transitive - // failure, but it doesn't need recording, because this type semantically depends - // on the failed type, so when that is changed, this type will be updated. - comp.link_prog_node.completeOne(); - return; - } - try comp.link_queue.enqueueZcu(comp, tid, .{ .link_type = ty }); - }, - .update_line_number => |tracked_inst| { - try comp.link_queue.enqueueZcu(comp, tid, .{ .update_line_number = tracked_inst }); - }, - .analyze_func => |func| { - const tracy_trace = traceNamed(@src(), "analyze_func"); - defer tracy_trace.end(); - - const pt: Zcu.PerThread = .activate(comp.zcu.?, tid); - defer pt.deactivate(); - - pt.ensureFuncBodyUpToDate(func) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.Canceled => |e| return e, - error.AnalysisFail => return, - }; - }, - .analyze_comptime_unit => |unit| { - const tracy_trace = traceNamed(@src(), "analyze_comptime_unit"); - defer tracy_trace.end(); - - const pt: Zcu.PerThread = .activate(comp.zcu.?, tid); - defer pt.deactivate(); - - const maybe_err: Zcu.SemaError!void = switch (unit.unwrap()) { - .@"comptime" => |cu| pt.ensureComptimeUnitUpToDate(cu), - .nav_ty => |nav| pt.ensureNavTypeUpToDate(nav), - .nav_val => |nav| pt.ensureNavValUpToDate(nav), - .type => |ty| if (pt.ensureTypeUpToDate(ty)) |_| {} else |err| err, - .memoized_state => |stage| pt.ensureMemoizedStateUpToDate(stage), - .func => unreachable, - }; - maybe_err catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.Canceled => |e| return e, - error.AnalysisFail => return, - }; - - queue_test_analysis: { - if (!comp.config.is_test) break :queue_test_analysis; - const nav = switch (unit.unwrap()) { - .nav_val => |nav| nav, - else => break :queue_test_analysis, - }; - - // Check if this is a test function. - const ip = &pt.zcu.intern_pool; - if (!pt.zcu.test_functions.contains(nav)) { - break :queue_test_analysis; - } - - // Tests are always emitted in test binaries. The decl_refs are created by - // Zcu.populateTestFunctions, but this will not queue body analysis, so do - // that now. - try pt.zcu.ensureFuncBodyAnalysisQueued(ip.getNav(nav).status.fully_resolved.val); - } - }, - .resolve_type_fully => |ty| { - const tracy_trace = traceNamed(@src(), "resolve_type_fully"); - defer tracy_trace.end(); - - const pt: Zcu.PerThread = .activate(comp.zcu.?, tid); - defer pt.deactivate(); - Type.fromInterned(ty).resolveFully(pt) catch |err| switch (err) { - error.OutOfMemory, error.Canceled => |e| return e, - error.AnalysisFail => return, - }; - }, - .analyze_mod => |mod| { - const tracy_trace = traceNamed(@src(), "analyze_mod"); - defer tracy_trace.end(); - - const pt: Zcu.PerThread = .activate(comp.zcu.?, tid); - defer pt.deactivate(); - pt.semaMod(mod) catch |err| switch (err) { - error.OutOfMemory, error.Canceled => |e| return e, - error.AnalysisFail => return, - }; - }, - .windows_import_lib => |index| { - const tracy_trace = traceNamed(@src(), "windows_import_lib"); - defer tracy_trace.end(); - - const link_lib = comp.windows_libs.keys()[index]; - mingw.buildImportLib(comp, link_lib) catch |err| { - // TODO Surface more error details. - comp.lockAndSetMiscFailure( - .windows_import_lib, - "unable to generate DLL import .lib file for {s}: {t}", - .{ link_lib, err }, - ); - }; - }, - } -} - fn createDepFile(comp: *Compilation, dep_file: []const u8, bin_file: Cache.Path) anyerror!void { const io = comp.io; @@ -5641,112 +5088,6 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) SubU }; } -fn workerUpdateFile( - comp: *Compilation, - file: *Zcu.File, - file_index: Zcu.File.Index, - prog_node: std.Progress.Node, - group: *Io.Group, -) void { - const io = comp.io; - const tid: Zcu.PerThread.Id = .acquire(io); - defer tid.release(io); - - const child_prog_node = prog_node.start(fs.path.basename(file.path.sub_path), 0); - defer child_prog_node.end(); - - const pt: Zcu.PerThread = .activate(comp.zcu.?, tid); - defer pt.deactivate(); - pt.updateFile(file_index, file) catch |err| { - pt.reportRetryableFileError(file_index, "unable to load '{s}': {s}", .{ fs.path.basename(file.path.sub_path), @errorName(err) }) catch |oom| switch (oom) { - error.OutOfMemory => { - comp.mutex.lockUncancelable(io); - defer comp.mutex.unlock(io); - comp.setAllocFailure(); - }, - }; - return; - }; - - switch (file.getMode()) { - .zig => {}, // continue to logic below - .zon => return, // ZON can't import anything so we're done - } - - // Discover all imports in the file. Imports of modules we ignore for now since we don't - // know which module we're in, but imports of file paths might need us to queue up other - // AstGen jobs. - const imports_index = file.zir.?.extra[@intFromEnum(Zir.ExtraIndex.imports)]; - if (imports_index != 0) { - const extra = file.zir.?.extraData(Zir.Inst.Imports, imports_index); - var import_i: u32 = 0; - var extra_index = extra.end; - - while (import_i < extra.data.imports_len) : (import_i += 1) { - const item = file.zir.?.extraData(Zir.Inst.Imports.Item, extra_index); - extra_index = item.end; - - const import_path = file.zir.?.nullTerminatedString(item.data.name); - - if (pt.discoverImport(file.path, import_path)) |res| switch (res) { - .module, .existing_file => {}, - .new_file => |new| { - group.async(io, workerUpdateFile, .{ - comp, new.file, new.index, prog_node, group, - }); - }, - } else |err| switch (err) { - error.OutOfMemory => { - comp.mutex.lockUncancelable(io); - defer comp.mutex.unlock(io); - comp.setAllocFailure(); - }, - } - } - } -} - -fn workerUpdateBuiltinFile(comp: *Compilation, file: *Zcu.File) void { - Builtin.updateFileOnDisk(file, comp) catch |err| comp.lockAndSetMiscFailure( - .write_builtin_zig, - "unable to write '{f}': {s}", - .{ file.path.fmt(comp), @errorName(err) }, - ); -} - -fn workerUpdateEmbedFile(comp: *Compilation, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) void { - const io = comp.io; - const tid: Zcu.PerThread.Id = .acquire(io); - defer tid.release(io); - comp.detectEmbedFileUpdate(tid, ef_index, ef) catch |err| switch (err) { - error.OutOfMemory => { - comp.mutex.lockUncancelable(io); - defer comp.mutex.unlock(io); - comp.setAllocFailure(); - }, - }; -} - -fn detectEmbedFileUpdate(comp: *Compilation, tid: Zcu.PerThread.Id, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) !void { - const io = comp.io; - const zcu = comp.zcu.?; - const pt: Zcu.PerThread = .activate(zcu, tid); - defer pt.deactivate(); - - const old_val = ef.val; - const old_err = ef.err; - - try pt.updateEmbedFile(ef, null); - - if (ef.val != .none and ef.val == old_val) return; // success, value unchanged - if (ef.val == .none and old_val == .none and ef.err == old_err) return; // failure, error unchanged - - comp.mutex.lockUncancelable(io); - defer comp.mutex.unlock(io); - - try zcu.markDependeeOutdated(.not_marked_po, .{ .embed_file = ef_index }); -} - pub fn obtainCObjectCacheManifest( comp: *const Compilation, owner_mod: *Package.Module, @@ -8375,12 +7716,10 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void { // If we haven't seen this library yet and we're targeting Windows, we need // to queue up a work item to produce the DLL import library for this. const gop = try comp.windows_libs.getOrPut(comp.gpa, lib_name); - if (gop.found_existing) return; - { + if (!gop.found_existing) { errdefer _ = comp.windows_libs.pop(); gop.key_ptr.* = try comp.gpa.dupe(u8, lib_name); } - try comp.queueJob(.{ .windows_import_lib = gop.index }); } /// This decides the optimization mode for all zig-provided libraries, including diff --git a/src/IncrementalDebugServer.zig b/src/IncrementalDebugServer.zig @@ -306,12 +306,8 @@ fn handleCommand(zcu: *Zcu, w: *Io.Writer, cmd_str: []const u8, arg_str: []const try w.print("[{d}] ", .{i}); switch (dependee) { .src_hash, .namespace, .namespace_name, .zon_file, .embed_file => try w.print("{f}", .{zcu.fmtDependee(dependee)}), - .nav_val, .nav_ty => |nav| try w.print("{s} {d}", .{ @tagName(dependee), @intFromEnum(nav) }), - .interned => |ip_index| switch (ip.indexToKey(ip_index)) { - .struct_type, .union_type, .enum_type => try w.print("type {d}", .{@intFromEnum(ip_index)}), - .func => try w.print("func {d}", .{@intFromEnum(ip_index)}), - else => unreachable, - }, + .nav_val, .nav_ty => |nav| try w.print("{t} {d}", .{ dependee, @intFromEnum(nav) }), + .type_layout, .struct_defaults, .func_ies => |ip_index| try w.print("{t} {d}", .{ dependee, @intFromEnum(ip_index) }), .memoized_state => |stage| try w.print("memoized_state {s}", .{@tagName(stage)}), } try w.writeByte('\n'); @@ -376,8 +372,10 @@ fn parseAnalUnit(str: []const u8) ?AnalUnit { return .wrap(.{ .nav_val = @enumFromInt(parseIndex(idx_str) orelse return null) }); } else if (std.mem.eql(u8, kind, "nav_ty")) { return .wrap(.{ .nav_ty = @enumFromInt(parseIndex(idx_str) orelse return null) }); - } else if (std.mem.eql(u8, kind, "type")) { - return .wrap(.{ .type = @enumFromInt(parseIndex(idx_str) orelse return null) }); + } else if (std.mem.eql(u8, kind, "type_layout")) { + return .wrap(.{ .type_layout = @enumFromInt(parseIndex(idx_str) orelse return null) }); + } else if (std.mem.eql(u8, kind, "struct_defaults")) { + return .wrap(.{ .struct_defaults = @enumFromInt(parseIndex(idx_str) orelse return null) }); } else if (std.mem.eql(u8, kind, "func")) { return .wrap(.{ .func = @enumFromInt(parseIndex(idx_str) orelse return null) }); } else if (std.mem.eql(u8, kind, "memoized_state")) { diff --git a/src/InternPool.zig b/src/InternPool.zig @@ -17,6 +17,7 @@ const Hash = std.hash.Wyhash; const Zir = std.zig.Zir; const Zcu = @import("Zcu.zig"); +const TypeClass = @import("Type.zig").Class; /// One item per thread, indexed by `tid`, which is dense and unique per thread. locals: []Local, @@ -47,11 +48,15 @@ nav_val_deps: std.AutoArrayHashMapUnmanaged(Nav.Index, DepEntry.Index), /// Dependencies on the type of a Nav. /// Value is index into `dep_entries` of the first dependency on this Nav value. nav_ty_deps: std.AutoArrayHashMapUnmanaged(Nav.Index, DepEntry.Index), -/// Dependencies on an interned value, either: -/// * a runtime function (invalidated when its IES changes) -/// * a container type requiring resolution (invalidated when the type must be recreated at a new index) -/// Value is index into `dep_entries` of the first dependency on this interned value. -interned_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index), +/// Dependencies on a function's inferred error set. Key is the function body, not the IES. +/// Value is index into `dep_entries` of the first dependency on this function's IES. +func_ies_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index), +/// Dependencies on the resolved layout of a `struct`, `union`, or `enum` type. +/// Value is index into `dep_entries` of the first dependency on this type's layout. +type_layout_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index), +/// Dependencies on the resolved default field values of a `struct` type. +/// Value is index into `dep_entries` of the first dependency on this type's inits. +struct_defaults_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index), /// Dependencies on a ZON file. Triggered by `@import` of ZON. /// Value is index into `dep_entries` of the first dependency on this ZON file. zon_file_deps: std.AutoArrayHashMapUnmanaged(FileIndex, DepEntry.Index), @@ -104,7 +109,9 @@ pub const empty: InternPool = .{ .src_hash_deps = .empty, .nav_val_deps = .empty, .nav_ty_deps = .empty, - .interned_deps = .empty, + .func_ies_deps = .empty, + .type_layout_deps = .empty, + .struct_defaults_deps = .empty, .zon_file_deps = .empty, .embed_file_deps = .empty, .namespace_deps = .empty, @@ -415,7 +422,8 @@ pub const AnalUnit = packed struct(u64) { @"comptime", nav_val, nav_ty, - type, + type_layout, + struct_defaults, func, memoized_state, }; @@ -427,9 +435,10 @@ pub const AnalUnit = packed struct(u64) { nav_val: Nav.Index, /// This `AnalUnit` resolves the type of the given `Nav`. nav_ty: Nav.Index, - /// This `AnalUnit` resolves the given `struct`/`union`/`enum` type. - /// Generated tag enums are never used here (they do not undergo type resolution). - type: InternPool.Index, + /// This `AnalUnit` resolves the layout of the given `struct`, `union`, or `enum` type. + type_layout: InternPool.Index, + /// This `AnalUnit` resolves the default field values of the given `struct` type. + struct_defaults: InternPool.Index, /// This `AnalUnit` analyzes the body of the given runtime function. func: InternPool.Index, /// This `AnalUnit` resolves all state which is memoized in fields on `Zcu`. @@ -538,6 +547,8 @@ pub const Nav = struct { analysis: ?struct { namespace: NamespaceIndex, zir_index: TrackedInst.Index, + /// Initially `false`. Set to `true` by `setWantNavAnalysis`. + wanted: bool, }, status: union(enum) { /// This `Nav` is pending semantic analysis. @@ -735,7 +746,7 @@ pub const Nav = struct { const Repr = struct { name: NullTerminatedString, fqn: NullTerminatedString, - // The following 1 fields are either both populated, or both `.none`. + // The following 2 fields are either both populated, or both `.none`. analysis_namespace: OptionalNamespaceIndex, analysis_zir_index: TrackedInst.Index.Optional, /// Populated only if `bits.status != .unresolved`. @@ -754,7 +765,7 @@ pub const Nav = struct { @"addrspace": std.builtin.AddressSpace, /// Populated only if `bits.status == .type_resolved`. is_threadlocal: bool, - _: u1 = 0, + want_analysis: bool, }; fn unpack(repr: Repr) Nav { @@ -764,6 +775,7 @@ pub const Nav = struct { .analysis = if (repr.analysis_namespace.unwrap()) |namespace| .{ .namespace = namespace, .zir_index = repr.analysis_zir_index.unwrap().?, + .wanted = repr.bits.want_analysis, } else a: { assert(repr.analysis_zir_index == .none); break :a null; @@ -816,6 +828,7 @@ pub const Nav = struct { .alignment = .none, .@"addrspace" = .generic, .is_threadlocal = false, + .want_analysis = if (nav.analysis) |a| a.wanted else false, }, .type_resolved => |r| .{ .status = if (r.is_extern_decl) .type_resolved_extern_decl else .type_resolved, @@ -823,6 +836,7 @@ pub const Nav = struct { .alignment = r.alignment, .@"addrspace" = r.@"addrspace", .is_threadlocal = r.is_threadlocal, + .want_analysis = if (nav.analysis) |a| a.wanted else false, }, .fully_resolved => |r| .{ .status = .fully_resolved, @@ -830,6 +844,7 @@ pub const Nav = struct { .alignment = r.alignment, .@"addrspace" = r.@"addrspace", .is_threadlocal = false, + .want_analysis = if (nav.analysis) |a| a.wanted else false, }, }, }; @@ -840,7 +855,10 @@ pub const Dependee = union(enum) { src_hash: TrackedInst.Index, nav_val: Nav.Index, nav_ty: Nav.Index, - interned: Index, + /// Index is the function, not its IES. + func_ies: Index, + type_layout: Index, + struct_defaults: Index, zon_file: FileIndex, embed_file: Zcu.EmbedFile.Index, namespace: TrackedInst.Index, @@ -892,7 +910,9 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI .src_hash => |x| ip.src_hash_deps.get(x), .nav_val => |x| ip.nav_val_deps.get(x), .nav_ty => |x| ip.nav_ty_deps.get(x), - .interned => |x| ip.interned_deps.get(x), + .func_ies => |x| ip.func_ies_deps.get(x), + .type_layout => |x| ip.type_layout_deps.get(x), + .struct_defaults => |x| ip.struct_defaults_deps.get(x), .zon_file => |x| ip.zon_file_deps.get(x), .embed_file => |x| ip.embed_file_deps.get(x), .namespace => |x| ip.namespace_deps.get(x), @@ -965,7 +985,9 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend .src_hash => ip.src_hash_deps, .nav_val => ip.nav_val_deps, .nav_ty => ip.nav_ty_deps, - .interned => ip.interned_deps, + .func_ies => ip.func_ies_deps, + .type_layout => ip.type_layout_deps, + .struct_defaults => ip.struct_defaults_deps, .zon_file => ip.zon_file_deps, .embed_file => ip.embed_file_deps, .namespace => ip.namespace_deps, @@ -2065,15 +2087,15 @@ pub const Key = union(enum) { simple_type: SimpleType, /// This represents a struct that has been explicitly declared in source code, /// or was created with `@Struct`. It is unique and based on a declaration. - struct_type: NamespaceType, + struct_type: ContainerType, /// This is a tuple type. Tuples are logically similar to structs, but have some /// important differences in semantics; they do not undergo staged type resolution, /// so cannot be self-referential, and they are not considered container/namespace /// types, so cannot have declarations and have structural equality properties. tuple_type: TupleType, - union_type: NamespaceType, - opaque_type: NamespaceType, - enum_type: NamespaceType, + union_type: ContainerType, + opaque_type: ContainerType, + enum_type: ContainerType, func_type: FuncType, error_set_type: ErrorSetType, /// The payload is the function body, either a `func_decl` or `func_instance`. @@ -2092,10 +2114,6 @@ pub const Key = union(enum) { enum_literal: NullTerminatedString, /// A specific enum tag, indicated by the integer tag value. enum_tag: EnumTag, - /// An empty enum or union. TODO: this value's existence is strange, because such a type in - /// reality has no values. See #15909. - /// Payload is the type for which we are an empty value. - empty_enum_value: Index, float: Float, ptr: Ptr, slice: Slice, @@ -2109,6 +2127,8 @@ pub const Key = union(enum) { aggregate: Aggregate, /// An instance of a union. un: Union, + /// An instance of a `packed struct` or `packed union`. + bitpack: Bitpack, /// A comptime function call with a memoized result. memoized_call: Key.MemoizedCall, @@ -2211,16 +2231,10 @@ pub const Key = union(enum) { /// * `loadUnionType` /// * `loadEnumType` /// * `loadOpaqueType` - pub const NamespaceType = union(enum) { + pub const ContainerType = union(enum) { /// This type corresponds to an actual source declaration, e.g. `struct { ... }`. /// It is hashed based on its ZIR instruction index and set of captures. declared: Declared, - /// This type is an automatically-generated enum tag type for a union. - /// It is hashed based on the index of the union type it corresponds to. - generated_tag: struct { - /// The union for which this is a tag type. - union_type: Index, - }, /// This type originates from a reification via `@Enum`, `@Struct`, `@Union` or from an anonymous initialization. /// It is hashed based on its ZIR instruction index and fields, attributes, etc. /// To avoid making this key overly complex, the type-specific data is hashed by Sema. @@ -2231,6 +2245,9 @@ pub const Key = union(enum) { /// A hash of this type's attributes, fields, etc, generated by Sema. type_hash: u64, }, + /// This type is an automatically-generated enum tag type for this union type. + /// It is hashed based on the index of the union type it corresponds to. + generated_union_tag: Index, pub const Declared = struct { /// A `struct_decl`, `union_decl`, `enum_decl`, or `opaque_decl` instruction. @@ -2254,7 +2271,6 @@ pub const Key = union(enum) { noalias_bits: u32, cc: std.builtin.CallingConvention, is_var_args: bool, - is_generic: bool, is_noinline: bool, pub fn paramIsComptime(self: @This(), i: u5) bool { @@ -2273,7 +2289,6 @@ pub const Key = union(enum) { a.comptime_bits == b.comptime_bits and a.noalias_bits == b.noalias_bits and a.is_var_args == b.is_var_args and - a.is_generic == b.is_generic and a.is_noinline == b.is_noinline and std.meta.eql(a.cc, b.cc); } @@ -2287,7 +2302,6 @@ pub const Key = union(enum) { std.hash.autoHash(hasher, self.noalias_bits); std.hash.autoHash(hasher, self.cc); std.hash.autoHash(hasher, self.is_var_args); - std.hash.autoHash(hasher, self.is_generic); std.hash.autoHash(hasher, self.is_noinline); } }; @@ -2403,17 +2417,6 @@ pub const Key = union(enum) { @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release); } - pub fn setAnalyzed(func: Func, ip: *InternPool, io: Io) void { - const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const analysis_ptr = func.analysisPtr(ip); - var analysis = analysis_ptr.*; - analysis.is_analyzed = true; - @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release); - } - /// Returns a pointer that becomes invalid after any additions to the `InternPool`. fn zirBodyInstPtr(func: Func, ip: *const InternPool) *TrackedInst.Index { const extra = ip.getLocalShared(func.tid).extra.acquire(); @@ -2471,8 +2474,6 @@ pub const Key = union(enum) { u64: u64, i64: i64, big_int: BigIntConst, - lazy_align: Index, - lazy_size: Index, /// Big enough to fit any non-BigInt value pub const BigIntSpace = struct { @@ -2485,7 +2486,6 @@ pub const Key = union(enum) { return switch (storage) { .big_int => |x| x, inline .u64, .i64 => |x| BigIntMutable.init(&space.limbs, x).toConst(), - .lazy_align, .lazy_size => unreachable, }; } }; @@ -2680,6 +2680,15 @@ pub const Key = union(enum) { }; }; + /// As well as a key, this type doubles as the payload in `extra` for `Tag.bitpack`. + pub const Bitpack = struct { + /// The `packed struct` or `packed union` type. + ty: Index, + /// The contents of the bitpack, represented as the backing integer value. The type of this + /// value is the same as the backing integer type of `ty`. + backing_int_val: Index, + }; + pub const MemoizedCall = struct { func: Index, arg_values: []const Index, @@ -2710,7 +2719,6 @@ pub const Key = union(enum) { .err, .enum_literal, .enum_tag, - .empty_enum_value, .inferred_error_set_type, .un, => |x| Hash.hash(seed, asBytes(&x)), @@ -2742,13 +2750,13 @@ pub const Key = union(enum) { std.hash.autoHash(&hasher, cv); } }, - .generated_tag => |generated_tag| { - std.hash.autoHash(&hasher, generated_tag.union_type); - }, .reified => |reified| { std.hash.autoHash(&hasher, reified.zir_index); std.hash.autoHash(&hasher, reified.type_hash); }, + .generated_union_tag => |union_type| { + std.hash.autoHash(&hasher, union_type); + }, } return hasher.final(); }, @@ -2756,23 +2764,12 @@ pub const Key = union(enum) { .int => |int| { var hasher = Hash.init(seed); // Canonicalize all integers by converting them to BigIntConst. - switch (int.storage) { - .u64, .i64, .big_int => { - var buffer: Key.Int.Storage.BigIntSpace = undefined; - const big_int = int.storage.toBigInt(&buffer); - - std.hash.autoHash(&hasher, int.ty); - std.hash.autoHash(&hasher, big_int.positive); - for (big_int.limbs) |limb| std.hash.autoHash(&hasher, limb); - }, - .lazy_align, .lazy_size => |lazy_ty| { - std.hash.autoHash( - &hasher, - @as(@typeInfo(Key.Int.Storage).@"union".tag_type.?, int.storage), - ); - std.hash.autoHash(&hasher, lazy_ty); - }, - } + var buffer: Key.Int.Storage.BigIntSpace = undefined; + const big_int = int.storage.toBigInt(&buffer); + + std.hash.autoHash(&hasher, int.ty); + std.hash.autoHash(&hasher, big_int.positive); + for (big_int.limbs) |limb| std.hash.autoHash(&hasher, limb); return hasher.final(); }, @@ -2929,6 +2926,8 @@ pub const Key = union(enum) { asBytes(&e.relocation) ++ asBytes(&e.is_const) ++ asBytes(&e.alignment) ++ asBytes(&e.@"addrspace") ++ asBytes(&e.zir_index) ++ &[1]u8{@intFromEnum(e.source)}), + + .bitpack => |bitpack| Hash.hash(seed, asBytes(&bitpack.ty) ++ asBytes(&bitpack.backing_int_val)), }; } @@ -3002,9 +3001,9 @@ pub const Key = union(enum) { const b_info = b.enum_tag; return std.meta.eql(a_info, b_info); }, - .empty_enum_value => |a_info| { - const b_info = b.empty_enum_value; - return a_info == b_info; + .bitpack => |a_info| { + const b_info = b.bitpack; + return a_info.ty == b_info.ty and a_info.backing_int_val == b_info.backing_int_val; }, .variable => |a_info| { @@ -3102,27 +3101,16 @@ pub const Key = union(enum) { .u64 => |bb| aa == bb, .i64 => |bb| aa == bb, .big_int => |bb| bb.orderAgainstScalar(aa) == .eq, - .lazy_align, .lazy_size => false, }, .i64 => |aa| switch (b_info.storage) { .u64 => |bb| aa == bb, .i64 => |bb| aa == bb, .big_int => |bb| bb.orderAgainstScalar(aa) == .eq, - .lazy_align, .lazy_size => false, }, .big_int => |aa| switch (b_info.storage) { .u64 => |bb| aa.orderAgainstScalar(bb) == .eq, .i64 => |bb| aa.orderAgainstScalar(bb) == .eq, .big_int => |bb| aa.eql(bb), - .lazy_align, .lazy_size => false, - }, - .lazy_align => |aa| switch (b_info.storage) { - .u64, .i64, .big_int, .lazy_size => false, - .lazy_align => |bb| aa == bb, - }, - .lazy_size => |aa| switch (b_info.storage) { - .u64, .i64, .big_int, .lazy_align => false, - .lazy_size => |bb| aa == bb, }, }; }, @@ -3175,12 +3163,12 @@ pub const Key = union(enum) { }; return std.mem.eql(u32, @ptrCast(a_captures), @ptrCast(b_captures)); }, - .generated_tag => |a_gt| return a_gt.union_type == b_info.generated_tag.union_type, .reified => |a_r| { const b_r = b_info.reified; return a_r.zir_index == b_r.zir_index and a_r.type_hash == b_r.type_hash; }, + .generated_union_tag => |a_union_ty| return a_union_ty == b_info.generated_union_tag, } }, .aggregate => |a_info| { @@ -3292,19 +3280,17 @@ pub const Key = union(enum) { .enum_tag, .aggregate, .un, + .bitpack, => |x| x.ty, .enum_literal => .enum_literal_type, .undef => |x| x, - .empty_enum_value => |x| x, .simple_value => |s| switch (s) { - .undefined => .undefined_type, .void => .void_type, .null => .null_type, .false, .true => .bool_type, - .empty_tuple => .empty_tuple_type, .@"unreachable" => .noreturn_type, }, @@ -3313,374 +3299,53 @@ pub const Key = union(enum) { } }; -pub const RequiresComptime = enum(u2) { no, yes, unknown, wip }; - -// Unlike `Tag.TypeUnion` which is an encoding, and `Key.UnionType` which is a -// minimal hashmap key, this type is a convenience type that contains info -// needed by semantic analysis. -pub const LoadedUnionType = struct { - tid: Zcu.PerThread.Id, - /// The index of the `Tag.TypeUnion` payload. - extra_index: u32, - // TODO: the non-fqn will be needed by the new dwarf structure - /// The name of this union type. - name: NullTerminatedString, - /// Represents the declarations inside this union. - namespace: NamespaceIndex, - /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after. - /// Otherwise, this is `.none`. - name_nav: Nav.Index.Optional, - /// The enum tag type. - enum_tag_ty: Index, - /// List of field types in declaration order. - /// These are `none` until `status` is `have_field_types` or `have_layout`. - field_types: Index.Slice, - /// List of field alignments in declaration order. - /// `none` means the ABI alignment of the type. - /// If this slice has length 0 it means all elements are `none`. - field_aligns: Alignment.Slice, - /// Index of the union_decl or reify ZIR instruction. +pub const LoadedStructType = struct { + /// Index of the `struct_decl` or `reify` ZIR instruction. zir_index: TrackedInst.Index, captures: CaptureValue.Slice, + is_reified: bool, - pub const RuntimeTag = enum(u2) { - none, - safety, - tagged, - - pub fn hasTag(self: RuntimeTag) bool { - return switch (self) { - .none => false, - .tagged, .safety => true, - }; - } - }; - - pub const Status = enum(u3) { - none, - field_types_wip, - have_field_types, - layout_wip, - have_layout, - fully_resolved_wip, - /// The types and all its fields have had their layout resolved. - /// Even through pointer, which `have_layout` does not ensure. - fully_resolved, - - pub fn haveFieldTypes(status: Status) bool { - return switch (status) { - .none, - .field_types_wip, - => false, - .have_field_types, - .layout_wip, - .have_layout, - .fully_resolved_wip, - .fully_resolved, - => true, - }; - } - - pub fn haveLayout(status: Status) bool { - return switch (status) { - .none, - .field_types_wip, - .have_field_types, - .layout_wip, - => false, - .have_layout, - .fully_resolved_wip, - .fully_resolved, - => true, - }; - } - }; - - pub fn loadTagType(self: LoadedUnionType, ip: *const InternPool) LoadedEnumType { - return ip.loadEnumType(self.enum_tag_ty); - } - - /// Pointer to an enum type which is used for the tag of the union. - /// This type is created even for untagged unions, even when the memory - /// layout does not store the tag. - /// Whether zig chooses this type or the user specifies it, it is stored here. - /// This will be set to the null type until status is `have_field_types`. - /// This accessor is provided so that the tag type can be mutated, and so that - /// when it is mutated, the mutations are observed. - /// The returned pointer expires with any addition to the `InternPool`. - fn tagTypePtr(self: LoadedUnionType, ip: *const InternPool) *Index { - const extra = ip.getLocalShared(self.tid).extra.acquire(); - const field_index = std.meta.fieldIndex(Tag.TypeUnion, "tag_ty").?; - return @ptrCast(&extra.view().items(.@"0")[self.extra_index + field_index]); - } - - pub fn tagTypeUnordered(u: LoadedUnionType, ip: *const InternPool) Index { - return @atomicLoad(Index, u.tagTypePtr(ip), .unordered); - } - - pub fn setTagType(u: LoadedUnionType, ip: *InternPool, io: Io, tag_type: Index) void { - const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - @atomicStore(Index, u.tagTypePtr(ip), tag_type, .release); - } - - /// The returned pointer expires with any addition to the `InternPool`. - fn flagsPtr(self: LoadedUnionType, ip: *const InternPool) *Tag.TypeUnion.Flags { - const extra = ip.getLocalShared(self.tid).extra.acquire(); - const field_index = std.meta.fieldIndex(Tag.TypeUnion, "flags").?; - return @ptrCast(&extra.view().items(.@"0")[self.extra_index + field_index]); - } - - pub fn flagsUnordered(u: LoadedUnionType, ip: *const InternPool) Tag.TypeUnion.Flags { - return @atomicLoad(Tag.TypeUnion.Flags, u.flagsPtr(ip), .unordered); - } - - pub fn setStatus(u: LoadedUnionType, ip: *InternPool, io: Io, status: Status) void { - const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = u.flagsPtr(ip); - var flags = flags_ptr.*; - flags.status = status; - @atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release); - } - - pub fn setStatusIfLayoutWip(u: LoadedUnionType, ip: *InternPool, io: Io, status: Status) void { - const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = u.flagsPtr(ip); - var flags = flags_ptr.*; - if (flags.status == .layout_wip) flags.status = status; - @atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release); - } - - pub fn setAlignment(u: LoadedUnionType, ip: *InternPool, io: Io, alignment: Alignment) void { - const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = u.flagsPtr(ip); - var flags = flags_ptr.*; - flags.alignment = alignment; - @atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release); - } - - pub fn assumeRuntimeBitsIfFieldTypesWip(u: LoadedUnionType, ip: *InternPool, io: Io) bool { - const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = u.flagsPtr(ip); - var flags = flags_ptr.*; - defer if (flags.status == .field_types_wip) { - flags.assumed_runtime_bits = true; - @atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release); - }; - return flags.status == .field_types_wip; - } - - pub fn requiresComptime(u: LoadedUnionType, ip: *const InternPool) RequiresComptime { - return u.flagsUnordered(ip).requires_comptime; - } - - pub fn setRequiresComptimeWip(u: LoadedUnionType, ip: *InternPool, io: Io) RequiresComptime { - const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = u.flagsPtr(ip); - var flags = flags_ptr.*; - defer if (flags.requires_comptime == .unknown) { - flags.requires_comptime = .wip; - @atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release); - }; - return flags.requires_comptime; - } - - pub fn setRequiresComptime(u: LoadedUnionType, ip: *InternPool, io: Io, requires_comptime: RequiresComptime) void { - assert(requires_comptime != .wip); // see setRequiresComptimeWip - - const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = u.flagsPtr(ip); - var flags = flags_ptr.*; - flags.requires_comptime = requires_comptime; - @atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release); - } - - pub fn assumePointerAlignedIfFieldTypesWip(u: LoadedUnionType, ip: *InternPool, io: Io, ptr_align: Alignment) bool { - const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = u.flagsPtr(ip); - var flags = flags_ptr.*; - defer if (flags.status == .field_types_wip) { - flags.alignment = ptr_align; - flags.assumed_pointer_aligned = true; - @atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release); - }; - return flags.status == .field_types_wip; - } - - /// The returned pointer expires with any addition to the `InternPool`. - fn sizePtr(self: LoadedUnionType, ip: *const InternPool) *u32 { - const extra = ip.getLocalShared(self.tid).extra.acquire(); - const field_index = std.meta.fieldIndex(Tag.TypeUnion, "size").?; - return &extra.view().items(.@"0")[self.extra_index + field_index]; - } - - pub fn sizeUnordered(u: LoadedUnionType, ip: *const InternPool) u32 { - return @atomicLoad(u32, u.sizePtr(ip), .unordered); - } - - /// The returned pointer expires with any addition to the `InternPool`. - fn paddingPtr(self: LoadedUnionType, ip: *const InternPool) *u32 { - const extra = ip.getLocalShared(self.tid).extra.acquire(); - const field_index = std.meta.fieldIndex(Tag.TypeUnion, "padding").?; - return &extra.view().items(.@"0")[self.extra_index + field_index]; - } - - pub fn paddingUnordered(u: LoadedUnionType, ip: *const InternPool) u32 { - return @atomicLoad(u32, u.paddingPtr(ip), .unordered); - } - - pub fn hasTag(self: LoadedUnionType, ip: *const InternPool) bool { - return self.flagsUnordered(ip).runtime_tag.hasTag(); - } - - pub fn haveFieldTypes(self: LoadedUnionType, ip: *const InternPool) bool { - return self.flagsUnordered(ip).status.haveFieldTypes(); - } - - pub fn haveLayout(self: LoadedUnionType, ip: *const InternPool) bool { - return self.flagsUnordered(ip).status.haveLayout(); - } - - pub fn setHaveLayout(u: LoadedUnionType, ip: *InternPool, io: Io, size: u32, padding: u32, alignment: Alignment) void { - const extra_mutex = &ip.getLocal(u.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - @atomicStore(u32, u.sizePtr(ip), size, .unordered); - @atomicStore(u32, u.paddingPtr(ip), padding, .unordered); - const flags_ptr = u.flagsPtr(ip); - var flags = flags_ptr.*; - flags.alignment = alignment; - flags.status = .have_layout; - @atomicStore(Tag.TypeUnion.Flags, flags_ptr, flags, .release); - } - - pub fn fieldAlign(self: LoadedUnionType, ip: *const InternPool, field_index: usize) Alignment { - if (self.field_aligns.len == 0) return .none; - return self.field_aligns.get(ip)[field_index]; - } - - /// This does not mutate the field of LoadedUnionType. - pub fn setZirIndex(self: LoadedUnionType, ip: *InternPool, new_zir_index: TrackedInst.Index.Optional) void { - const flags_field_index = std.meta.fieldIndex(Tag.TypeUnion, "flags").?; - const zir_index_field_index = std.meta.fieldIndex(Tag.TypeUnion, "zir_index").?; - const ptr: *TrackedInst.Index.Optional = - @ptrCast(&ip.extra_.items[self.flags_index - flags_field_index + zir_index_field_index]); - ptr.* = new_zir_index; - } - - pub fn setFieldTypes(self: LoadedUnionType, ip: *const InternPool, types: []const Index) void { - @memcpy(self.field_types.get(ip), types); - } - - pub fn setFieldAligns(self: LoadedUnionType, ip: *const InternPool, aligns: []const Alignment) void { - if (aligns.len == 0) return; - assert(self.flagsUnordered(ip).any_aligned_fields); - @memcpy(self.field_aligns.get(ip), aligns); - } -}; - -pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType { - const unwrapped_index = index.unwrap(ip); - const extra_list = unwrapped_index.getExtra(ip); - const data = unwrapped_index.getData(ip); - const type_union = extraDataTrail(extra_list, Tag.TypeUnion, data); - const fields_len = type_union.data.fields_len; - - var extra_index = type_union.end; - const captures_len = if (type_union.data.flags.any_captures) c: { - const len = extra_list.view().items(.@"0")[extra_index]; - extra_index += 1; - break :c len; - } else 0; - - const captures: CaptureValue.Slice = .{ - .tid = unwrapped_index.tid, - .start = extra_index, - .len = captures_len, - }; - extra_index += captures_len; - if (type_union.data.flags.is_reified) { - extra_index += 2; // PackedU64 - } - - const field_types: Index.Slice = .{ - .tid = unwrapped_index.tid, - .start = extra_index, - .len = fields_len, - }; - extra_index += fields_len; - - const field_aligns = if (type_union.data.flags.any_aligned_fields) a: { - const a: Alignment.Slice = .{ - .tid = unwrapped_index.tid, - .start = extra_index, - .len = fields_len, - }; - extra_index += std.math.divCeil(u32, fields_len, 4) catch unreachable; - break :a a; - } else Alignment.Slice.empty; - - return .{ - .tid = unwrapped_index.tid, - .extra_index = data, - .name = type_union.data.name, - .name_nav = type_union.data.name_nav, - .namespace = type_union.data.namespace, - .enum_tag_ty = type_union.data.tag_ty, - .field_types = field_types, - .field_aligns = field_aligns, - .zir_index = type_union.data.zir_index, - .captures = captures, - }; -} - -pub const LoadedStructType = struct { - tid: Zcu.PerThread.Id, - /// The index of the `Tag.TypeStruct` or `Tag.TypeStructPacked` payload. - extra_index: u32, // TODO: the non-fqn will be needed by the new dwarf structure /// The name of this struct type. name: NullTerminatedString, - namespace: NamespaceIndex, /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after. /// Otherwise, or if this is a file's root struct type, this is `.none`. name_nav: Nav.Index.Optional, - /// Index of the `struct_decl` or `reify` ZIR instruction. - zir_index: TrackedInst.Index, + namespace: NamespaceIndex, + layout: std.builtin.Type.ContainerLayout, + /// May be `undefined` if `layout != .@"packed"`. + packed_backing_mode: BackingTypeMode, + + /// Initially `false`, and set to `true` once any dependency on or reference to the struct's + /// layout is encountered, after which it is never reset to `false`, even across incremental + /// updates. + /// + /// This field is purely an optimization to avoid resolving the layout of types whose layouts + /// are never demanded. If this field is `true` but the layout is not actually needed, the + /// compiler frontend resolves this by traversing the reference graph at the end of each update + /// with `Zcu.resolveReferences` and hiding compile errors which arise from this analysis. + want_layout: bool, + + // The remaining fields are only valid once the struct's layout is resolved. + field_name_map: MapIndex, field_names: NullTerminatedString.Slice, field_types: Index.Slice, - field_inits: Index.Slice, + field_defaults: Index.Slice, field_aligns: Alignment.Slice, - runtime_order: RuntimeOrder.Slice, - comptime_bits: ComptimeBits, - offsets: Offsets, - names_map: OptionalMapIndex, - captures: CaptureValue.Slice, + field_is_comptime_bits: ComptimeBits, + /// If `layout` is `.@"packed"`, this is `.empty`. + field_runtime_order: RuntimeOrder.Slice, + /// If `layout` is `.@"packed"`, this is `.empty`. + field_offsets: Offsets, + /// Only valid if `layout` is `.@"packed"`. + packed_backing_int_type: Index, + /// Only valid if `layout` is *not* `.@"packed"`. + class: TypeClass, + /// Only valid if `layout` is *not* `.@"packed"`. + size: u32, + /// Only valid if `layout` is *not* `.@"packed"`. + alignment: Alignment, pub const ComptimeBits = struct { tid: Zcu.PerThread.Id, @@ -3690,22 +3355,14 @@ pub const LoadedStructType = struct { pub const empty: ComptimeBits = .{ .tid = .main, .start = 0, .len = 0 }; - pub fn get(this: ComptimeBits, ip: *const InternPool) []u32 { + pub fn getAll(this: ComptimeBits, ip: *const InternPool) []u32 { const extra = ip.getLocalShared(this.tid).extra.acquire(); return extra.view().items(.@"0")[this.start..][0..this.len]; } - pub fn getBit(this: ComptimeBits, ip: *const InternPool, i: usize) bool { + pub fn get(this: ComptimeBits, ip: *const InternPool, i: usize) bool { if (this.len == 0) return false; - return @as(u1, @truncate(this.get(ip)[i / 32] >> @intCast(i % 32))) != 0; - } - - pub fn setBit(this: ComptimeBits, ip: *const InternPool, i: usize) void { - this.get(ip)[i / 32] |= @as(u32, 1) << @intCast(i % 32); - } - - pub fn clearBit(this: ComptimeBits, ip: *const InternPool, i: usize) void { - this.get(ip)[i / 32] &= ~(@as(u32, 1) << @intCast(i % 32)); + return @as(u1, @truncate(this.getAll(ip)[i / 32] >> @intCast(i % 32))) != 0; } }; @@ -3753,865 +3410,602 @@ pub const LoadedStructType = struct { /// Look up field index based on field name. pub fn nameIndex(s: LoadedStructType, ip: *const InternPool, name: NullTerminatedString) ?u32 { - const names_map = s.names_map.unwrap() orelse { - const i = name.toUnsigned(ip) orelse return null; - if (i >= s.field_types.len) return null; - return i; - }; - const map = names_map.get(ip); + const map = s.field_name_map.get(ip); const adapter: NullTerminatedString.Adapter = .{ .strings = s.field_names.get(ip) }; const field_index = map.getIndexAdapted(name, adapter) orelse return null; return @intCast(field_index); } - /// Returns the already-existing field with the same name, if any. - pub fn addFieldName( - s: LoadedStructType, - ip: *InternPool, - name: NullTerminatedString, - ) ?u32 { - const extra = ip.getLocalShared(s.tid).extra.acquire(); - return ip.addFieldName(extra, s.names_map.unwrap().?, s.field_names.start, name); + /// Iterates over non-comptime fields in the order they are laid out in memory at runtime. + /// May or may not include zero-bit fields. + /// Asserts the struct is not packed. + pub fn iterateRuntimeOrder(s: *const LoadedStructType, ip: *const InternPool) RuntimeOrderIterator { + switch (s.layout) { + .auto => { + const ro = std.mem.sliceTo(s.field_runtime_order.get(ip), .omitted); + return .{ + .runtime_order = ro, + .fields_len = @intCast(ro.len), + .next_index = 0, + }; + }, + .@"extern" => return .{ + .runtime_order = null, + .fields_len = s.field_names.len, + .next_index = 0, + }, + .@"packed" => unreachable, + } } + pub const RuntimeOrderIterator = struct { + runtime_order: ?[]const RuntimeOrder, + fields_len: u32, + next_index: u32, + pub fn next(it: *RuntimeOrderIterator) ?u32 { + const i = it.next_index; + if (i == it.fields_len) return null; + it.next_index = i + 1; + const ro = it.runtime_order orelse return i; + return ro[i].toInt().?; + } + }; - pub fn fieldAlign(s: LoadedStructType, ip: *const InternPool, i: usize) Alignment { - if (s.field_aligns.len == 0) return .none; - return s.field_aligns.get(ip)[i]; + pub fn iterateRuntimeOrderReverse(s: *const LoadedStructType, ip: *InternPool) ReverseRuntimeOrderIterator { + switch (s.layout) { + .auto => { + const ro = std.mem.sliceTo(s.field_runtime_order.get(ip), .omitted); + return .{ + .runtime_order = ro, + .last_index = @intCast(ro.len), + }; + }, + .@"extern" => return .{ + .runtime_order = null, + .last_index = s.field_names.len, + }, + .@"packed" => unreachable, + } } + pub const ReverseRuntimeOrderIterator = struct { + runtime_order: ?[]const RuntimeOrder, + last_index: u32, + pub fn next(it: *ReverseRuntimeOrderIterator) ?u32 { + if (it.last_index == 0) return null; + const i = it.last_index - 1; + it.last_index = i; + const ro = it.runtime_order orelse return i; + return ro[i].toInt().?; + } + }; +}; - pub fn fieldInit(s: LoadedStructType, ip: *const InternPool, i: usize) Index { - if (s.field_inits.len == 0) return .none; - assert(s.haveFieldInits(ip)); - return s.field_inits.get(ip)[i]; - } +/// Unlike `Tag.TypeUnion` which is an encoding, and `Key.UnionType` which is a +/// minimal hashmap key, this type is a convenience type that contains info +/// needed by semantic analysis. +pub const LoadedUnionType = struct { + /// Index of the `union_decl` or `reify` ZIR instruction. + zir_index: TrackedInst.Index, + captures: CaptureValue.Slice, + is_reified: bool, - pub fn fieldName(s: LoadedStructType, ip: *const InternPool, i: usize) NullTerminatedString { - return s.field_names.get(ip)[i]; - } + // TODO: the non-fqn will be needed by the new dwarf structure + /// The name of this union type. + name: NullTerminatedString, + /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after. + /// Otherwise, this is `.none`. + name_nav: Nav.Index.Optional, + namespace: NamespaceIndex, - pub fn fieldIsComptime(s: LoadedStructType, ip: *const InternPool, i: usize) bool { - return s.comptime_bits.getBit(ip, i); - } + layout: std.builtin.Type.ContainerLayout, + enum_tag_mode: BackingTypeMode, + /// May be `undefined` if `layout != .@"packed"`. + packed_backing_mode: BackingTypeMode, + + /// Only reified unions store field names; typically they should be loaded from `enum_tag_type` + /// instead. Reified unions store them because type resolution needs them in order to validate + /// or populate `enum_tag_type`. + reified_field_names: NullTerminatedString.Slice, + + /// Initially `false`, and set to `true` once any dependency on or reference to the struct's + /// layout is encountered, after which it is never reset to `false`, even across incremental + /// updates. + /// + /// This field is purely an optimization to avoid resolving the layout of types whose layouts + /// are never demanded. If this field is `true` but the layout is not actually needed, the + /// compiler frontend resolves this by traversing the reference graph at the end of each update + /// with `Zcu.resolveReferences` and hiding compile errors which arise from this analysis. + want_layout: bool, - pub fn setFieldComptime(s: LoadedStructType, ip: *InternPool, i: usize) void { - s.comptime_bits.setBit(ip, i); - } + // The remaining fields are only valid once the union's layout is resolved. + field_types: Index.Slice, + field_aligns: Alignment.Slice, + tag_usage: TagUsage, + /// While `tag_usage` indicates whether the union should logically contain a tag, it may be + /// omitted if the union layout is resolved as OPV or NPV. This field is `true` iff there is an + /// actual runtime tag, with one or more runtime bits, in the union layout. It is always `false` + /// if `layout` is not `.auto`. + has_runtime_tag: bool, + /// Even if `tag_usage == .none` and `has_runtime_tag == false`, this is still populated with + /// the union's "hypothetical" tag type. + enum_tag_type: Index, + /// Only valid if `layout` is `.@"packed"`. + packed_backing_int_type: Index, + /// Not valid if `layout` is `.@"packed"`. + class: TypeClass, + /// Not valid if `layout` is `.@"packed"`. + size: u32, + /// Not valid if `layout` is `.@"packed"`. + padding: u32, + /// Not valid if `layout` is `.@"packed"`. + alignment: Alignment, + + pub const TagUsage = enum(u2) { + none, + safety, + tagged, + }; +}; - /// The returned pointer expires with any addition to the `InternPool`. - /// Asserts the struct is not packed. - fn flagsPtr(s: LoadedStructType, ip: *const InternPool) *Tag.TypeStruct.Flags { - assert(s.layout != .@"packed"); - const extra = ip.getLocalShared(s.tid).extra.acquire(); - const flags_field_index = std.meta.fieldIndex(Tag.TypeStruct, "flags").?; - return @ptrCast(&extra.view().items(.@"0")[s.extra_index + flags_field_index]); - } +pub const LoadedEnumType = struct { + /// This is `none` iff this is a generated tag type. + /// Otherwise, index of the `enum_decl` or `reify` ZIR instruction. + zir_index: TrackedInst.Index.Optional, + captures: CaptureValue.Slice, + /// If `zir_index` is `.none`, this is the union type for which this enum is the tag type. + owner_union: Index, + is_reified: bool, - pub fn flagsUnordered(s: LoadedStructType, ip: *const InternPool) Tag.TypeStruct.Flags { - return @atomicLoad(Tag.TypeStruct.Flags, s.flagsPtr(ip), .unordered); - } + // TODO: the non-fqn will be needed by the new dwarf structure + /// The name of this enum type. + name: NullTerminatedString, + /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after. + /// Otherwise, this is `.none`. + name_nav: Nav.Index.Optional, + namespace: NamespaceIndex, - /// The returned pointer expires with any addition to the `InternPool`. - /// Asserts that the struct is packed. - fn packedFlagsPtr(s: LoadedStructType, ip: *const InternPool) *Tag.TypeStructPacked.Flags { - assert(s.layout == .@"packed"); - const extra = ip.getLocalShared(s.tid).extra.acquire(); - const flags_field_index = std.meta.fieldIndex(Tag.TypeStructPacked, "flags").?; - return @ptrCast(&extra.view().items(.@"0")[s.extra_index + flags_field_index]); - } + int_tag_mode: BackingTypeMode, + nonexhaustive: bool, - pub fn packedFlagsUnordered(s: LoadedStructType, ip: *const InternPool) Tag.TypeStructPacked.Flags { - return @atomicLoad(Tag.TypeStructPacked.Flags, s.packedFlagsPtr(ip), .unordered); - } + /// Initially `false`, and set to `true` once any dependency on or reference to the struct's + /// layout is encountered, after which it is never reset to `false`, even across incremental + /// updates. + /// + /// This field is purely an optimization to avoid resolving the layout of types whose layouts + /// are never demanded. If this field is `true` but the layout is not actually needed, the + /// compiler frontend resolves this by traversing the reference graph at the end of each update + /// with `Zcu.resolveReferences` and hiding compile errors which arise from this analysis. + want_layout: bool, - /// Reads the non-opv flag calculated during AstGen. Used to short-circuit more - /// complicated logic. - pub fn knownNonOpv(s: LoadedStructType, ip: *const InternPool) bool { - return switch (s.layout) { - .@"packed" => false, - .auto, .@"extern" => s.flagsUnordered(ip).known_non_opv, - }; - } + // The remaining fields are only valid once the enum's layout is resolved. + int_tag_type: Index, + field_name_map: MapIndex, + field_names: NullTerminatedString.Slice, + field_value_map: OptionalMapIndex, + field_values: Index.Slice, - pub fn requiresComptime(s: LoadedStructType, ip: *const InternPool) RequiresComptime { - return s.flagsUnordered(ip).requires_comptime; + /// Look up field index based on field name. + pub fn nameIndex(e: LoadedEnumType, ip: *const InternPool, name: NullTerminatedString) ?u32 { + const map = e.field_name_map.get(ip); + const adapter: NullTerminatedString.Adapter = .{ .strings = e.field_names.get(ip) }; + const field_index = map.getIndexAdapted(name, adapter) orelse return null; + return @intCast(field_index); } - pub fn setRequiresComptimeWip(s: LoadedStructType, ip: *InternPool, io: Io) RequiresComptime { - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = s.flagsPtr(ip); - var flags = flags_ptr.*; - defer if (flags.requires_comptime == .unknown) { - flags.requires_comptime = .wip; - @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release); + /// Look up field index based on integer tag value. + /// Asserts that the type of `tag_val` is `enum_obj.int_tag_type`. + /// Asserts that `tag_val` is not `undefined`. + pub fn tagValueIndex(e: LoadedEnumType, ip: *const InternPool, tag_val: Index) ?u32 { + assert(ip.typeOf(tag_val) == e.int_tag_type); + assert(ip.indexToKey(tag_val) == .int); + if (e.field_value_map.unwrap()) |field_value_map| { + const map = field_value_map.get(ip); + const adapter: Index.Adapter = .{ .indexes = e.field_values.get(ip) }; + const field_index = map.getIndexAdapted(tag_val, adapter) orelse return null; + return @intCast(field_index); + } + // Auto-numbered enum, so convert `tag_val` to field index + const field_index = switch (ip.indexToKey(tag_val).int.storage) { + inline .u64, .i64 => |x| std.math.cast(u32, x) orelse return null, + .big_int => |x| x.toInt(u32) catch return null, }; - return flags.requires_comptime; + return if (field_index < e.field_names.len) field_index else null; } +}; - pub fn setRequiresComptime(s: LoadedStructType, ip: *InternPool, io: Io, requires_comptime: RequiresComptime) void { - assert(requires_comptime != .wip); // see setRequiresComptimeWip +pub const LoadedOpaqueType = struct { + /// Index of the `opaque_decl` instruction. + zir_index: TrackedInst.Index, + captures: CaptureValue.Slice, - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = s.flagsPtr(ip); - var flags = flags_ptr.*; - flags.requires_comptime = requires_comptime; - @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release); - } - - pub fn assumeRuntimeBitsIfFieldTypesWip(s: LoadedStructType, ip: *InternPool, io: Io) bool { - if (s.layout == .@"packed") return false; - - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = s.flagsPtr(ip); - var flags = flags_ptr.*; - defer if (flags.field_types_wip) { - flags.assumed_runtime_bits = true; - @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release); - }; - return flags.field_types_wip; - } - - pub fn setFieldTypesWip(s: LoadedStructType, ip: *InternPool, io: Io) bool { - if (s.layout == .@"packed") return false; - - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = s.flagsPtr(ip); - var flags = flags_ptr.*; - defer { - flags.field_types_wip = true; - @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release); - } - return flags.field_types_wip; - } - - pub fn clearFieldTypesWip(s: LoadedStructType, ip: *InternPool, io: Io) void { - if (s.layout == .@"packed") return; - - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = s.flagsPtr(ip); - var flags = flags_ptr.*; - flags.field_types_wip = false; - @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release); - } - - pub fn setLayoutWip(s: LoadedStructType, ip: *InternPool, io: Io) bool { - if (s.layout == .@"packed") return false; - - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = s.flagsPtr(ip); - var flags = flags_ptr.*; - defer { - flags.layout_wip = true; - @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release); - } - return flags.layout_wip; - } - - pub fn clearLayoutWip(s: LoadedStructType, ip: *InternPool, io: Io) void { - if (s.layout == .@"packed") return; - - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = s.flagsPtr(ip); - var flags = flags_ptr.*; - flags.layout_wip = false; - @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release); - } - - pub fn setAlignment(s: LoadedStructType, ip: *InternPool, io: Io, alignment: Alignment) void { - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = s.flagsPtr(ip); - var flags = flags_ptr.*; - flags.alignment = alignment; - @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release); - } - - pub fn assumePointerAlignedIfFieldTypesWip(s: LoadedStructType, ip: *InternPool, io: Io, ptr_align: Alignment) bool { - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = s.flagsPtr(ip); - var flags = flags_ptr.*; - defer if (flags.field_types_wip) { - flags.alignment = ptr_align; - flags.assumed_pointer_aligned = true; - @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release); - }; - return flags.field_types_wip; - } - - pub fn assumePointerAlignedIfWip(s: LoadedStructType, ip: *InternPool, io: Io, ptr_align: Alignment) bool { - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = s.flagsPtr(ip); - var flags = flags_ptr.*; - defer { - if (flags.alignment_wip) { - flags.alignment = ptr_align; - flags.assumed_pointer_aligned = true; - } else flags.alignment_wip = true; - @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release); - } - return flags.alignment_wip; - } - - pub fn clearAlignmentWip(s: LoadedStructType, ip: *InternPool, io: Io) void { - if (s.layout == .@"packed") return; - - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = s.flagsPtr(ip); - var flags = flags_ptr.*; - flags.alignment_wip = false; - @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release); - } - - pub fn setInitsWip(s: LoadedStructType, ip: *InternPool, io: Io) bool { - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - switch (s.layout) { - .@"packed" => { - const flags_ptr = s.packedFlagsPtr(ip); - var flags = flags_ptr.*; - defer { - flags.field_inits_wip = true; - @atomicStore(Tag.TypeStructPacked.Flags, flags_ptr, flags, .release); - } - return flags.field_inits_wip; - }, - .auto, .@"extern" => { - const flags_ptr = s.flagsPtr(ip); - var flags = flags_ptr.*; - defer { - flags.field_inits_wip = true; - @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release); - } - return flags.field_inits_wip; - }, - } - } - - pub fn clearInitsWip(s: LoadedStructType, ip: *InternPool, io: Io) void { - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - switch (s.layout) { - .@"packed" => { - const flags_ptr = s.packedFlagsPtr(ip); - var flags = flags_ptr.*; - flags.field_inits_wip = false; - @atomicStore(Tag.TypeStructPacked.Flags, flags_ptr, flags, .release); - }, - .auto, .@"extern" => { - const flags_ptr = s.flagsPtr(ip); - var flags = flags_ptr.*; - flags.field_inits_wip = false; - @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release); - }, - } - } - - pub fn setFullyResolved(s: LoadedStructType, ip: *InternPool, io: Io) bool { - if (s.layout == .@"packed") return true; - - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = s.flagsPtr(ip); - var flags = flags_ptr.*; - defer { - flags.fully_resolved = true; - @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release); - } - return flags.fully_resolved; - } - - pub fn clearFullyResolved(s: LoadedStructType, ip: *InternPool, io: Io) void { - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - const flags_ptr = s.flagsPtr(ip); - var flags = flags_ptr.*; - flags.fully_resolved = false; - @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release); - } - - /// The returned pointer expires with any addition to the `InternPool`. - /// Asserts the struct is not packed. - fn sizePtr(s: LoadedStructType, ip: *const InternPool) *u32 { - assert(s.layout != .@"packed"); - const extra = ip.getLocalShared(s.tid).extra.acquire(); - const size_field_index = std.meta.fieldIndex(Tag.TypeStruct, "size").?; - return @ptrCast(&extra.view().items(.@"0")[s.extra_index + size_field_index]); - } - - pub fn sizeUnordered(s: LoadedStructType, ip: *const InternPool) u32 { - return @atomicLoad(u32, s.sizePtr(ip), .unordered); - } - - /// The backing integer type of the packed struct. Whether zig chooses - /// this type or the user specifies it, it is stored here. This will be - /// set to `none` until the layout is resolved. - /// Asserts the struct is packed. - fn backingIntTypePtr(s: LoadedStructType, ip: *const InternPool) *Index { - assert(s.layout == .@"packed"); - const extra = ip.getLocalShared(s.tid).extra.acquire(); - const field_index = std.meta.fieldIndex(Tag.TypeStructPacked, "backing_int_ty").?; - return @ptrCast(&extra.view().items(.@"0")[s.extra_index + field_index]); - } - - pub fn backingIntTypeUnordered(s: LoadedStructType, ip: *const InternPool) Index { - return @atomicLoad(Index, s.backingIntTypePtr(ip), .unordered); - } - - pub fn setBackingIntType(s: LoadedStructType, ip: *InternPool, io: Io, backing_int_ty: Index) void { - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - @atomicStore(Index, s.backingIntTypePtr(ip), backing_int_ty, .release); - } - - /// Asserts the struct is not packed. - pub fn setZirIndex(s: LoadedStructType, ip: *InternPool, new_zir_index: TrackedInst.Index.Optional) void { - assert(s.layout != .@"packed"); - const field_index = std.meta.fieldIndex(Tag.TypeStruct, "zir_index").?; - ip.extra_.items[s.extra_index + field_index] = @intFromEnum(new_zir_index); - } - - pub fn haveFieldTypes(s: LoadedStructType, ip: *const InternPool) bool { - const types = s.field_types.get(ip); - return types.len == 0 or types[types.len - 1] != .none; - } - - pub fn haveFieldInits(s: LoadedStructType, ip: *const InternPool) bool { - return switch (s.layout) { - .@"packed" => s.packedFlagsUnordered(ip).inits_resolved, - .auto, .@"extern" => s.flagsUnordered(ip).inits_resolved, - }; - } - - pub fn setHaveFieldInits(s: LoadedStructType, ip: *InternPool, io: Io) void { - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - switch (s.layout) { - .@"packed" => { - const flags_ptr = s.packedFlagsPtr(ip); - var flags = flags_ptr.*; - flags.inits_resolved = true; - @atomicStore(Tag.TypeStructPacked.Flags, flags_ptr, flags, .release); - }, - .auto, .@"extern" => { - const flags_ptr = s.flagsPtr(ip); - var flags = flags_ptr.*; - flags.inits_resolved = true; - @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release); - }, - } - } - - pub fn haveLayout(s: LoadedStructType, ip: *const InternPool) bool { - return switch (s.layout) { - .@"packed" => s.backingIntTypeUnordered(ip) != .none, - .auto, .@"extern" => s.flagsUnordered(ip).layout_resolved, - }; - } - - pub fn setLayoutResolved(s: LoadedStructType, ip: *InternPool, io: Io, size: u32, alignment: Alignment) void { - const extra_mutex = &ip.getLocal(s.tid).mutate.extra.mutex; - extra_mutex.lockUncancelable(io); - defer extra_mutex.unlock(io); - - @atomicStore(u32, s.sizePtr(ip), size, .unordered); - const flags_ptr = s.flagsPtr(ip); - var flags = flags_ptr.*; - flags.alignment = alignment; - flags.layout_resolved = true; - @atomicStore(Tag.TypeStruct.Flags, flags_ptr, flags, .release); - } - - pub fn hasReorderedFields(s: LoadedStructType) bool { - return s.layout == .auto; - } - - pub const RuntimeOrderIterator = struct { - ip: *InternPool, - field_index: u32, - struct_type: InternPool.LoadedStructType, - - pub fn next(it: *@This()) ?u32 { - var i = it.field_index; - - if (i >= it.struct_type.field_types.len) - return null; - - if (it.struct_type.hasReorderedFields()) { - it.field_index += 1; - return it.struct_type.runtime_order.get(it.ip)[i].toInt(); - } - - while (it.struct_type.fieldIsComptime(it.ip, i)) { - i += 1; - if (i >= it.struct_type.field_types.len) - return null; - } - - it.field_index = i + 1; - return i; - } - }; - - /// Iterates over non-comptime fields in the order they are laid out in memory at runtime. - /// May or may not include zero-bit fields. - /// Asserts the struct is not packed. - pub fn iterateRuntimeOrder(s: LoadedStructType, ip: *InternPool) RuntimeOrderIterator { - assert(s.layout != .@"packed"); - return .{ - .ip = ip, - .field_index = 0, - .struct_type = s, - }; - } - - pub const ReverseRuntimeOrderIterator = struct { - ip: *InternPool, - last_index: u32, - struct_type: InternPool.LoadedStructType, - - pub fn next(it: *@This()) ?u32 { - if (it.last_index == 0) - return null; - - if (it.struct_type.hasReorderedFields()) { - it.last_index -= 1; - const order = it.struct_type.runtime_order.get(it.ip); - while (order[it.last_index] == .omitted) { - it.last_index -= 1; - if (it.last_index == 0) - return null; - } - return order[it.last_index].toInt(); - } - - it.last_index -= 1; - while (it.struct_type.fieldIsComptime(it.ip, it.last_index)) { - it.last_index -= 1; - if (it.last_index == 0) - return null; - } - - return it.last_index; - } - }; - - pub fn iterateRuntimeOrderReverse(s: LoadedStructType, ip: *InternPool) ReverseRuntimeOrderIterator { - assert(s.layout != .@"packed"); - return .{ - .ip = ip, - .last_index = s.field_types.len, - .struct_type = s, - }; - } -}; + // TODO: the non-fqn will be needed by the new dwarf structure + /// The name of this opaque type. + name: NullTerminatedString, + /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after. + /// Otherwise, this is `.none`. + name_nav: Nav.Index.Optional, + namespace: NamespaceIndex, +}; pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { const unwrapped_index = index.unwrap(ip); const extra_list = unwrapped_index.getExtra(ip); const extra_items = extra_list.view().items(.@"0"); const item = unwrapped_index.getItem(ip); - switch (item.tag) { + // Exiting this `switch` means this is a `packed struct`. + const backing_mode: BackingTypeMode, const any_defaults: bool = switch (item.tag) { + .type_struct_packed_auto => .{ .auto, false }, + .type_struct_packed_explicit => .{ .explicit, false }, + .type_struct_packed_auto_defaults => .{ .auto, true }, + .type_struct_packed_explicit_defaults => .{ .explicit, true }, .type_struct => { - const name: NullTerminatedString = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "name").?]); - const name_nav: Nav.Index.Optional = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "name_nav").?]); - const namespace: NamespaceIndex = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "namespace").?]); - const zir_index: TrackedInst.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "zir_index").?]); - const fields_len = extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "fields_len").?]; - const flags: Tag.TypeStruct.Flags = @bitCast(@atomicLoad(u32, &extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "flags").?], .unordered)); - var extra_index = item.data + @as(u32, @typeInfo(Tag.TypeStruct).@"struct".fields.len); - const captures_len = if (flags.any_captures) c: { - const len = extra_list.view().items(.@"0")[extra_index]; - extra_index += 1; - break :c len; - } else 0; - const captures: CaptureValue.Slice = .{ + const extra = extraDataTrail(extra_list, Tag.TypeStruct, item.data); + var extra_index = extra.end; + const captures: CaptureValue.Slice = switch (extra.data.flags.any_captures) { + .reified => captures: { + extra_index += 2; // type_hash: PackedU64 + break :captures .empty; + }, + .false => .empty, + .true => captures: { + const len = extra_items[extra_index]; + extra_index += 1; + break :captures .{ + .tid = unwrapped_index.tid, + .start = extra_index, + .len = len, + }; + }, + }; + extra_index += captures.len; + const field_names: NullTerminatedString.Slice = .{ .tid = unwrapped_index.tid, .start = extra_index, - .len = captures_len, + .len = extra.data.fields_len, }; - extra_index += captures_len; - if (flags.is_reified) { - extra_index += 2; // type_hash: PackedU64 - } + extra_index += field_names.len; const field_types: Index.Slice = .{ .tid = unwrapped_index.tid, .start = extra_index, - .len = fields_len, + .len = extra.data.fields_len, }; - extra_index += fields_len; - const names_map: OptionalMapIndex, const names = n: { - const names_map: OptionalMapIndex = @enumFromInt(extra_list.view().items(.@"0")[extra_index]); - extra_index += 1; - const names: NullTerminatedString.Slice = .{ - .tid = unwrapped_index.tid, - .start = extra_index, - .len = fields_len, - }; - extra_index += fields_len; - break :n .{ names_map, names }; - }; - const inits: Index.Slice = if (flags.any_default_inits) i: { - const inits: Index.Slice = .{ - .tid = unwrapped_index.tid, - .start = extra_index, - .len = fields_len, - }; - extra_index += fields_len; - break :i inits; - } else Index.Slice.empty; - const aligns: Alignment.Slice = if (flags.any_aligned_fields) a: { - const a: Alignment.Slice = .{ - .tid = unwrapped_index.tid, - .start = extra_index, - .len = fields_len, - }; - extra_index += std.math.divCeil(u32, fields_len, 4) catch unreachable; - break :a a; - } else Alignment.Slice.empty; - const comptime_bits: LoadedStructType.ComptimeBits = if (flags.any_comptime_fields) c: { - const len = std.math.divCeil(u32, fields_len, 32) catch unreachable; - const c: LoadedStructType.ComptimeBits = .{ - .tid = unwrapped_index.tid, - .start = extra_index, - .len = len, - }; - extra_index += len; - break :c c; - } else LoadedStructType.ComptimeBits.empty; - const runtime_order: LoadedStructType.RuntimeOrder.Slice = if (!flags.is_extern) ro: { - const ro: LoadedStructType.RuntimeOrder.Slice = .{ - .tid = unwrapped_index.tid, - .start = extra_index, - .len = fields_len, - }; - extra_index += fields_len; - break :ro ro; - } else LoadedStructType.RuntimeOrder.Slice.empty; - const offsets: LoadedStructType.Offsets = o: { - const o: LoadedStructType.Offsets = .{ - .tid = unwrapped_index.tid, - .start = extra_index, - .len = fields_len, - }; - extra_index += fields_len; - break :o o; - }; - return .{ + extra_index += field_types.len; + const field_defaults: Index.Slice = if (extra.data.flags.any_field_defaults) .{ .tid = unwrapped_index.tid, - .extra_index = item.data, - .name = name, - .name_nav = name_nav, - .namespace = namespace, - .zir_index = zir_index, - .layout = if (flags.is_extern) .@"extern" else .auto, - .field_names = names, - .field_types = field_types, - .field_inits = inits, - .field_aligns = aligns, - .runtime_order = runtime_order, - .comptime_bits = comptime_bits, - .offsets = offsets, - .names_map = names_map, - .captures = captures, - }; - }, - .type_struct_packed, .type_struct_packed_inits => { - const name: NullTerminatedString = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "name").?]); - const name_nav: Nav.Index.Optional = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "name_nav").?]); - const zir_index: TrackedInst.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "zir_index").?]); - const fields_len = extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "fields_len").?]; - const namespace: NamespaceIndex = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?]); - const names_map: MapIndex = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "names_map").?]); - const flags: Tag.TypeStructPacked.Flags = @bitCast(@atomicLoad(u32, &extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "flags").?], .unordered)); - var extra_index = item.data + @as(u32, @typeInfo(Tag.TypeStructPacked).@"struct".fields.len); - const has_inits = item.tag == .type_struct_packed_inits; - const captures_len = if (flags.any_captures) c: { - const len = extra_list.view().items(.@"0")[extra_index]; - extra_index += 1; - break :c len; - } else 0; - const captures: CaptureValue.Slice = .{ + .start = extra_index, + .len = extra.data.fields_len, + } else .empty; + extra_index += field_defaults.len; + const field_aligns: Alignment.Slice = if (extra.data.flags.any_field_aligns) .{ .tid = unwrapped_index.tid, .start = extra_index, - .len = captures_len, - }; - extra_index += captures_len; - if (flags.is_reified) { - extra_index += 2; // PackedU64 - } - const field_types: Index.Slice = .{ + .len = extra.data.fields_len, + } else .empty; + extra_index += std.math.divCeil(u32, field_aligns.len, 4) catch unreachable; + const field_is_comptime_bits: LoadedStructType.ComptimeBits = if (extra.data.flags.any_comptime_fields) .{ .tid = unwrapped_index.tid, .start = extra_index, - .len = fields_len, - }; - extra_index += fields_len; - const field_names: NullTerminatedString.Slice = .{ + .len = std.math.divCeil(u32, extra.data.fields_len, 32) catch unreachable, + } else .empty; + extra_index += field_is_comptime_bits.len; + const field_runtime_order: LoadedStructType.RuntimeOrder.Slice = if (extra.data.flags.layout == .auto) .{ .tid = unwrapped_index.tid, .start = extra_index, - .len = fields_len, + .len = extra.data.fields_len, + } else .empty; + extra_index += field_runtime_order.len; + const field_offsets: LoadedStructType.Offsets = .{ + .tid = unwrapped_index.tid, + .start = extra_index, + .len = extra.data.fields_len, }; - extra_index += fields_len; - const field_inits: Index.Slice = if (has_inits) inits: { - const i: Index.Slice = .{ - .tid = unwrapped_index.tid, - .start = extra_index, - .len = fields_len, - }; - extra_index += fields_len; - break :inits i; - } else Index.Slice.empty; + extra_index += field_offsets.len; + return .{ - .tid = unwrapped_index.tid, - .extra_index = item.data, - .name = name, - .name_nav = name_nav, - .namespace = namespace, - .zir_index = zir_index, - .layout = .@"packed", + .zir_index = extra.data.zir_index, + .captures = captures, + .is_reified = extra.data.flags.any_captures == .reified, + .name = extra.data.name, + .name_nav = extra.data.name_nav, + .namespace = extra.data.namespace, + .layout = switch (extra.data.flags.layout) { + .auto => .auto, + .@"extern" => .@"extern", + }, + .packed_backing_mode = undefined, + + .want_layout = extra.data.flags.want_layout, + + .field_name_map = extra.data.field_name_map, .field_names = field_names, .field_types = field_types, - .field_inits = field_inits, - .field_aligns = Alignment.Slice.empty, - .runtime_order = LoadedStructType.RuntimeOrder.Slice.empty, - .comptime_bits = LoadedStructType.ComptimeBits.empty, - .offsets = LoadedStructType.Offsets.empty, - .names_map = names_map.toOptional(), - .captures = captures, + .field_defaults = field_defaults, + .field_aligns = field_aligns, + .field_is_comptime_bits = field_is_comptime_bits, + .field_runtime_order = field_runtime_order, + .field_offsets = field_offsets, + .packed_backing_int_type = .none, + .class = extra.data.flags.class, + .size = extra.data.size, + .alignment = extra.data.flags.alignment, }; }, else => unreachable, - } -} - -pub const LoadedEnumType = struct { - // TODO: the non-fqn will be needed by the new dwarf structure - /// The name of this enum type. - name: NullTerminatedString, - /// Represents the declarations inside this enum. - namespace: NamespaceIndex, - /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after. - /// Otherwise, this is `.none`. - name_nav: Nav.Index.Optional, - /// An integer type which is used for the numerical value of the enum. - /// This field is present regardless of whether the enum has an - /// explicitly provided tag type or auto-numbered. - tag_ty: Index, - /// Set of field names in declaration order. - names: NullTerminatedString.Slice, - /// Maps integer tag value to field index. - /// Entries are in declaration order, same as `fields`. - /// If this is empty, it means the enum tags are auto-numbered. - values: Index.Slice, - tag_mode: TagMode, - names_map: MapIndex, - /// This is guaranteed to not be `.none` if explicit values are provided. - values_map: OptionalMapIndex, - /// This is `none` only if this is a generated tag type. - zir_index: TrackedInst.Index.Optional, - captures: CaptureValue.Slice, - - pub const TagMode = enum { - /// The integer tag type was auto-numbered by zig. - auto, - /// The integer tag type was provided by the enum declaration, and the enum - /// is exhaustive. - explicit, - /// The integer tag type was provided by the enum declaration, and the enum - /// is non-exhaustive. - nonexhaustive, }; + const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, item.data); + var extra_index = extra.end; + const captures: CaptureValue.Slice = switch (extra.data.bits.captures_len) { + .reified => captures: { + extra_index += 2; // type_hash: PackedU64 + break :captures .empty; + }, + _ => |n| .{ + .tid = unwrapped_index.tid, + .start = extra_index, + .len = @intFromEnum(n), + }, + }; + extra_index += captures.len; + const field_names: NullTerminatedString.Slice = .{ + .tid = unwrapped_index.tid, + .start = extra_index, + .len = extra.data.fields_len, + }; + extra_index += field_names.len; + const field_types: Index.Slice = .{ + .tid = unwrapped_index.tid, + .start = extra_index, + .len = extra.data.fields_len, + }; + extra_index += field_types.len; + const field_defaults: Index.Slice = if (any_defaults) .{ + .tid = unwrapped_index.tid, + .start = extra_index, + .len = extra.data.fields_len, + } else .empty; + extra_index += field_defaults.len; + return .{ + .zir_index = extra.data.zir_index, + .captures = captures, + .is_reified = extra.data.bits.captures_len == .reified, + .name = extra.data.name, + .name_nav = extra.data.name_nav, + .namespace = extra.data.namespace, + .layout = .@"packed", + .packed_backing_mode = backing_mode, - /// Look up field index based on field name. - pub fn nameIndex(self: LoadedEnumType, ip: *const InternPool, name: NullTerminatedString) ?u32 { - const map = self.names_map.get(ip); - const adapter: NullTerminatedString.Adapter = .{ .strings = self.names.get(ip) }; - const field_index = map.getIndexAdapted(name, adapter) orelse return null; - return @intCast(field_index); - } + .want_layout = extra.data.bits.want_layout, - /// Look up field index based on tag value. - /// Asserts that `values_map` is not `none`. - /// This function returns `null` when `tag_val` does not have the - /// integer tag type of the enum. - pub fn tagValueIndex(self: LoadedEnumType, ip: *const InternPool, tag_val: Index) ?u32 { - assert(tag_val != .none); - // TODO: we should probably decide a single interface for this function, but currently - // it's being called with both tag values and underlying ints. Fix this! - const int_tag_val = switch (ip.indexToKey(tag_val)) { - .enum_tag => |enum_tag| enum_tag.int, - .int => tag_val, - else => unreachable, - }; - if (self.values_map.unwrap()) |values_map| { - const map = values_map.get(ip); - const adapter: Index.Adapter = .{ .indexes = self.values.get(ip) }; - const field_index = map.getIndexAdapted(int_tag_val, adapter) orelse return null; - return @intCast(field_index); - } - // Auto-numbered enum. Convert `int_tag_val` to field index. - const field_index = switch (ip.indexToKey(int_tag_val).int.storage) { - inline .u64, .i64 => |x| std.math.cast(u32, x) orelse return null, - .big_int => |x| x.toInt(u32) catch return null, - .lazy_align, .lazy_size => unreachable, - }; - return if (field_index < self.names.len) field_index else null; - } -}; + .field_name_map = extra.data.field_name_map, + .field_names = field_names, + .field_types = field_types, + .field_defaults = field_defaults, + .field_aligns = .empty, + .field_is_comptime_bits = .empty, + .field_runtime_order = .empty, + .field_offsets = .empty, + .packed_backing_int_type = extra.data.backing_int_type, + .class = undefined, + .size = undefined, + .alignment = undefined, + }; +} -pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType { +pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType { const unwrapped_index = index.unwrap(ip); const extra_list = unwrapped_index.getExtra(ip); + const extra_items = extra_list.view().items(.@"0"); const item = unwrapped_index.getItem(ip); - const tag_mode: LoadedEnumType.TagMode = switch (item.tag) { - .type_enum_auto => { - const extra = extraDataTrail(extra_list, EnumAuto, item.data); - var extra_index: u32 = @intCast(extra.end); - if (extra.data.zir_index == .none) { - extra_index += 1; // owner_union - } - const captures_len = if (extra.data.captures_len == std.math.maxInt(u32)) c: { - extra_index += 2; // type_hash: PackedU64 - break :c 0; - } else extra.data.captures_len; + // Exiting this `switch` means this is a `packed union`. + const backing_mode: BackingTypeMode = switch (item.tag) { + .type_union_packed_auto => .auto, + .type_union_packed_explicit => .explicit, + .type_union => { + const extra = extraDataTrail(extra_list, Tag.TypeUnion, item.data); + var extra_index = extra.end; + const captures: CaptureValue.Slice = switch (extra.data.flags.any_captures) { + .reified => captures: { + extra_index += 2; // type_hash: PackedU64 + break :captures .empty; + }, + .false => .empty, + .true => captures: { + const len = extra_items[extra_index]; + extra_index += 1; + break :captures .{ + .tid = unwrapped_index.tid, + .start = extra_index, + .len = len, + }; + }, + }; + extra_index += captures.len; + const reified_field_names: NullTerminatedString.Slice = if (extra.data.flags.any_captures == .reified) .{ + .tid = unwrapped_index.tid, + .start = extra_index, + .len = extra.data.fields_len, + } else .empty; + extra_index += reified_field_names.len; + const field_types: Index.Slice = .{ + .tid = unwrapped_index.tid, + .start = extra_index, + .len = extra.data.fields_len, + }; + extra_index += field_types.len; + const field_aligns: Alignment.Slice = if (extra.data.flags.any_field_aligns) .{ + .tid = unwrapped_index.tid, + .start = extra_index, + .len = extra.data.fields_len, + } else .empty; + extra_index += std.math.divCeil(u32, field_aligns.len, 4) catch unreachable; + return .{ + .zir_index = extra.data.zir_index, + .captures = captures, + .is_reified = extra.data.flags.any_captures == .reified, .name = extra.data.name, .name_nav = extra.data.name_nav, .namespace = extra.data.namespace, - .tag_ty = extra.data.int_tag_type, - .names = .{ - .tid = unwrapped_index.tid, - .start = extra_index + captures_len, - .len = extra.data.fields_len, - }, - .values = Index.Slice.empty, - .tag_mode = .auto, - .names_map = extra.data.names_map, - .values_map = .none, - .zir_index = extra.data.zir_index, - .captures = .{ - .tid = unwrapped_index.tid, - .start = extra_index, - .len = captures_len, + .layout = switch (extra.data.flags.layout) { + .auto => .auto, + .@"extern" => .@"extern", }, + .tag_usage = extra.data.flags.tag_usage, + .enum_tag_mode = extra.data.flags.enum_tag_mode, + .enum_tag_type = extra.data.enum_tag_type, + .packed_backing_mode = undefined, + .packed_backing_int_type = undefined, + .reified_field_names = reified_field_names, + .want_layout = extra.data.flags.want_layout, + .field_types = field_types, + .field_aligns = field_aligns, + .has_runtime_tag = extra.data.flags.has_runtime_tag, + .class = extra.data.flags.class, + .size = extra.data.size, + .padding = extra.data.padding, + .alignment = extra.data.flags.alignment, }; }, - .type_enum_explicit => .explicit, - .type_enum_nonexhaustive => .nonexhaustive, else => unreachable, }; - const extra = extraDataTrail(extra_list, EnumExplicit, item.data); - var extra_index: u32 = @intCast(extra.end); - if (extra.data.zir_index == .none) { - extra_index += 1; // owner_union - } - const captures_len = if (extra.data.captures_len == std.math.maxInt(u32)) c: { - extra_index += 2; // type_hash: PackedU64 - break :c 0; - } else extra.data.captures_len; - return .{ - .name = extra.data.name, - .name_nav = extra.data.name_nav, - .namespace = extra.data.namespace, - .tag_ty = extra.data.int_tag_type, - .names = .{ - .tid = unwrapped_index.tid, - .start = extra_index + captures_len, - .len = extra.data.fields_len, + const extra = extraDataTrail(extra_list, Tag.TypeUnionPacked, item.data); + var extra_index = extra.end; + const captures: CaptureValue.Slice = switch (extra.data.bits.captures_len) { + .reified => captures: { + extra_index += 2; // type_hash: PackedU64 + break :captures .empty; }, - .values = .{ - .tid = unwrapped_index.tid, - .start = extra_index + captures_len + extra.data.fields_len, - .len = if (extra.data.values_map != .none) extra.data.fields_len else 0, - }, - .tag_mode = tag_mode, - .names_map = extra.data.names_map, - .values_map = extra.data.values_map, - .zir_index = extra.data.zir_index, - .captures = .{ + _ => |n| .{ .tid = unwrapped_index.tid, .start = extra_index, - .len = captures_len, + .len = @intFromEnum(n), }, }; + extra_index += captures.len; + const reified_field_names: NullTerminatedString.Slice = if (extra.data.bits.captures_len == .reified) .{ + .tid = unwrapped_index.tid, + .start = extra_index, + .len = extra.data.fields_len, + } else .empty; + extra_index += reified_field_names.len; + const field_types: Index.Slice = .{ + .tid = unwrapped_index.tid, + .start = extra_index, + .len = extra.data.fields_len, + }; + extra_index += field_types.len; + return .{ + .zir_index = extra.data.zir_index, + .captures = captures, + .is_reified = extra.data.bits.captures_len == .reified, + .name = extra.data.name, + .name_nav = extra.data.name_nav, + .namespace = extra.data.namespace, + .layout = .@"packed", + .tag_usage = .none, + .enum_tag_mode = .auto, + .enum_tag_type = extra.data.enum_tag_type, + .packed_backing_mode = backing_mode, + .packed_backing_int_type = extra.data.backing_int_type, + .reified_field_names = reified_field_names, + .want_layout = extra.data.bits.want_layout, + .field_types = field_types, + .field_aligns = .empty, + .has_runtime_tag = false, + .class = undefined, + .size = undefined, + .padding = undefined, + .alignment = undefined, + }; } -/// Note that this type doubles as the payload for `Tag.type_opaque`. -pub const LoadedOpaqueType = struct { - /// Contains the declarations inside this opaque. - namespace: NamespaceIndex, - // TODO: the non-fqn will be needed by the new dwarf structure - /// The name of this opaque type. - name: NullTerminatedString, - /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after. - /// Otherwise, this is `.none`. - name_nav: Nav.Index.Optional, - /// Index of the `opaque_decl` or `reify` instruction. - zir_index: TrackedInst.Index, - captures: CaptureValue.Slice, -}; +pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType { + const unwrapped_index = index.unwrap(ip); + const extra_list = unwrapped_index.getExtra(ip); + const extra_items = extra_list.view().items(.@"0"); + const item = unwrapped_index.getItem(ip); + const explicit_int_tag: bool, const nonexhaustive: bool = switch (item.tag) { + .type_enum_auto => .{ false, false }, + .type_enum_explicit => .{ true, false }, + .type_enum_nonexhaustive => .{ true, true }, + else => unreachable, + }; + const extra = extraDataTrail(extra_list, Tag.TypeEnum, item.data); + var extra_index: u32 = @intCast(extra.end); + const zir_index: TrackedInst.Index.Optional, const captures: CaptureValue.Slice, const owner_union: Index = switch (extra.data.bits.captures_len) { + .reified => info: { + const zir_index: TrackedInst.Index = @enumFromInt(extra_items[extra_index]); + extra_index += 1; + extra_index += 2; // type_hash: PackedU64 + break :info .{ zir_index.toOptional(), .empty, .none }; + }, + .generated_union_tag => info: { + const owner_union: Index = @enumFromInt(extra_items[extra_index]); + extra_index += 1; + break :info .{ .none, .empty, owner_union }; + }, + _ => |n| info: { + const zir_index: TrackedInst.Index = @enumFromInt(extra_items[extra_index]); + extra_index += 1; + const captures: CaptureValue.Slice = .{ + .tid = unwrapped_index.tid, + .start = extra_index, + .len = @intFromEnum(n), + }; + extra_index += captures.len; + break :info .{ zir_index.toOptional(), captures, .none }; + }, + }; + const field_value_map: OptionalMapIndex = if (explicit_int_tag) m: { + const map: MapIndex = @enumFromInt(extra_items[extra_index]); + extra_index += 1; + break :m map.toOptional(); + } else .none; + const field_names: NullTerminatedString.Slice = .{ + .tid = unwrapped_index.tid, + .start = extra_index, + .len = extra.data.fields_len, + }; + extra_index += field_names.len; + const field_values: Index.Slice = if (explicit_int_tag) .{ + .tid = unwrapped_index.tid, + .start = extra_index, + .len = extra.data.fields_len, + } else .empty; + extra_index += field_values.len; + return .{ + .zir_index = zir_index, + .captures = captures, + .is_reified = extra.data.bits.captures_len == .reified, + .owner_union = owner_union, + .name = extra.data.name, + .name_nav = extra.data.name_nav, + .namespace = extra.data.namespace, + .int_tag_type = extra.data.int_tag_type, + .int_tag_mode = if (explicit_int_tag) .explicit else .auto, + .nonexhaustive = nonexhaustive, + .want_layout = extra.data.bits.want_layout, + .field_name_map = extra.data.field_name_map, + .field_value_map = field_value_map, + .field_names = field_names, + .field_values = field_values, + }; +} pub fn loadOpaqueType(ip: *const InternPool, index: Index) LoadedOpaqueType { const unwrapped_index = index.unwrap(ip); const item = unwrapped_index.getItem(ip); assert(item.tag == .type_opaque); const extra = extraDataTrail(unwrapped_index.getExtra(ip), Tag.TypeOpaque, item.data); - const captures_len = if (extra.data.captures_len == std.math.maxInt(u32)) - 0 - else - extra.data.captures_len; return .{ - .name = extra.data.name, - .name_nav = extra.data.name_nav, - .namespace = extra.data.namespace, .zir_index = extra.data.zir_index, .captures = .{ .tid = unwrapped_index.tid, .start = extra.end, - .len = captures_len, + .len = extra.data.captures_len, }, + .name = extra.data.name, + .name_nav = extra.data.name_nav, + .namespace = extra.data.namespace, }; } @@ -4816,6 +4210,13 @@ pub const Index = enum(u32) { const extra = ip.getLocalShared(slice.tid).extra.acquire(); return @ptrCast(extra.view().items(.@"0")[slice.start..][0..slice.len]); } + + /// If `slice` is empty (`slice.len == 0`), returns `.none`. + /// Otherwise, asserts that `index < slice.len`, and returns the value at `index`. + pub fn getOrNone(slice: Slice, ip: *const InternPool, index: usize) Index { + if (slice.len == 0) return .none; + return slice.get(ip)[index]; + } }; /// Used for a map of `Index` values to the index within a list of `Index` values. @@ -4891,26 +4292,6 @@ pub const Index = enum(u32) { /// Tag to encoding mapping to facilitate fancy debug printing for this type. fn dbHelper(self: *Index, tag_to_encoding_map: *struct { const DataIsIndex = struct { data: Index }; - const DataIsExtraIndexOfEnumExplicit = struct { - const @"data.fields_len" = opaque {}; - data: *EnumExplicit, - @"trailing.names.len": *@"data.fields_len", - @"trailing.values.len": *@"data.fields_len", - trailing: struct { - names: []NullTerminatedString, - values: []Index, - }, - }; - const DataIsExtraIndexOfTypeTuple = struct { - const @"data.fields_len" = opaque {}; - data: *TypeTuple, - @"trailing.types.len": *@"data.fields_len", - @"trailing.values.len": *@"data.fields_len", - trailing: struct { - types: []Index, - values: []Index, - }, - }; removed: void, type_int_signed: struct { data: u32 }, @@ -4931,31 +4312,40 @@ pub const Index = enum(u32) { trailing: struct { names: []NullTerminatedString }, }, type_inferred_error_set: DataIsIndex, - type_enum_auto: struct { + simple_type: void, + type_function: struct { + const @"data.flags.has_comptime_bits" = opaque {}; + const @"data.flags.has_noalias_bits" = opaque {}; + const @"data.params_len" = opaque {}; + data: *Tag.TypeFunction, + @"trailing.comptime_bits.len": *@"data.flags.has_comptime_bits", + @"trailing.noalias_bits.len": *@"data.flags.has_noalias_bits", + @"trailing.param_types.len": *@"data.params_len", + trailing: struct { comptime_bits: []u32, noalias_bits: []u32, param_types: []Index }, + }, + type_tuple: struct { const @"data.fields_len" = opaque {}; - data: *EnumAuto, - @"trailing.names.len": *@"data.fields_len", - trailing: struct { names: []NullTerminatedString }, + data: *TypeTuple, + @"trailing.types.len": *@"data.fields_len", + @"trailing.values.len": *@"data.fields_len", + trailing: struct { + types: []Index, + values: []Index, + }, }, - type_enum_explicit: DataIsExtraIndexOfEnumExplicit, - type_enum_nonexhaustive: DataIsExtraIndexOfEnumExplicit, - simple_type: void, - type_opaque: struct { data: *Tag.TypeOpaque }, + type_struct: struct { data: *Tag.TypeStruct }, - type_struct_packed: struct { data: *Tag.TypeStructPacked }, - type_struct_packed_inits: struct { data: *Tag.TypeStructPacked }, - type_tuple: DataIsExtraIndexOfTypeTuple, + type_struct_packed_auto: struct { data: *Tag.TypeStructPacked }, + type_struct_packed_explicit: struct { data: *Tag.TypeStructPacked }, + type_struct_packed_auto_defaults: struct { data: *Tag.TypeStructPacked }, + type_struct_packed_explicit_defaults: struct { data: *Tag.TypeStructPacked }, type_union: struct { data: *Tag.TypeUnion }, - type_function: struct { - const @"data.flags.has_comptime_bits" = opaque {}; - const @"data.flags.has_noalias_bits" = opaque {}; - const @"data.params_len" = opaque {}; - data: *Tag.TypeFunction, - @"trailing.comptime_bits.len": *@"data.flags.has_comptime_bits", - @"trailing.noalias_bits.len": *@"data.flags.has_noalias_bits", - @"trailing.param_types.len": *@"data.params_len", - trailing: struct { comptime_bits: []u32, noalias_bits: []u32, param_types: []Index }, - }, + type_union_packed_auto: struct { data: *Tag.TypeUnionPacked }, + type_union_packed_explicit: struct { data: *Tag.TypeUnionPacked }, + type_enum_auto: struct { data: *Tag.TypeEnum }, + type_enum_explicit: struct { data: *Tag.TypeEnum }, + type_enum_nonexhaustive: struct { data: *Tag.TypeEnum }, + type_opaque: struct { data: *Tag.TypeOpaque }, undef: DataIsIndex, simple_value: void, @@ -4982,8 +4372,6 @@ pub const Index = enum(u32) { int_small: struct { data: *IntSmall }, int_positive: struct { data: u32 }, int_negative: struct { data: u32 }, - int_lazy_align: struct { data: *IntLazy }, - int_lazy_size: struct { data: *IntLazy }, error_set_error: struct { data: *Key.Error }, error_union_error: struct { data: *Key.Error }, error_union_payload: struct { data: *Tag.TypeValue }, @@ -5027,6 +4415,7 @@ pub const Index = enum(u32) { trailing: struct { element_values: []Index }, }, repeated: struct { data: *Repeated }, + bitpack: struct { data: *Key.Bitpack }, memoized_call: struct { const @"data.args_len" = opaque {}; @@ -5037,7 +4426,7 @@ pub const Index = enum(u32) { }) void { _ = self; const map_fields = @typeInfo(@typeInfo(@TypeOf(tag_to_encoding_map)).pointer.child).@"struct".fields; - @setEvalBranchQuota(2_000); + @setEvalBranchQuota(3_000); inline for (@typeInfo(Tag).@"enum".fields, 0..) |tag, start| { inline for (0..map_fields.len) |offset| { if (comptime std.mem.eql(u8, tag.name, map_fields[(start + offset) % map_fields.len].name)) break; @@ -5409,7 +4798,7 @@ pub const static_keys: [static_len]Key = .{ .values = .empty, } }, - .{ .simple_value = .undefined }, + .{ .undef = .undefined_type }, .{ .undef = .bool_type }, .{ .undef = .usize_type }, .{ .undef = .u1_type }, @@ -5469,7 +4858,11 @@ pub const static_keys: [static_len]Key = .{ .{ .simple_value = .null }, .{ .simple_value = .true }, .{ .simple_value = .false }, - .{ .simple_value = .empty_tuple }, + + .{ .aggregate = .{ + .ty = .empty_tuple_type, + .storage = .{ .elems = &.{} }, + } }, }; /// How many items in the InternPool are statically known. @@ -5485,6 +4878,8 @@ pub const Tag = enum(u8) { /// assert not this tag. `data` is unused. removed, + /// A type that can be represented with only an enum tag. + simple_type, /// An integer type. /// data is number of bits type_int_signed, @@ -5524,41 +4919,68 @@ pub const Tag = enum(u8) { /// The inferred error set type of a function. /// data is `Index` of a `func_decl` or `func_instance`. type_inferred_error_set, - /// An enum type with auto-numbered tag values. - /// The enum is exhaustive. - /// data is payload index to `EnumAuto`. - type_enum_auto, - /// An enum type with an explicitly provided integer tag type. - /// The enum is exhaustive. - /// data is payload index to `EnumExplicit`. - type_enum_explicit, - /// An enum type with an explicitly provided integer tag type. - /// The enum is non-exhaustive. - /// data is payload index to `EnumExplicit`. - type_enum_nonexhaustive, - /// A type that can be represented with only an enum tag. - simple_type, - /// An opaque type. - /// data is index of Tag.TypeOpaque in extra. - type_opaque, + /// A function body type. + /// `data` is extra index to `TypeFunction`. + type_function, + /// A `TupleType`. + /// data is extra index of `TypeTuple`. + type_tuple, + /// A non-packed struct type. - /// data is 0 or extra index of `TypeStruct`. + /// data is extra index of `TypeStruct`. type_struct, - /// A packed struct, no fields have any init values. + /// `packed struct { ... }` with no default field values. /// data is extra index of `TypeStructPacked`. - type_struct_packed, - /// A packed struct, one or more fields have init values. + type_struct_packed_auto, + /// `packed struct(T) { ... }` with no default field values. /// data is extra index of `TypeStructPacked`. - type_struct_packed_inits, - /// A `TupleType`. - /// data is extra index of `TypeTuple`. - type_tuple, - /// A union type. - /// `data` is extra index of `TypeUnion`. + type_struct_packed_explicit, + /// `packed struct { ... }` with one or more default field values. + /// data is extra index of `TypeStructPacked`. + type_struct_packed_auto_defaults, + /// `packed struct(T) { ... }` with one or more default field values. + /// data is extra index of `TypeStructPacked`. + type_struct_packed_explicit_defaults, + + /// A non-packed union type. + /// data is extra index of `TypeUnion`. type_union, - /// A function body type. - /// `data` is extra index to `TypeFunction`. - type_function, + /// `packed union { ... }`. + /// data is extra index of `TypeUnionPacked`. + type_union_packed_auto, + /// `packed union(T) { ... }`. + /// data is extra index of `TypeUnionPacked`. + type_union_packed_explicit, + + /// An exhaustive enum type *without* an explicit integer tag type. The tag type is inferred. + /// + /// Because the tag type is inferred, there are no explicit field values. + /// + /// May be the generated tag type for a `union(enum)`. + /// + /// data is extra index of `TypeEnum`. + type_enum_auto, + /// An exhaustive enum type *with* an explicit integer tag type. + /// + /// May have explicit field values. + /// + /// May be the generated tag type for a `union(enum(T))`. + /// + /// data is extra index of `TypeEnum`. + type_enum_explicit, + /// An non-exhaustive enum type (with an explicit integer tag type, since it is required for + /// non-exhaustive enums). + /// + /// May have explicit field values. + /// + /// This is *not* a union's generated tag type, because such types are always exhaustive. + /// + /// data is extra index of `TypeEnum`. + type_enum_nonexhaustive, + + /// An opaque type. + /// data is extra index of `TypeOpaque`. + type_opaque, /// Typed `undefined`. /// `data` is `Index` of the type. @@ -5644,12 +5066,6 @@ pub const Tag = enum(u8) { /// A negative integer value. /// data is a limbs index to `Int`. int_negative, - /// The ABI alignment of a lazy type. - /// data is extra index of `IntLazy`. - int_lazy_align, - /// The ABI size of a lazy type. - /// data is extra index of `IntLazy`. - int_lazy_size, /// An error value. /// data is extra index of `Key.Error`. error_set_error, @@ -5735,6 +5151,9 @@ pub const Tag = enum(u8) { /// An instance of an array or vector with every element being the same value. /// data is extra index to `Repeated`. repeated, + /// An instance of a `packed struct` or `packed union`. + /// data is extra index to `Key.Bitpack`. + bitpack, /// A memoized comptime function call result. /// data is extra index to `MemoizedCall` @@ -5747,24 +5166,77 @@ pub const Tag = enum(u8) { const Union = Key.Union; const TypePointer = Key.PtrType; - const enum_explicit_encoding = .{ + const struct_packed_encoding = .{ + .summary = .@"{.payload.name%summary#\"}", + .payload = TypeStructPacked, + .trailing = struct { + type_hash: ?u64, + captures: ?[]CaptureValue, + field_names: []NullTerminatedString, + field_types: []Index, + }, + .config = .{ + .@"trailing.type_hash.?" = .@"payload.captures_len == .reified", + .@"trailing.captures.?" = .@"payload.captures_len != .reified", + .@"trailing.captures.?.len" = .@"@intFromEnum(payload.captures_len)", + .@"trailing.field_names.len" = .@"payload.fields_len", + .@"trailing.field_types.len" = .@"payload.fields_len", + }, + }; + const struct_packed_defaults_encoding = .{ + .summary = .@"{.payload.name%summary#\"}", + .payload = TypeStructPacked, + .trailing = struct { + type_hash: ?u64, + captures: ?[]CaptureValue, + field_names: []NullTerminatedString, + field_types: []Index, + field_defaults: []Index, + }, + .config = .{ + .@"trailing.type_hash.?" = .@"payload.captures_len == .reified", + .@"trailing.captures.?" = .@"payload.captures_len != .reified", + .@"trailing.captures.?.len" = .@"@intFromEnum(payload.captures_len)", + .@"trailing.field_names.len" = .@"payload.fields_len", + .@"trailing.field_types.len" = .@"payload.fields_len", + .@"trailing.field_defaults.len" = .@"payload.fields_len", + }, + }; + const union_packed_encoding = .{ .summary = .@"{.payload.name%summary#\"}", - .payload = EnumExplicit, + .payload = TypeUnionPacked, .trailing = struct { - owner_union: Index, + type_hash: ?u64, captures: ?[]CaptureValue, + field_types: []Index, + }, + .config = .{ + .@"trailing.type_hash.?" = .@"payload.captures_len == .reified", + .@"trailing.captures.?" = .@"payload.captures_len != .reified", + .@"trailing.captures.?.len" = .@"@intFromEnum(payload.captures_len)", + .@"trailing.field_types.len" = .@"payload.fields_len", + }, + }; + const enum_explicit_encoding = .{ + .summary = .@"{.payload.name%summary#\"}", + .payload = TypeEnum, + .trailing = struct { + owner_union: ?Index, + zir_index: ?TrackedInst.Index, type_hash: ?u64, + captures: ?[]CaptureValue, + field_value_map: MapIndex, field_names: []NullTerminatedString, - tag_values: []Index, + field_values: []Index, }, .config = .{ - .@"trailing.owner_union.?" = .@"payload.zir_index == .none", - .@"trailing.cau.?" = .@"payload.zir_index != .none", - .@"trailing.captures.?" = .@"payload.captures_len < 0xffffffff", - .@"trailing.captures.?.len" = .@"payload.captures_len", - .@"trailing.type_hash.?" = .@"payload.captures_len == 0xffffffff", + .@"trailing.owner_union.?" = .@"payload.captures_len == .generated_union_tag", + .@"trailing.zir_index.?" = .@"payload.captures_len != .generated_union_tag", + .@"trailing.type_hash.?" = .@"payload.captures_len == .reified", + .@"trailing.captures.?" = .@"payload.captures_len != .reified and payload.captures_len != .generated_enum_tag", + .@"trailing.captures.?.len" = .@"@intFromEnum(payload.captures_len)", .@"trailing.field_names.len" = .@"payload.fields_len", - .@"trailing.tag_values.len" = .@"payload.fields_len", + .@"trailing.field_values.len" = .@"payload.fields_len", }, }; const encodings = .{ @@ -5792,153 +5264,121 @@ pub const Tag = enum(u8) { .summary = .@"@typeInfo(@typeInfo(@TypeOf({.data%summary})).@\"fn\".return_type.?).error_union.error_set", .data = Index, }, - .type_enum_auto = .{ - .summary = .@"{.payload.name%summary#\"}", - .payload = EnumAuto, + .simple_type = .{ .summary = .@"{.index%value#.}", .index = SimpleType }, + .type_tuple = .{ + .summary = .@"struct {...}", + .payload = TypeTuple, .trailing = struct { - owner_union: ?Index, - captures: ?[]CaptureValue, - type_hash: ?u64, - field_names: []NullTerminatedString, + field_types: []Index, + field_values: []Index, }, .config = .{ - .@"trailing.owner_union.?" = .@"payload.zir_index == .none", - .@"trailing.cau.?" = .@"payload.zir_index != .none", - .@"trailing.captures.?" = .@"payload.captures_len < 0xffffffff", - .@"trailing.captures.?.len" = .@"payload.captures_len", - .@"trailing.type_hash.?" = .@"payload.captures_len == 0xffffffff", - .@"trailing.field_names.len" = .@"payload.fields_len", + .@"trailing.field_types.len" = .@"payload.fields_len", + .@"trailing.field_values.len" = .@"payload.fields_len", }, }, - .type_enum_explicit = enum_explicit_encoding, - .type_enum_nonexhaustive = enum_explicit_encoding, - .simple_type = .{ .summary = .@"{.index%value#.}", .index = SimpleType }, - .type_opaque = .{ - .summary = .@"{.payload.name%summary#\"}", - .payload = TypeOpaque, - .trailing = struct { captures: []CaptureValue }, - .config = .{ .@"trailing.captures.len" = .@"payload.captures_len" }, + .type_function = .{ + .summary = .@"fn (...) ... {.payload.return_type%summary}", + .payload = TypeFunction, + .trailing = struct { + param_comptime_bits: ?[]u32, + param_noalias_bits: ?[]u32, + param_type: []Index, + }, + .config = .{ + .@"trailing.param_comptime_bits.?" = .@"payload.flags.has_comptime_bits", + .@"trailing.param_comptime_bits.?.len" = .@"(payload.params_len + 31) / 32", + .@"trailing.param_noalias_bits.?" = .@"payload.flags.has_noalias_bits", + .@"trailing.param_noalias_bits.?.len" = .@"(payload.params_len + 31) / 32", + .@"trailing.param_type.len" = .@"payload.params_len", + }, }, + .type_struct = .{ .summary = .@"{.payload.name%summary#\"}", .payload = TypeStruct, .trailing = struct { + type_hash: ?u64, captures_len: ?u32, captures: ?[]CaptureValue, - type_hash: ?u64, - field_types: []Index, - field_names_map: OptionalMapIndex, field_names: []NullTerminatedString, - field_inits: ?[]Index, + field_types: []Index, + field_defaults: ?[]Index, field_aligns: ?[]Alignment, field_is_comptime_bits: ?[]u32, - field_index: ?[]LoadedStructType.RuntimeOrder, - field_offset: []u32, + field_runtime_order: ?[]u32, + field_offsets: []u32, }, .config = .{ - .@"trailing.captures_len.?" = .@"payload.flags.any_captures", - .@"trailing.captures.?" = .@"payload.flags.any_captures", + .@"trailing.type_hash.?" = .@"payload.flags.any_captures == .reified", + .@"trailing.captures_len.?" = .@"payload.flags.any_captures == .true", + .@"trailing.captures.?" = .@"payload.flags.any_captures == .true", .@"trailing.captures.?.len" = .@"trailing.captures_len.?", - .@"trailing.type_hash.?" = .@"payload.flags.is_reified", - .@"trailing.field_types.len" = .@"payload.fields_len", .@"trailing.field_names.len" = .@"payload.fields_len", - .@"trailing.field_inits.?" = .@"payload.flags.any_default_inits", - .@"trailing.field_inits.?.len" = .@"payload.fields_len", - .@"trailing.field_aligns.?" = .@"payload.flags.any_aligned_fields", + .@"trailing.field_types.len" = .@"payload.fields_len", + .@"trailing.field_defaults.?" = .@"payload.flags.any_field_defaults", + .@"trailing.field_defaults.?.len" = .@"payload.fields_len", + .@"trailing.field_aligns.?" = .@"payload.flags.any_field_aligns", .@"trailing.field_aligns.?.len" = .@"payload.fields_len", .@"trailing.field_is_comptime_bits.?" = .@"payload.flags.any_comptime_fields", .@"trailing.field_is_comptime_bits.?.len" = .@"(payload.fields_len + 31) / 32", - .@"trailing.field_index.?" = .@"!payload.flags.is_extern", - .@"trailing.field_index.?.len" = .@"payload.fields_len", - .@"trailing.field_offset.len" = .@"payload.fields_len", + .@"trailing.field_runtime_order.?" = .@"payload.flags.layout == .auto", + .@"trailing.field_runtime_order.?.len" = .@"payload.fields_len", + .@"trailing.field_offsets.len" = .@"payload.fields_len", }, }, - .type_struct_packed = .{ + .type_struct_packed_auto = struct_packed_encoding, + .type_struct_packed_explicit = struct_packed_encoding, + .type_struct_packed_auto_defaults = struct_packed_defaults_encoding, + .type_struct_packed_explicit_defaults = struct_packed_defaults_encoding, + .type_union = .{ .summary = .@"{.payload.name%summary#\"}", - .payload = TypeStructPacked, + .payload = TypeUnion, .trailing = struct { + type_hash: ?u64, captures_len: ?u32, captures: ?[]CaptureValue, - type_hash: ?u64, field_types: []Index, - field_names: []NullTerminatedString, + field_aligns: ?[]Alignment, }, .config = .{ - .@"trailing.captures_len.?" = .@"payload.flags.any_captures", - .@"trailing.captures.?" = .@"payload.flags.any_captures", + .@"trailing.type_hash.?" = .@"payload.flags.any_captures == .reified", + .@"trailing.captures_len.?" = .@"payload.flags.any_captures == .true", + .@"trailing.captures.?" = .@"payload.flags.any_captures == .true", .@"trailing.captures.?.len" = .@"trailing.captures_len.?", - .@"trailing.type_hash.?" = .@"payload.is_flags.is_reified", .@"trailing.field_types.len" = .@"payload.fields_len", - .@"trailing.field_names.len" = .@"payload.fields_len", + .@"trailing.field_aligns.?" = .@"payloads.flags.any_field_aligns", + .@"trailing.field_aligns.?.len" = .@"payload.fields_len", }, }, - .type_struct_packed_inits = .{ + .type_union_packed_auto = union_packed_encoding, + .type_union_packed_explicit = union_packed_encoding, + .type_enum_auto = .{ .summary = .@"{.payload.name%summary#\"}", - .payload = TypeStructPacked, + .payload = TypeEnum, .trailing = struct { - captures_len: ?u32, - captures: ?[]CaptureValue, + owner_union: ?Index, + zir_index: ?TrackedInst.Index, type_hash: ?u64, - field_types: []Index, + captures: ?[]CaptureValue, field_names: []NullTerminatedString, - field_inits: []Index, }, .config = .{ - .@"trailing.captures_len.?" = .@"payload.flags.any_captures", - .@"trailing.captures.?" = .@"payload.flags.any_captures", - .@"trailing.captures.?.len" = .@"trailing.captures_len.?", - .@"trailing.type_hash.?" = .@"payload.is_flags.is_reified", - .@"trailing.field_types.len" = .@"payload.fields_len", + .@"trailing.owner_union.?" = .@"payload.captures_len == .generated_union_tag", + .@"trailing.zir_index.?" = .@"payload.captures_len != .generated_union_tag", + .@"trailing.type_hash.?" = .@"payload.captures_len == .reified", + .@"trailing.captures.?" = .@"payload.captures_len != .reified and payload.captures_len != .generated_enum_tag", + .@"trailing.captures.?.len" = .@"@intFromEnum(payload.captures_len)", .@"trailing.field_names.len" = .@"payload.fields_len", - .@"trailing.field_inits.len" = .@"payload.fields_len", - }, - }, - .type_tuple = .{ - .summary = .@"struct {...}", - .payload = TypeTuple, - .trailing = struct { - field_types: []Index, - field_values: []Index, - }, - .config = .{ - .@"trailing.field_types.len" = .@"payload.fields_len", - .@"trailing.field_values.len" = .@"payload.fields_len", }, }, - .type_union = .{ + .type_enum_explicit = enum_explicit_encoding, + .type_enum_nonexhaustive = enum_explicit_encoding, + .type_opaque = .{ .summary = .@"{.payload.name%summary#\"}", - .payload = TypeUnion, - .trailing = struct { - captures_len: ?u32, - captures: ?[]CaptureValue, - type_hash: ?u64, - field_types: []Index, - field_aligns: []Alignment, - }, - .config = .{ - .@"trailing.captures_len.?" = .@"payload.flags.any_captures", - .@"trailing.captures.?" = .@"payload.flags.any_captures", - .@"trailing.captures.?.len" = .@"trailing.captures_len.?", - .@"trailing.type_hash.?" = .@"payload.is_flags.is_reified", - .@"trailing.field_types.len" = .@"payload.fields_len", - .@"trailing.field_aligns.len" = .@"payload.fields_len", - }, - }, - .type_function = .{ - .summary = .@"fn (...) ... {.payload.return_type%summary}", - .payload = TypeFunction, - .trailing = struct { - param_comptime_bits: ?[]u32, - param_noalias_bits: ?[]u32, - param_type: []Index, - }, - .config = .{ - .@"trailing.param_comptime_bits.?" = .@"payload.flags.has_comptime_bits", - .@"trailing.param_comptime_bits.?.len" = .@"(payload.params_len + 31) / 32", - .@"trailing.param_noalias_bits.?" = .@"payload.flags.has_noalias_bits", - .@"trailing.param_noalias_bits.?.len" = .@"(payload.params_len + 31) / 32", - .@"trailing.param_type.len" = .@"payload.params_len", - }, + .payload = TypeOpaque, + .trailing = struct { captures: []CaptureValue }, + .config = .{ .@"trailing.captures.len" = .@"payload.captures_len" }, }, .undef = .{ .summary = .@"@as({.data%summary}, undefined)", .data = Index }, @@ -5999,8 +5439,6 @@ pub const Tag = enum(u8) { .int_small = .{ .summary = .@"@as({.payload.ty%summary}, {.payload.value%value})", .payload = IntSmall }, .int_positive = .{}, .int_negative = .{}, - .int_lazy_align = .{ .summary = .@"@as({.payload.ty%summary}, @alignOf({.payload.lazy_ty%summary}))", .payload = IntLazy }, - .int_lazy_size = .{ .summary = .@"@as({.payload.ty%summary}, @sizeOf({.payload.lazy_ty%summary}))", .payload = IntLazy }, .error_set_error = .{ .summary = .@"@as({.payload.ty%summary}, error.@{.payload.name%summary})", .payload = Error }, .error_union_error = .{ .summary = .@"@as({.payload.ty%summary}, error.@{.payload.name%summary})", .payload = Error }, .error_union_payload = .{ .summary = .@"@as({.payload.ty%summary}, {.payload.val%summary})", .payload = TypeValue }, @@ -6049,6 +5487,7 @@ pub const Tag = enum(u8) { .config = .{ .@"trailing.elements.len" = .@"payload.ty.payload.fields_len" }, }, .repeated = .{ .summary = .@"@as({.payload.ty%summary}, @splat({.payload.elem_val%summary}))", .payload = Repeated }, + .bitpack = .{ .summary = .@"@as({.payload.ty%summary}, {})", .payload = Key.Bitpack }, .memoized_call = .{ .summary = .@"@memoize({.payload.func%summary})", @@ -6141,194 +5580,288 @@ pub const Tag = enum(u8) { generic_owner: Index, }; - pub const FuncCoerced = struct { - ty: Index, - func: Index, - }; + pub const FuncCoerced = struct { + ty: Index, + func: Index, + }; + + /// Trailing: + /// 0. name: NullTerminatedString for each names_len + pub const ErrorSet = struct { + names_len: u32, + /// Maps error names to declaration index. + names_map: MapIndex, + }; + + /// Trailing: + /// 0. comptime_bits: u32, // if has_comptime_bits + /// 1. noalias_bits: u32, // if has_noalias_bits + /// 2. param_type: Index for each params_len + pub const TypeFunction = struct { + params_len: u32, + return_type: Index, + flags: Flags, + + pub const Flags = packed struct(u32) { + cc: PackedCallingConvention, + is_var_args: bool, + has_comptime_bits: bool, + has_noalias_bits: bool, + is_noinline: bool, + _: u10 = 0, + }; + }; + + /// At first I thought of storing the denormalized data externally, such as... + /// + /// * runtime field order + /// * calculated field offsets + /// * size and alignment of the struct + /// + /// ...since these can be computed based on the other data here. However, + /// this data does need to be memoized, and therefore stored in memory + /// while the compiler is running, in order to avoid O(N^2) logic in many + /// places. Since the data can be stored compactly in the InternPool + /// representation, it is better for memory usage to store denormalized data + /// here, and potentially also better for performance as well. It's also simpler + /// than coming up with some other scheme for the data. + /// + /// Trailing: + /// 0. type_hash: PackedU64 // if `any_captures == .reified` + /// 1. captures_len: u32 // if `any_captures == .true` + /// 2. capture: CaptureValue // for each `captures_len` + /// 3. field_name: NullTerminatedString // for each `fields_len` + /// 4. field_type: Index // for each `fields_len` + /// 5. field_default: Index // if `any_field_defaults`; for each `fields_len` + /// 6. field_align: Alignment // if `any_field_aligns`; for each `fields_len` + /// 7. field_is_comptime_bits: u32 // if `any_comptime_fields`; minimum `u32` for `fields_len`; LSB is field 0 + /// 8. field_runtime_order: RuntimeOrder // if `layout == .auto`; for each `fields_len` + /// 9. field_offset: u32 // for each `fields_len` + pub const TypeStruct = struct { + zir_index: TrackedInst.Index, + + name: NullTerminatedString, + name_nav: Nav.Index.Optional, + namespace: NamespaceIndex, + + fields_len: u32, + field_name_map: MapIndex, + + /// Size in bytes of the whole struct. Always 0 until layout resolved. + size: u32, + + flags: Flags, + + pub const Flags = packed struct(u32) { + any_captures: enum(u2) { true, false, reified }, + + /// `packed` layout is represented separately by `TypeStructPacked`. + layout: enum(u1) { auto, @"extern" }, + + any_comptime_fields: bool, + any_field_defaults: bool, + any_field_aligns: bool, + + class: TypeClass, + /// Alignment of the whole struct. Always `.none` until layout resolved. + alignment: Alignment, + + want_layout: bool, - /// Trailing: - /// 0. name: NullTerminatedString for each names_len - pub const ErrorSet = struct { - names_len: u32, - /// Maps error names to declaration index. - names_map: MapIndex, + _: u16 = 0, + }; }; /// Trailing: - /// 0. comptime_bits: u32, // if has_comptime_bits - /// 1. noalias_bits: u32, // if has_noalias_bits - /// 2. param_type: Index for each params_len - pub const TypeFunction = struct { - params_len: u32, - return_type: Index, - flags: Flags, + /// 0. type_hash: PackedU64 // if `captures_len == .reified` + /// 1. capture: CaptureValue // if `captures_len != .reified`; for each `captures_len` + /// 2. field_name: NullTerminatedString // for each `fields_len` + /// 3. field_type: Index // for each `fields_len` + /// 4. field_default: Index // if item tag implies field defaults; for each `fields_len` + pub const TypeStructPacked = struct { + zir_index: TrackedInst.Index, + bits: Bits, - pub const Flags = packed struct(u32) { - cc: PackedCallingConvention, - is_var_args: bool, - is_generic: bool, - has_comptime_bits: bool, - has_noalias_bits: bool, - is_noinline: bool, - _: u9 = 0, + name: NullTerminatedString, + name_nav: Nav.Index.Optional, + namespace: NamespaceIndex, + + /// The corresponding `BackingTypeMode` depends on the item's `Tag`. + backing_int_type: Index, + + fields_len: u32, + field_name_map: MapIndex, + + const Bits = packed struct(u32) { + captures_len: enum(u31) { + reified = std.math.maxInt(u31), + _, + }, + want_layout: bool, }; }; + /// For declared unions, field names are intentionally omitted because they are available in + /// `enum_tag_type`. However, reified unions do store field names, because they are needed by + /// type resolution to create or validate the enum tag type (type resolution for declared unions + /// instead fetches field names from ZIR). + /// /// Trailing: - /// 0. captures_len: u32 // if `any_captures` - /// 1. capture: CaptureValue // for each `captures_len` - /// 2. type_hash: PackedU64 // if `is_reified` - /// 3. field type: Index for each field; declaration order - /// 4. field align: Alignment for each field; declaration order + /// 0. type_hash: PackedU64 // if `any_captures == .reified` + /// 1. captures_len: u32 // if `any_captures == .true` + /// 2. capture: CaptureValue // if `any_captures == .true`; for each `captures_len` + /// 3. reified_field_name: NullTerminatedString // if `any_captures == .reified`; for each `fields_len` + /// 4. field_type: Index // for each `fields_len` + /// 5. field_align: Alignment // for each `fields_len` if `any_field_aligns` pub const TypeUnion = struct { + zir_index: TrackedInst.Index, + name: NullTerminatedString, name_nav: Nav.Index.Optional, - flags: Flags, + namespace: NamespaceIndex, + /// The enum that provides the list of field names and values. + enum_tag_type: Index, + /// This could be provided through the tag type, but it is more convenient /// to store it directly. This is also necessary for `dumpStatsFallible` to /// work on unresolved types. fields_len: u32, - /// Only valid after .have_layout + + /// Always 0 until layout resolved. size: u32, - /// Only valid after .have_layout + /// Always 0 until layout resolved. padding: u32, - namespace: NamespaceIndex, - /// The enum that provides the list of field names and values. - tag_ty: Index, - zir_index: TrackedInst.Index, + + flags: Flags, pub const Flags = packed struct(u32) { - any_captures: bool, - runtime_tag: LoadedUnionType.RuntimeTag, - /// If false, the field alignment trailing data is omitted. - any_aligned_fields: bool, - layout: std.builtin.Type.ContainerLayout, - status: LoadedUnionType.Status, - requires_comptime: RequiresComptime, - assumed_runtime_bits: bool, - assumed_pointer_aligned: bool, + any_captures: enum(u2) { true, false, reified }, + + /// Whether `enum_tag_type` was explicitly specified with `union(E)` syntax. + /// + /// For `union(enum(E))` syntax, this is `false`, but the generated enum tag type is + /// considered to have an explicitly specified integer tag type. + enum_tag_mode: BackingTypeMode, + + /// `packed` layout is represented separately by `TypeStructPacked`. + layout: enum(u1) { auto, @"extern" }, + + any_field_aligns: bool, + tag_usage: LoadedUnionType.TagUsage, + + class: TypeClass, + has_runtime_tag: bool, + + /// Alignment of the whole union. Always `.none` until layout resolved. alignment: Alignment, - is_reified: bool, - _: u12 = 0, + + want_layout: bool, + + _: u14 = 0, }; }; + /// For declared unions, field names are intentionally omitted because they are available in + /// `enum_tag_type`. However, reified unions do store field names, because they are needed by + /// type resolution to create or validate the enum tag type (type resolution for declared unions + /// instead fetches field names from ZIR). + /// /// Trailing: - /// 0. captures_len: u32 // if `any_captures` - /// 1. capture: CaptureValue // for each `captures_len` - /// 2. type_hash: PackedU64 // if `is_reified` - /// 3. type: Index for each fields_len - /// 4. name: NullTerminatedString for each fields_len - /// 5. init: Index for each fields_len // if tag is type_struct_packed_inits - pub const TypeStructPacked = struct { + /// 0. type_hash: PackedU64 // if `captures_len == .reified` + /// 1. capture: CaptureValue // if `captures_len != .reified`; for each `captures_len` + /// 2. reified_field_name: NullTerminatedString // if `captures_len == .reified`; for each `fields_len` + /// 3. field_type: Index // for each `fields_len` + pub const TypeUnionPacked = struct { + zir_index: TrackedInst.Index, + bits: Bits, + name: NullTerminatedString, name_nav: Nav.Index.Optional, - zir_index: TrackedInst.Index, - fields_len: u32, namespace: NamespaceIndex, - backing_int_ty: Index, - names_map: MapIndex, - flags: Flags, - pub const Flags = packed struct(u32) { - any_captures: bool = false, - /// Dependency loop detection when resolving field inits. - field_inits_wip: bool = false, - inits_resolved: bool = false, - is_reified: bool = false, - _: u28 = 0, + /// The corresponding `BackingTypeMode` depends on the item's `Tag`. + backing_int_type: Index, + /// Although packed unions do not semantically have a tag type, the compiler still assigns + /// them a "hypothetical" tag type. + enum_tag_type: Index, + + /// This could be provided through the tag type, but it is more convenient + /// to store it directly. This is also necessary for `dumpStatsFallible` to + /// work on unresolved types. + fields_len: u32, + + const Bits = packed struct(u32) { + captures_len: enum(u31) { + reified = std.math.maxInt(u31), + _, + }, + want_layout: bool, }; }; - /// At first I thought of storing the denormalized data externally, such as... - /// - /// * runtime field order - /// * calculated field offsets - /// * size and alignment of the struct - /// - /// ...since these can be computed based on the other data here. However, - /// this data does need to be memoized, and therefore stored in memory - /// while the compiler is running, in order to avoid O(N^2) logic in many - /// places. Since the data can be stored compactly in the InternPool - /// representation, it is better for memory usage to store denormalized data - /// here, and potentially also better for performance as well. It's also simpler - /// than coming up with some other scheme for the data. - /// /// Trailing: - /// 0. captures_len: u32 // if `any_captures` - /// 1. capture: CaptureValue // for each `captures_len` - /// 2. type_hash: PackedU64 // if `is_reified` - /// 3. type: Index for each field in declared order - /// 4. if any_default_inits: - /// init: Index // for each field in declared order - /// 5. if any_aligned_fields: - /// align: Alignment // for each field in declared order - /// 6. if any_comptime_fields: - /// field_is_comptime_bits: u32 // minimal number of u32s needed, LSB is field 0 - /// 7. if not is_extern: - /// field_index: RuntimeOrder // for each field in runtime order - /// 8. field_offset: u32 // for each field in declared order, undef until layout_resolved - pub const TypeStruct = struct { + /// 0. owner_union: Index // if `captures_len == .generated_union_tag` + /// 1. zir_index: TrackedInst.Index // if `captures_len != .generated_union_tag` + /// 2. type_hash: PackedU64 // if `captures_len == .reified` + /// 3. capture: CaptureValue // if `captures_len` is not a named tag; for each `captures_len` + /// 4. field_value_map: MapIndex // if tag is not `.type_enum_auto` + /// 5. field_name: NullTerminatedString // for each `fields_len` + /// 6. field_value: Index // if tag is not `.type_enum_auto`; for each `fields_len` + pub const TypeEnum = struct { + bits: Bits, + name: NullTerminatedString, name_nav: Nav.Index.Optional, - zir_index: TrackedInst.Index, namespace: NamespaceIndex, + + /// An integer type which is used for the numerical value of the enum. Whether this was + /// user-provided or inferred by the compiler depends on the tag. + int_tag_type: Index, + fields_len: u32, - flags: Flags, - size: u32, + field_name_map: MapIndex, - pub const Flags = packed struct(u32) { - any_captures: bool = false, - is_extern: bool = false, - known_non_opv: bool = false, - requires_comptime: RequiresComptime = @enumFromInt(0), - assumed_runtime_bits: bool = false, - assumed_pointer_aligned: bool = false, - any_comptime_fields: bool = false, - any_default_inits: bool = false, - any_aligned_fields: bool = false, - /// `.none` until layout_resolved - alignment: Alignment = @enumFromInt(0), - /// Dependency loop detection when resolving struct alignment. - alignment_wip: bool = false, - /// Dependency loop detection when resolving field types. - field_types_wip: bool = false, - /// Dependency loop detection when resolving struct layout. - layout_wip: bool = false, - /// Indicates whether `size`, `alignment`, runtime field order, and - /// field offets are populated. - layout_resolved: bool = false, - /// Dependency loop detection when resolving field inits. - field_inits_wip: bool = false, - /// Indicates whether `field_inits` has been resolved. - inits_resolved: bool = false, - // The types and all its fields have had their layout resolved. Even through pointer = false, - // which `layout_resolved` does not ensure. - fully_resolved: bool = false, - is_reified: bool = false, - _: u8 = 0, + const Bits = packed struct(u32) { + captures_len: enum(u31) { + reified = std.math.maxInt(u31), + generated_union_tag = std.math.maxInt(u31) - 1, + _, + }, + want_layout: bool, }; }; /// Trailing: /// 0. capture: CaptureValue // for each `captures_len` pub const TypeOpaque = struct { + zir_index: TrackedInst.Index, + captures_len: u32, + name: NullTerminatedString, name_nav: Nav.Index.Optional, - /// Contains the declarations inside this opaque. namespace: NamespaceIndex, - /// The index of the `opaque_decl` instruction. - zir_index: TrackedInst.Index, - /// `std.math.maxInt(u32)` indicates this type is reified. - captures_len: u32, }; }; +/// Differentiates between user-provided and compiler-generated backing types for packed and tagged types. +pub const BackingTypeMode = enum(u1) { + /// The backing type was explicitly provided by the user. For instance: + /// union(T) + /// enum(T) + /// packed struct(T) + /// packed union(T) + /// Type layout resolution will evaluate the user-provided expression and validate that type. + explicit, + /// No backing type was explicitly provided by the user. Type layout resolution will populate + /// an inferred/generated type. + auto, +}; + /// State that is mutable during semantic analysis. This data is not used for /// equality or hashing, except for `inferred_error_set` which is considered /// to be part of the type of the function. pub const FuncAnalysis = packed struct(u32) { - is_analyzed: bool, + want_runtime_analysis: bool, branch_hint: std.builtin.BranchHint, is_noinline: bool, has_error_trace: bool, @@ -6399,13 +5932,9 @@ pub const SimpleType = enum(u32) { }; pub const SimpleValue = enum(u32) { - /// This is untyped `undefined`. - undefined = @intFromEnum(Index.undef), void = @intFromEnum(Index.void_value), /// This is untyped `null`. null = @intFromEnum(Index.null_value), - /// This is the untyped empty struct/array literal: `.{}` - empty_tuple = @intFromEnum(Index.empty_tuple), true = @intFromEnum(Index.bool_true), false = @intFromEnum(Index.bool_false), @"unreachable" = @intFromEnum(Index.unreachable_value), @@ -6536,12 +6065,17 @@ pub const Alignment = enum(u6) { pub const empty: Slice = .{ .tid = .main, .start = 0, .len = 0 }; pub fn get(slice: Slice, ip: *const InternPool) []Alignment { - // TODO: implement @ptrCast between slices changing the length const extra = ip.getLocalShared(slice.tid).extra.acquire(); - //const bytes: []u8 = @ptrCast(extra.view().items(.@"0")[slice.start..]); - const bytes: []u8 = std.mem.sliceAsBytes(extra.view().items(.@"0")[slice.start..]); + const bytes: []u8 = @ptrCast(extra.view().items(.@"0")[slice.start..]); return @ptrCast(bytes[0..slice.len]); } + + /// If `slice` is empty (`slice.len == 0`), returns `.none`. + /// Otherwise, asserts that `index < slice.len`, and returns the value at `index`. + pub fn getOrNone(slice: Slice, ip: *const InternPool, index: usize) Alignment { + if (slice.len == 0) return .none; + return slice.get(ip)[index]; + } }; pub fn toRelaxedCompareUnits(a: Alignment) u8 { @@ -6596,55 +6130,6 @@ pub const Array = struct { } }; -/// Trailing: -/// 0. owner_union: Index // if `zir_index == .none` -/// 1. capture: CaptureValue // for each `captures_len` -/// 2. type_hash: PackedU64 // if reified (`captures_len == std.math.maxInt(u32)`) -/// 3. field name: NullTerminatedString for each fields_len; declaration order -/// 4. tag value: Index for each fields_len; declaration order -pub const EnumExplicit = struct { - name: NullTerminatedString, - name_nav: Nav.Index.Optional, - /// `std.math.maxInt(u32)` indicates this type is reified. - captures_len: u32, - namespace: NamespaceIndex, - /// An integer type which is used for the numerical value of the enum, which - /// has been explicitly provided by the enum declaration. - int_tag_type: Index, - fields_len: u32, - /// Maps field names to declaration index. - names_map: MapIndex, - /// Maps field values to declaration index. - /// If this is `none`, it means the trailing tag values are absent because - /// they are auto-numbered. - values_map: OptionalMapIndex, - /// `none` means this is a generated tag type. - /// There will be a trailing union type for which this is a tag. - zir_index: TrackedInst.Index.Optional, -}; - -/// Trailing: -/// 0. owner_union: Index // if `zir_index == .none` -/// 1. capture: CaptureValue // for each `captures_len` -/// 2. type_hash: PackedU64 // if reified (`captures_len == std.math.maxInt(u32)`) -/// 3. field name: NullTerminatedString for each fields_len; declaration order -pub const EnumAuto = struct { - name: NullTerminatedString, - name_nav: Nav.Index.Optional, - /// `std.math.maxInt(u32)` indicates this type is reified. - captures_len: u32, - namespace: NamespaceIndex, - /// An integer type which is used for the numerical value of the enum, which - /// was inferred by Zig based on the number of tags. - int_tag_type: Index, - fields_len: u32, - /// Maps field names to declaration index. - names_map: MapIndex, - /// `none` means this is a generated tag type. - /// There will be a trailing union type for which this is a tag. - zir_index: TrackedInst.Index.Optional, -}; - pub const PackedU64 = packed struct(u64) { a: u32, b: u32, @@ -6827,11 +6312,6 @@ pub const IntSmall = struct { value: u32, }; -pub const IntLazy = struct { - ty: Index, - lazy_ty: Index, -}; - /// A f64 value, broken up into 2 u32 parts. pub const Float64 = struct { piece0: u32, @@ -6994,7 +6474,9 @@ pub fn deinit(ip: *InternPool, gpa: Allocator, io: Io) void { ip.src_hash_deps.deinit(gpa); ip.nav_val_deps.deinit(gpa); ip.nav_ty_deps.deinit(gpa); - ip.interned_deps.deinit(gpa); + ip.func_ies_deps.deinit(gpa); + ip.type_layout_deps.deinit(gpa); + ip.struct_defaults_deps.deinit(gpa); ip.zon_file_deps.deinit(gpa); ip.embed_file_deps.deinit(gpa); ip.namespace_deps.deinit(gpa); @@ -7130,132 +6612,118 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { .type_inferred_error_set => .{ .inferred_error_set_type = @enumFromInt(data), }, - - .type_opaque => .{ .opaque_type = ns: { - const extra = extraDataTrail(unwrapped_index.getExtra(ip), Tag.TypeOpaque, data); - if (extra.data.captures_len == std.math.maxInt(u32)) { - break :ns .{ .reified = .{ - .zir_index = extra.data.zir_index, - .type_hash = 0, - } }; - } - break :ns .{ .declared = .{ - .zir_index = extra.data.zir_index, - .captures = .{ .owned = .{ - .tid = unwrapped_index.tid, - .start = extra.end, - .len = extra.data.captures_len, - } }, - } }; - } }, + .type_function => .{ .func_type = extraFuncType(unwrapped_index.tid, unwrapped_index.getExtra(ip), data) }, + .type_tuple => .{ .tuple_type = extraTypeTuple(unwrapped_index.tid, unwrapped_index.getExtra(ip), data) }, .type_struct => .{ .struct_type = ns: { const extra_list = unwrapped_index.getExtra(ip); - const extra_items = extra_list.view().items(.@"0"); - const zir_index: TrackedInst.Index = @enumFromInt(extra_items[data + std.meta.fieldIndex(Tag.TypeStruct, "zir_index").?]); - const flags: Tag.TypeStruct.Flags = @bitCast(@atomicLoad(u32, &extra_items[data + std.meta.fieldIndex(Tag.TypeStruct, "flags").?], .unordered)); - const end_extra_index = data + @as(u32, @typeInfo(Tag.TypeStruct).@"struct".fields.len); - if (flags.is_reified) { - assert(!flags.any_captures); - break :ns .{ .reified = .{ - .zir_index = zir_index, - .type_hash = extraData(extra_list, PackedU64, end_extra_index).get(), - } }; - } - break :ns .{ .declared = .{ - .zir_index = zir_index, - .captures = .{ .owned = if (flags.any_captures) .{ - .tid = unwrapped_index.tid, - .start = end_extra_index + 1, - .len = extra_list.view().items(.@"0")[end_extra_index], - } else CaptureValue.Slice.empty }, - } }; + const extra = extraDataTrail(extra_list, Tag.TypeStruct, data); + break :ns switch (extra.data.flags.any_captures) { + .reified => .{ .reified = .{ + .zir_index = extra.data.zir_index, + .type_hash = extraData(extra_list, PackedU64, extra.end).get(), + } }, + .false => .{ .declared = .{ + .zir_index = extra.data.zir_index, + .captures = .{ .owned = .empty }, + } }, + .true => .{ .declared = .{ + .zir_index = extra.data.zir_index, + .captures = .{ .owned = .{ + .tid = unwrapped_index.tid, + .start = extra.end + 1, + .len = extra_list.view().items(.@"0")[extra.end], + } }, + } }, + }; } }, - - .type_struct_packed, .type_struct_packed_inits => .{ .struct_type = ns: { + .type_struct_packed_auto, + .type_struct_packed_explicit, + .type_struct_packed_auto_defaults, + .type_struct_packed_explicit_defaults, + => .{ .struct_type = ns: { const extra_list = unwrapped_index.getExtra(ip); - const extra_items = extra_list.view().items(.@"0"); - const zir_index: TrackedInst.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "zir_index").?]); - const flags: Tag.TypeStructPacked.Flags = @bitCast(@atomicLoad(u32, &extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "flags").?], .unordered)); - const end_extra_index = data + @as(u32, @typeInfo(Tag.TypeStructPacked).@"struct".fields.len); - if (flags.is_reified) { - assert(!flags.any_captures); - break :ns .{ .reified = .{ - .zir_index = zir_index, - .type_hash = extraData(extra_list, PackedU64, end_extra_index).get(), - } }; - } - break :ns .{ .declared = .{ - .zir_index = zir_index, - .captures = .{ .owned = if (flags.any_captures) .{ - .tid = unwrapped_index.tid, - .start = end_extra_index + 1, - .len = extra_items[end_extra_index], - } else CaptureValue.Slice.empty }, - } }; + const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, data); + break :ns switch (extra.data.bits.captures_len) { + .reified => .{ .reified = .{ + .zir_index = extra.data.zir_index, + .type_hash = extraData(extra_list, PackedU64, extra.end).get(), + } }, + _ => |len| .{ .declared = .{ + .zir_index = extra.data.zir_index, + .captures = .{ .owned = .{ + .tid = unwrapped_index.tid, + .start = extra.end, + .len = @intFromEnum(len), + } }, + } }, + }; } }, - .type_tuple => .{ .tuple_type = extraTypeTuple(unwrapped_index.tid, unwrapped_index.getExtra(ip), data) }, .type_union => .{ .union_type = ns: { const extra_list = unwrapped_index.getExtra(ip); const extra = extraDataTrail(extra_list, Tag.TypeUnion, data); - if (extra.data.flags.is_reified) { - assert(!extra.data.flags.any_captures); - break :ns .{ .reified = .{ + break :ns switch (extra.data.flags.any_captures) { + .reified => .{ .reified = .{ .zir_index = extra.data.zir_index, .type_hash = extraData(extra_list, PackedU64, extra.end).get(), - } }; - } - break :ns .{ .declared = .{ - .zir_index = extra.data.zir_index, - .captures = .{ .owned = if (extra.data.flags.any_captures) .{ - .tid = unwrapped_index.tid, - .start = extra.end + 1, - .len = extra_list.view().items(.@"0")[extra.end], - } else CaptureValue.Slice.empty }, - } }; + } }, + .false => .{ .declared = .{ + .zir_index = extra.data.zir_index, + .captures = .{ .owned = .empty }, + } }, + .true => .{ .declared = .{ + .zir_index = extra.data.zir_index, + .captures = .{ .owned = .{ + .tid = unwrapped_index.tid, + .start = extra.end + 1, + .len = extra_list.view().items(.@"0")[extra.end], + } }, + } }, + }; } }, - - .type_enum_auto => .{ .enum_type = ns: { + .type_union_packed_auto, .type_union_packed_explicit => .{ .union_type = ns: { const extra_list = unwrapped_index.getExtra(ip); - const extra = extraDataTrail(extra_list, EnumAuto, data); - const zir_index = extra.data.zir_index.unwrap() orelse { - assert(extra.data.captures_len == 0); - break :ns .{ .generated_tag = .{ - .union_type = @enumFromInt(extra_list.view().items(.@"0")[extra.end]), - } }; - }; - if (extra.data.captures_len == std.math.maxInt(u32)) { - break :ns .{ .reified = .{ - .zir_index = zir_index, + const extra = extraDataTrail(extra_list, Tag.TypeUnionPacked, data); + break :ns switch (extra.data.bits.captures_len) { + .reified => .{ .reified = .{ + .zir_index = extra.data.zir_index, .type_hash = extraData(extra_list, PackedU64, extra.end).get(), - } }; - } - break :ns .{ .declared = .{ - .zir_index = zir_index, - .captures = .{ .owned = .{ - .tid = unwrapped_index.tid, - .start = extra.end, - .len = extra.data.captures_len, } }, - } }; + _ => |len| .{ .declared = .{ + .zir_index = extra.data.zir_index, + .captures = .{ .owned = .{ + .tid = unwrapped_index.tid, + .start = extra.end, + .len = @intFromEnum(len), + } }, + } }, + }; } }, - .type_enum_explicit, .type_enum_nonexhaustive => .{ .enum_type = ns: { + .type_enum_auto, .type_enum_explicit, .type_enum_nonexhaustive => .{ .enum_type = ns: { const extra_list = unwrapped_index.getExtra(ip); - const extra = extraDataTrail(extra_list, EnumExplicit, data); - const zir_index = extra.data.zir_index.unwrap() orelse { - assert(extra.data.captures_len == 0); - break :ns .{ .generated_tag = .{ - .union_type = @enumFromInt(extra_list.view().items(.@"0")[extra.end]), - } }; + const extra = extraDataTrail(extra_list, Tag.TypeEnum, data); + break :ns switch (extra.data.bits.captures_len) { + .reified => .{ .reified = .{ + .zir_index = @enumFromInt(extra_list.view().items(.@"0")[extra.end]), + .type_hash = extraData(extra_list, PackedU64, extra.end + 1).get(), + } }, + .generated_union_tag => .{ .generated_union_tag = owner_union: { + break :owner_union @enumFromInt(extra_list.view().items(.@"0")[extra.end]); + } }, + _ => |len| .{ .declared = .{ + .zir_index = @enumFromInt(extra_list.view().items(.@"0")[extra.end]), + .captures = .{ .owned = .{ + .tid = unwrapped_index.tid, + .start = extra.end + 1, + .len = @intFromEnum(len), + } }, + } }, }; - if (extra.data.captures_len == std.math.maxInt(u32)) { - break :ns .{ .reified = .{ - .zir_index = zir_index, - .type_hash = extraData(extra_list, PackedU64, extra.end).get(), - } }; - } + } }, + .type_opaque => .{ .opaque_type = ns: { + const extra = extraDataTrail(unwrapped_index.getExtra(ip), Tag.TypeOpaque, data); break :ns .{ .declared = .{ - .zir_index = zir_index, + .zir_index = extra.data.zir_index, .captures = .{ .owned = .{ .tid = unwrapped_index.tid, .start = extra.end, @@ -7263,7 +6731,6 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { } }, } }; } }, - .type_function => .{ .func_type = extraFuncType(unwrapped_index.tid, unwrapped_index.getExtra(ip), data) }, .undef => .{ .undef = @enumFromInt(data) }, .opt_null => .{ .opt = .{ @@ -7390,17 +6857,6 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { .storage = .{ .u64 = info.value }, } }; }, - .int_lazy_align, .int_lazy_size => |tag| { - const info = extraData(unwrapped_index.getExtra(ip), IntLazy, data); - return .{ .int = .{ - .ty = info.ty, - .storage = switch (tag) { - .int_lazy_align => .{ .lazy_align = info.lazy_ty }, - .int_lazy_size => .{ .lazy_size = info.lazy_ty }, - else => unreachable, - }, - } }; - }, .float_f16 => .{ .float = .{ .ty = .f16_type, .storage = .{ .f16 = @bitCast(@as(u16, @intCast(data))) }, @@ -7488,7 +6944,8 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { }, .type_array_small, .type_vector, - .type_struct_packed, + .type_struct_packed_auto, + .type_struct_packed_explicit, => .{ .aggregate = .{ .ty = ty, .storage = .{ .elems = &.{} }, @@ -7496,11 +6953,14 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { // There is only one possible value precisely due to the // fact that this values slice is fully populated! - .type_struct, .type_struct_packed_inits => { + .type_struct, + .type_struct_packed_auto_defaults, + .type_struct_packed_explicit_defaults, + => { const info = loadStructType(ip, ty); return .{ .aggregate = .{ .ty = ty, - .storage = .{ .elems = @ptrCast(info.field_inits.get(ip)) }, + .storage = .{ .elems = @ptrCast(info.field_defaults.get(ip)) }, } }; }, @@ -7516,11 +6976,6 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { } }; }, - .type_enum_auto, - .type_enum_explicit, - .type_union, - => .{ .empty_enum_value = ty }, - else => unreachable, }; }, @@ -7566,6 +7021,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { }, .enum_literal => .{ .enum_literal = @enumFromInt(data) }, .enum_tag => .{ .enum_tag = extraData(unwrapped_index.getExtra(ip), Tag.EnumTag, data) }, + .bitpack => .{ .bitpack = extraData(unwrapped_index.getExtra(ip), Key.Bitpack, data) }, .memoized_call => { const extra_list = unwrapped_index.getExtra(ip); @@ -7634,7 +7090,6 @@ fn extraFuncType(tid: Zcu.PerThread.Id, extra: Local.Extra, extra_index: u32) Ke .cc = type_function.data.flags.cc.unpack(), .is_var_args = type_function.data.flags.is_var_args, .is_noinline = type_function.data.flags.is_noinline, - .is_generic = type_function.data.flags.is_generic, }; } @@ -7893,45 +7348,6 @@ fn getOrPutKeyEnsuringAdditionalCapacity( .map_index = map_index, } }; } -/// Like `getOrPutKey`, but asserts that the key already exists, and prepares to replace -/// its shard entry with a new `Index` anyway. After finalizing this, the old index remains -/// valid (in that `indexToKey` and similar queries will behave as before), but it will -/// never be returned from a lookup (`getOrPutKey` etc). -/// This is used by incremental compilation when an existing container type is outdated. In -/// this case, the type must be recreated at a new `InternPool.Index`, but the old index must -/// remain valid since now-unreferenced `AnalUnit`s may retain references to it. The old index -/// will be cleaned up when the `Zcu` undergoes garbage collection. -fn putKeyReplace( - ip: *InternPool, - io: Io, - tid: Zcu.PerThread.Id, - key: Key, -) GetOrPutKey { - const full_hash = key.hash64(ip); - const hash: u32 = @truncate(full_hash >> 32); - const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))]; - shard.mutate.map.mutex.lock(io, tid); - errdefer shard.mutate.map.mutex.unlock(io); - const map = shard.shared.map; - const map_mask = map.header().mask(); - var map_index = hash; - while (true) : (map_index += 1) { - map_index &= map_mask; - const entry = &map.entries[map_index]; - const index = entry.value; - assert(index != .none); // key not present - if (entry.hash == hash and ip.indexToKey(index).eql(key, ip)) { - break; // we found the entry to replace - } - } - return .{ .new = .{ - .ip = ip, - .tid = tid, - .io = io, - .shard = shard, - .map_index = map_index, - } }; -} pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: Key) Allocator.Error!Index { var gop = try ip.getOrPutKey(gpa, io, tid, key); @@ -8084,12 +7500,12 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: }); }, - .struct_type => unreachable, // use getStructType() instead - .tuple_type => unreachable, // use getTupleType() instead - .union_type => unreachable, // use getUnionType() instead - .opaque_type => unreachable, // use getOpaqueType() instead + .struct_type => unreachable, // instead use: getDeclaredStructType, getReifiedStructType + .union_type => unreachable, // instead use: getDeclaredUnionType, getReifiedUnionType + .enum_type => unreachable, // instead use: getDeclaredEnumType, getReifiedEnumType, getGeneratedEnumTagType + .opaque_type => unreachable, // instead use: getDeclaredOpaqueType - .enum_type => unreachable, // use getEnumType() instead + .tuple_type => unreachable, // use getTupleType() instead .func_type => unreachable, // use getFuncType() instead .@"extern" => unreachable, // use getExtern() instead .func => unreachable, // use getFuncInstance() or getFuncDecl() instead @@ -8247,25 +7663,8 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: }); }, - .int => |int| b: { - assert(ip.isIntegerType(int.ty)); - switch (int.storage) { - .u64, .i64, .big_int => {}, - .lazy_align, .lazy_size => |lazy_ty| { - items.appendAssumeCapacity(.{ - .tag = switch (int.storage) { - else => unreachable, - .lazy_align => .int_lazy_align, - .lazy_size => .int_lazy_size, - }, - .data = try addExtra(extra, IntLazy{ - .ty = int.ty, - .lazy_ty = lazy_ty, - }), - }); - return gop.put(); - }, - } + .int => |int| b: { + assert(ip.isIntegerType(int.ty)); switch (int.ty) { .u8_type => switch (int.storage) { .big_int => |big_int| { @@ -8282,7 +7681,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: }); break :b; }, - .lazy_align, .lazy_size => unreachable, }, .u16_type => switch (int.storage) { .big_int => |big_int| { @@ -8299,7 +7697,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: }); break :b; }, - .lazy_align, .lazy_size => unreachable, }, .u32_type => switch (int.storage) { .big_int => |big_int| { @@ -8316,7 +7713,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: }); break :b; }, - .lazy_align, .lazy_size => unreachable, }, .i32_type => switch (int.storage) { .big_int => |big_int| { @@ -8334,7 +7730,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: }); break :b; }, - .lazy_align, .lazy_size => unreachable, }, .usize_type => switch (int.storage) { .big_int => |big_int| { @@ -8355,7 +7750,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: break :b; } }, - .lazy_align, .lazy_size => unreachable, }, .comptime_int_type => switch (int.storage) { .big_int => |big_int| { @@ -8390,7 +7784,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: break :b; } }, - .lazy_align, .lazy_size => unreachable, }, else => {}, } @@ -8427,7 +7820,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: const tag: Tag = if (big_int.positive) .int_positive else .int_negative; try addInt(ip, gpa, io, tid, int.ty, tag, big_int.limbs); }, - .lazy_align, .lazy_size => unreachable, } }, @@ -8468,7 +7860,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: assert(ip.isEnumType(enum_tag.ty)); switch (ip.indexToKey(enum_tag.ty)) { .simple_type => assert(ip.isIntegerType(ip.typeOf(enum_tag.int))), - .enum_type => assert(ip.typeOf(enum_tag.int) == ip.loadEnumType(enum_tag.ty).tag_ty), + .enum_type => assert(ip.typeOf(enum_tag.int) == ip.loadEnumType(enum_tag.ty).int_tag_type), else => unreachable, } items.appendAssumeCapacity(.{ @@ -8477,11 +7869,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: }); }, - .empty_enum_value => |enum_or_union_ty| items.appendAssumeCapacity(.{ - .tag = .only_possible_value, - .data = @intFromEnum(enum_or_union_ty), - }), - .float => |float| { switch (float.ty) { .f16_type => items.appendAssumeCapacity(.{ @@ -8525,15 +7912,14 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: .aggregate => |aggregate| { const ty_key = ip.indexToKey(aggregate.ty); const len = ip.aggregateTypeLen(aggregate.ty); - const child = switch (ty_key) { - .array_type => |array_type| array_type.child, - .vector_type => |vector_type| vector_type.child, - .tuple_type, .struct_type => .none, - else => unreachable, - }; - const sentinel = switch (ty_key) { - .array_type => |array_type| array_type.sentinel, - .vector_type, .tuple_type, .struct_type => .none, + const child: Index, const sentinel: Index = switch (ty_key) { + .array_type => |array_type| .{ array_type.child, array_type.sentinel }, + .vector_type => |vector_type| .{ vector_type.child, .none }, + .tuple_type => .{ .none, .none }, + .struct_type => child: { + assert(ip.loadStructType(aggregate.ty).layout != .@"packed"); + break :child .{ .none, .none }; + }, else => unreachable, }; const len_including_sentinel = len + @intFromBool(sentinel != .none); @@ -8715,224 +8101,929 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: extra.appendSliceAssumeCapacity(.{@ptrCast(aggregate.storage.elems)}); if (sentinel != .none) extra.appendAssumeCapacity(.{@intFromEnum(sentinel)}); }, + .bitpack => |bitpack| { + switch (ip.zigTypeTag(bitpack.ty)) { + .@"struct" => assert(ip.typeOf(bitpack.backing_int_val) == ip.loadStructType(bitpack.ty).packed_backing_int_type), + .@"union" => assert(ip.typeOf(bitpack.backing_int_val) == ip.loadUnionType(bitpack.ty).packed_backing_int_type), + else => unreachable, + } + assert(!ip.isUndef(bitpack.backing_int_val)); + items.appendAssumeCapacity(.{ + .tag = .bitpack, + .data = try addExtra(extra, bitpack), + }); + }, .memoized_call => |memoized_call| { for (memoized_call.arg_values) |arg| assert(arg != .none); try extra.ensureUnusedCapacity(@typeInfo(MemoizedCall).@"struct".fields.len + memoized_call.arg_values.len); items.appendAssumeCapacity(.{ - .tag = .memoized_call, - .data = addExtraAssumeCapacity(extra, MemoizedCall{ - .func = memoized_call.func, - .args_len = @intCast(memoized_call.arg_values.len), - .result = memoized_call.result, - .branch_count = memoized_call.branch_count, - }), + .tag = .memoized_call, + .data = addExtraAssumeCapacity(extra, MemoizedCall{ + .func = memoized_call.func, + .args_len = @intCast(memoized_call.arg_values.len), + .result = memoized_call.result, + .branch_count = memoized_call.branch_count, + }), + }); + extra.appendSliceAssumeCapacity(.{@ptrCast(memoized_call.arg_values)}); + }, + } + return gop.put(); +} + +pub fn getDeclaredStructType( + ip: *InternPool, + gpa: Allocator, + io: Io, + tid: Zcu.PerThread.Id, + ini: struct { + zir_index: TrackedInst.Index, + captures: []const CaptureValue, + + // If the value of any of the following fields would change on an incremental update, then logic + // in `Zcu.mapOldZirToNew` must detect that (these properties are all trivially known from ZIR) + // and refuse to map the type declaration. This causes `zir_index` to change so that a new type + // will be interned at a fresh index. + // + // In the future, it would be good to remove all of those fields from `ini`, and in fact just + // have a single function `getDeclaredContainer` which is suitable for all container types. + // However, this requires some major changes to how container types are represented in the + // InternPool, so that it is possible for their backing storage to be "reallocated" as needed + // during type resolution. + fields_len: u32, + layout: std.builtin.Type.ContainerLayout, + any_comptime_fields: bool, + any_field_defaults: bool, + any_field_aligns: bool, + packed_backing_mode: BackingTypeMode, + }, +) Allocator.Error!WipContainerType.Result { + var gop = try ip.getOrPutKey(gpa, io, tid, .{ .struct_type = .{ .declared = .{ + .zir_index = ini.zir_index, + .captures = .{ .external = ini.captures }, + } } }); + defer gop.deinit(); + if (gop == .existing) return .{ .existing = gop.existing }; + + const local = ip.getLocal(tid); + const items = local.getMutableItems(gpa, io); + const extra = local.getMutableExtra(gpa, io); + try items.ensureUnusedCapacity(1); + + const field_name_map = try ip.addMap(gpa, io, tid, ini.fields_len); + errdefer local.mutate.maps.len -= 1; + + const is_extern = switch (ini.layout) { + .auto => false, + .@"extern" => true, + .@"packed" => { + try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeStructPacked).@"struct".fields.len + + ini.captures.len + // capture + ini.fields_len + // field_name + ini.fields_len + // field_type + (if (ini.any_field_defaults) ini.fields_len else 0)); // field_default + + const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStructPacked{ + .zir_index = ini.zir_index, + .bits = .{ + .captures_len = @enumFromInt(ini.captures.len), + .want_layout = false, + }, + .name = undefined, // set by `finish` + .name_nav = undefined, // set by `finish` + .namespace = undefined, // set by `finish` + .backing_int_type = .none, + .fields_len = ini.fields_len, + .field_name_map = field_name_map, + }); + extra.appendSliceAssumeCapacity(.{@ptrCast(ini.captures)}); // capture + extra.appendNTimesAssumeCapacity(.{@intFromEnum(NullTerminatedString.empty)}, ini.fields_len); // field_name + extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_type + if (ini.any_field_defaults) { + extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_default + } + items.appendAssumeCapacity(.{ + .tag = switch (ini.packed_backing_mode) { + .auto => if (ini.any_field_defaults) .type_struct_packed_auto_defaults else .type_struct_packed_auto, + .explicit => if (ini.any_field_defaults) .type_struct_packed_explicit_defaults else .type_struct_packed_explicit, + }, + .data = extra_index, + }); + return .{ .wip = .{ + .index = gop.put(), + .tid = tid, + .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name").?, + .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name_nav").?, + .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?, + .field_names = undefined, + .field_types = undefined, + .field_values = undefined, + .field_aligns = undefined, + .field_is_comptime_bits = undefined, + } }; + }, + }; + + try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeStruct).@"struct".fields.len + + 1 + // captures_len + ini.captures.len + // capture + ini.fields_len + // field_name + ini.fields_len + // field_type + (if (ini.any_field_defaults) ini.fields_len else 0) + // field_default + (if (ini.any_field_aligns) (ini.fields_len + 3) / 4 else 0) + // field_align + (if (ini.any_comptime_fields) (ini.fields_len + 31) / 32 else 0) + // field_is_comptime_bits + (if (!is_extern) ini.fields_len else 0) + // field_runtime_order + ini.fields_len); // field_offset + + const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStruct{ + .zir_index = ini.zir_index, + .name = undefined, // set by `finish` + .name_nav = undefined, // set by `finish` + .namespace = undefined, // set by `finish` + .fields_len = ini.fields_len, + .field_name_map = field_name_map, + .size = 0, + .flags = .{ + .any_captures = if (ini.captures.len != 0) .true else .false, + .layout = if (is_extern) .@"extern" else .auto, + .any_comptime_fields = ini.any_comptime_fields, + .any_field_defaults = ini.any_field_defaults, + .any_field_aligns = ini.any_field_aligns, + .class = .no_possible_value, + .alignment = .none, + .want_layout = false, + }, + }); + if (ini.captures.len != 0) { + extra.appendAssumeCapacity(.{@intCast(ini.captures.len)}); // captures_len + extra.appendSliceAssumeCapacity(.{@ptrCast(ini.captures)}); // capture + } + extra.appendNTimesAssumeCapacity(.{@intFromEnum(NullTerminatedString.empty)}, ini.fields_len); // field_name + extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_type + if (ini.any_field_defaults) { + extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_default + } + if (ini.any_field_aligns) { + extra.appendNTimesAssumeCapacity(.{0}, (ini.fields_len + 3) / 4); // field_align + } + if (ini.any_comptime_fields) { + extra.appendNTimesAssumeCapacity(.{0}, (ini.fields_len + 31) / 32); // field_is_comptime_bits + } + if (!is_extern) { + extra.appendNTimesAssumeCapacity(.{@intFromEnum(LoadedStructType.RuntimeOrder.unresolved)}, ini.fields_len); // field_runtime_order + } + extra.appendNTimesAssumeCapacity(.{0}, ini.fields_len); // field_offset + items.appendAssumeCapacity(.{ + .tag = .type_struct, + .data = extra_index, + }); + return .{ .wip = .{ + .index = gop.put(), + .tid = tid, + .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name").?, + .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name_nav").?, + .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "namespace").?, + .field_names = undefined, + .field_types = undefined, + .field_values = undefined, + .field_aligns = undefined, + .field_is_comptime_bits = undefined, + } }; +} + +pub fn getReifiedStructType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct { + zir_index: TrackedInst.Index, + type_hash: u64, + fields_len: u32, + layout: std.builtin.Type.ContainerLayout, + any_comptime_fields: bool, + any_field_defaults: bool, + any_field_aligns: bool, + /// Explicitly specified backing int type. `.none` if not packed or if backing type is inferred. + packed_backing_int_type: Index, +}) Allocator.Error!WipContainerType.Result { + var gop = try ip.getOrPutKey(gpa, io, tid, .{ .struct_type = .{ .reified = .{ + .zir_index = ini.zir_index, + .type_hash = ini.type_hash, + } } }); + defer gop.deinit(); + if (gop == .existing) return .{ .existing = gop.existing }; + + const local = ip.getLocal(tid); + const items = local.getMutableItems(gpa, io); + const extra = local.getMutableExtra(gpa, io); + try items.ensureUnusedCapacity(1); + + const field_name_map = try ip.addMap(gpa, io, tid, ini.fields_len); + errdefer local.mutate.maps.len -= 1; + + const is_extern = switch (ini.layout) { + .auto => false, + .@"extern" => true, + .@"packed" => { + try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeStructPacked).@"struct".fields.len + + 2 + // type_hash + ini.fields_len + // field_name + ini.fields_len + // field_type + (if (ini.any_field_defaults) ini.fields_len else 0)); // field_default + + const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStructPacked{ + .zir_index = ini.zir_index, + .bits = .{ + .captures_len = .reified, + .want_layout = false, + }, + .name = undefined, // set by `finish` + .name_nav = undefined, // set by `finish` + .namespace = undefined, // set by `finish` + .backing_int_type = ini.packed_backing_int_type, + .fields_len = ini.fields_len, + .field_name_map = field_name_map, + }); + _ = addExtraAssumeCapacity(extra, PackedU64.init(ini.type_hash)); // type_hash + const field_names_start = extra.mutate.len; + extra.appendNTimesAssumeCapacity(.{@intFromEnum(NullTerminatedString.empty)}, ini.fields_len); // field_name + const field_types_start = extra.mutate.len; + extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_type + const field_defaults_start = extra.mutate.len; + if (ini.any_field_defaults) { + extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_default + } + items.appendAssumeCapacity(.{ + .tag = switch (ini.packed_backing_int_type) { + .none => if (ini.any_field_defaults) .type_struct_packed_auto_defaults else .type_struct_packed_auto, + else => if (ini.any_field_defaults) .type_struct_packed_explicit_defaults else .type_struct_packed_explicit, + }, + .data = extra_index, + }); + return .{ .wip = .{ + .index = gop.put(), + .tid = tid, + .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name").?, + .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name_nav").?, + .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?, + .field_names = .{ .tid = tid, .start = field_names_start, .len = ini.fields_len }, + .field_types = .{ .tid = tid, .start = field_types_start, .len = ini.fields_len }, + .field_values = if (ini.any_field_defaults) + .{ .tid = tid, .start = field_defaults_start, .len = ini.fields_len } + else + undefined, + .field_aligns = undefined, + .field_is_comptime_bits = undefined, + } }; + }, + }; + + try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeStruct).@"struct".fields.len + + 2 + // type_hash + ini.fields_len + // field_name + ini.fields_len + // field_type + (if (ini.any_field_defaults) ini.fields_len else 0) + // field_default + (if (ini.any_field_aligns) (ini.fields_len + 3) / 4 else 0) + // field_align + (if (ini.any_comptime_fields) (ini.fields_len + 31) / 32 else 0) + // field_is_comptime_bits + (if (!is_extern) ini.fields_len else 0) + // field_runtime_order + ini.fields_len); // field_offset + + const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStruct{ + .zir_index = ini.zir_index, + .name = undefined, // set by `finish` + .name_nav = undefined, // set by `finish` + .namespace = undefined, // set by `finish` + .fields_len = ini.fields_len, + .field_name_map = field_name_map, + .size = 0, + .flags = .{ + .any_captures = .reified, + .layout = if (is_extern) .@"extern" else .auto, + .any_comptime_fields = ini.any_comptime_fields, + .any_field_defaults = ini.any_field_defaults, + .any_field_aligns = ini.any_field_aligns, + .class = .no_possible_value, + .alignment = .none, + .want_layout = false, + }, + }); + _ = addExtraAssumeCapacity(extra, PackedU64.init(ini.type_hash)); // type_hash + const field_names_start = extra.mutate.len; + extra.appendNTimesAssumeCapacity(.{@intFromEnum(NullTerminatedString.empty)}, ini.fields_len); // field_name + const field_types_start = extra.mutate.len; + extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_type + const field_defaults_start = extra.mutate.len; + if (ini.any_field_defaults) { + extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_default + } + const field_aligns_start = extra.mutate.len; + if (ini.any_field_aligns) { + extra.appendNTimesAssumeCapacity(.{0}, (ini.fields_len + 3) / 4); // field_align + } + const field_is_comptime_bits_start = extra.mutate.len; + if (ini.any_comptime_fields) { + extra.appendNTimesAssumeCapacity(.{0}, (ini.fields_len + 31) / 32); // field_is_comptime_bits + } + if (!is_extern) { + extra.appendNTimesAssumeCapacity(.{@intFromEnum(LoadedStructType.RuntimeOrder.unresolved)}, ini.fields_len); // field_runtime_order + } + extra.appendNTimesAssumeCapacity(.{0}, ini.fields_len); // field_offset + items.appendAssumeCapacity(.{ + .tag = .type_struct, + .data = extra_index, + }); + return .{ .wip = .{ + .index = gop.put(), + .tid = tid, + .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name").?, + .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name_nav").?, + .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "namespace").?, + .field_names = .{ .tid = tid, .start = field_names_start, .len = ini.fields_len }, + .field_types = .{ .tid = tid, .start = field_types_start, .len = ini.fields_len }, + .field_values = if (ini.any_field_defaults) + .{ .tid = tid, .start = field_defaults_start, .len = ini.fields_len } + else + undefined, + .field_aligns = if (ini.any_field_aligns) + .{ .tid = tid, .start = field_aligns_start, .len = ini.fields_len } + else + undefined, + .field_is_comptime_bits = if (ini.any_comptime_fields) + .{ .tid = tid, .start = field_is_comptime_bits_start, .len = (ini.fields_len + 31) / 32 } + else + undefined, + } }; +} + +pub fn getDeclaredUnionType( + ip: *InternPool, + gpa: Allocator, + io: Io, + tid: Zcu.PerThread.Id, + ini: struct { + zir_index: TrackedInst.Index, + captures: []const CaptureValue, + + // If the value of any of the following fields would change on an incremental update, then logic + // in `Zcu.mapOldZirToNew` must detect that (these properties are all trivially known from ZIR) + // and refuse to map the type declaration. This causes `zir_index` to change so that a new type + // will be interned at a fresh index. + // + // In the future, it would be good to remove all of those fields from `ini`, and in fact just + // have a single function `getDeclaredContainer` which is suitable for all container types. + // However, this requires some major changes to how container types are represented in the + // InternPool, so that it is possible for their backing storage to be "reallocated" as needed + // during type resolution. + fields_len: u32, + layout: std.builtin.Type.ContainerLayout, + any_field_aligns: bool, + tag_usage: LoadedUnionType.TagUsage, + enum_tag_mode: BackingTypeMode, + packed_backing_mode: BackingTypeMode, + }, +) Allocator.Error!WipContainerType.Result { + var gop = try ip.getOrPutKey(gpa, io, tid, .{ .union_type = .{ .declared = .{ + .zir_index = ini.zir_index, + .captures = .{ .external = ini.captures }, + } } }); + defer gop.deinit(); + if (gop == .existing) return .{ .existing = gop.existing }; + + const local = ip.getLocal(tid); + const items = local.getMutableItems(gpa, io); + const extra = local.getMutableExtra(gpa, io); + try items.ensureUnusedCapacity(1); + + const is_extern = switch (ini.layout) { + .auto => false, + .@"extern" => true, + .@"packed" => { + try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeUnionPacked).@"struct".fields.len + + ini.captures.len + // capture + ini.fields_len); // field_type + + const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnionPacked{ + .zir_index = ini.zir_index, + .bits = .{ + .captures_len = @enumFromInt(ini.captures.len), + .want_layout = false, + }, + .name = undefined, // set by `finish` + .name_nav = undefined, // set by `finish` + .namespace = undefined, // set by `finish` + .backing_int_type = .none, + .enum_tag_type = .none, + .fields_len = ini.fields_len, + }); + extra.appendSliceAssumeCapacity(.{@ptrCast(ini.captures)}); // capture + extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_type + items.appendAssumeCapacity(.{ + .tag = switch (ini.packed_backing_mode) { + .auto => .type_union_packed_auto, + .explicit => .type_union_packed_explicit, + }, + .data = extra_index, + }); + return .{ .wip = .{ + .index = gop.put(), + .tid = tid, + .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "name").?, + .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "name_nav").?, + .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "namespace").?, + .field_names = undefined, + .field_types = undefined, + .field_values = undefined, + .field_aligns = undefined, + .field_is_comptime_bits = undefined, + } }; + }, + }; + + try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeUnion).@"struct".fields.len + + 1 + // captures_len + ini.captures.len + // capture + ini.fields_len + // field_type + (if (ini.any_field_aligns) (ini.fields_len + 3) / 4 else 0)); // field_align + + const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnion{ + .zir_index = ini.zir_index, + .name = undefined, // set by `finish` + .name_nav = undefined, // set by `finish` + .namespace = undefined, // set by `finish` + .enum_tag_type = .none, + .fields_len = ini.fields_len, + .size = 0, + .padding = 0, + .flags = .{ + .any_captures = if (ini.captures.len != 0) .true else .false, + .enum_tag_mode = ini.enum_tag_mode, + .layout = if (is_extern) .@"extern" else .auto, + .any_field_aligns = ini.any_field_aligns, + .tag_usage = ini.tag_usage, + .class = .no_possible_value, + .has_runtime_tag = false, + .alignment = .none, + .want_layout = false, + }, + }); + if (ini.captures.len > 0) { + extra.appendAssumeCapacity(.{@intCast(ini.captures.len)}); // captures_len + extra.appendSliceAssumeCapacity(.{@ptrCast(ini.captures)}); // capture + } + extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_type + if (ini.any_field_aligns) { + extra.appendNTimesAssumeCapacity(.{0}, (ini.fields_len + 3) / 4); // field_align + } + items.appendAssumeCapacity(.{ + .tag = .type_union, + .data = extra_index, + }); + return .{ .wip = .{ + .index = gop.put(), + .tid = tid, + .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name").?, + .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name_nav").?, + .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "namespace").?, + .field_names = undefined, + .field_types = undefined, + .field_values = undefined, + .field_aligns = undefined, + .field_is_comptime_bits = undefined, + } }; +} + +pub fn getReifiedUnionType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct { + zir_index: TrackedInst.Index, + type_hash: u64, + fields_len: u32, + layout: std.builtin.Type.ContainerLayout, + any_field_aligns: bool, + tag_usage: LoadedUnionType.TagUsage, + /// Explicitly specified enum tag type. `.none` if `tag_usage != .tagged`. + enum_tag_type: Index, + /// Explicitly specified backing int type. `.none` if not packed or if backing type is inferred. + packed_backing_int_type: Index, +}) Allocator.Error!WipContainerType.Result { + var gop = try ip.getOrPutKey(gpa, io, tid, .{ .union_type = .{ .reified = .{ + .zir_index = ini.zir_index, + .type_hash = ini.type_hash, + } } }); + defer gop.deinit(); + if (gop == .existing) return .{ .existing = gop.existing }; + + const local = ip.getLocal(tid); + const items = local.getMutableItems(gpa, io); + const extra = local.getMutableExtra(gpa, io); + try items.ensureUnusedCapacity(1); + + const is_extern = switch (ini.layout) { + .auto => false, + .@"extern" => true, + .@"packed" => { + try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeUnionPacked).@"struct".fields.len + + 2 + // type_hash + ini.fields_len + // reified_field_name + ini.fields_len); // field_type + + const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnionPacked{ + .zir_index = ini.zir_index, + .bits = .{ + .captures_len = .reified, + .want_layout = false, + }, + .name = undefined, // set by `finish` + .name_nav = undefined, // set by `finish` + .namespace = undefined, // set by `finish` + .backing_int_type = ini.packed_backing_int_type, + .enum_tag_type = .none, + .fields_len = ini.fields_len, + }); + _ = addExtraAssumeCapacity(extra, PackedU64.init(ini.type_hash)); // type_hash + const field_names_start = extra.mutate.len; + extra.appendNTimesAssumeCapacity(.{@intFromEnum(NullTerminatedString.empty)}, ini.fields_len); // reified_field_name + const field_types_start = extra.mutate.len; + extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_type + items.appendAssumeCapacity(.{ + .tag = switch (ini.packed_backing_int_type) { + .none => .type_union_packed_auto, + else => .type_union_packed_explicit, + }, + .data = extra_index, }); - extra.appendSliceAssumeCapacity(.{@ptrCast(memoized_call.arg_values)}); + return .{ .wip = .{ + .index = gop.put(), + .tid = tid, + .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "name").?, + .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "name_nav").?, + .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "namespace").?, + .field_names = .{ .tid = tid, .start = field_names_start, .len = ini.fields_len }, + .field_types = .{ .tid = tid, .start = field_types_start, .len = ini.fields_len }, + .field_values = undefined, + .field_aligns = undefined, + .field_is_comptime_bits = undefined, + } }; + }, + }; + + try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeUnion).@"struct".fields.len + + 2 + // type_hash + ini.fields_len + // reified_field_name + ini.fields_len + // field_type + (if (ini.any_field_aligns) (ini.fields_len + 3) / 4 else 0)); // field_align + + const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnion{ + .zir_index = ini.zir_index, + .name = undefined, // set by `finish` + .name_nav = undefined, // set by `finish` + .namespace = undefined, // set by `finish` + .enum_tag_type = ini.enum_tag_type, + .fields_len = ini.fields_len, + .size = 0, + .padding = 0, + .flags = .{ + .any_captures = .reified, + .enum_tag_mode = if (ini.enum_tag_type == .none) .auto else .explicit, + .layout = if (is_extern) .@"extern" else .auto, + .any_field_aligns = ini.any_field_aligns, + .tag_usage = ini.tag_usage, + .class = .no_possible_value, + .has_runtime_tag = false, + .alignment = .none, + .want_layout = false, }, + }); + _ = addExtraAssumeCapacity(extra, PackedU64.init(ini.type_hash)); + const field_names_start = extra.mutate.len; + extra.appendNTimesAssumeCapacity(.{@intFromEnum(NullTerminatedString.empty)}, ini.fields_len); // reified_field_name + const field_types_start = extra.mutate.len; + extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_type + const field_aligns_start = extra.mutate.len; + if (ini.any_field_aligns) { + extra.appendNTimesAssumeCapacity(.{0}, (ini.fields_len + 3) / 4); // field_align } - return gop.put(); + items.appendAssumeCapacity(.{ + .tag = .type_union, + .data = extra_index, + }); + return .{ .wip = .{ + .index = gop.put(), + .tid = tid, + .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name").?, + .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name_nav").?, + .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "namespace").?, + .field_names = .{ .tid = tid, .start = field_names_start, .len = ini.fields_len }, + .field_types = .{ .tid = tid, .start = field_types_start, .len = ini.fields_len }, + .field_values = undefined, + .field_aligns = if (ini.any_field_aligns) + .{ .tid = tid, .start = field_aligns_start, .len = ini.fields_len } + else + undefined, + .field_is_comptime_bits = undefined, + } }; } -pub fn getUnion( +pub fn getDeclaredEnumType( ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, - un: Key.Union, -) Allocator.Error!Index { - var gop = try ip.getOrPutKey(gpa, io, tid, .{ .un = un }); + ini: struct { + zir_index: TrackedInst.Index, + captures: []const CaptureValue, + + // If the value of any of the following fields would change on an incremental update, then logic + // in `Zcu.mapOldZirToNew` must detect that (these properties are all trivially known from ZIR) + // and refuse to map the type declaration. This causes `zir_index` to change so that a new type + // will be interned at a fresh index. + // + // In the future, it would be good to remove all of those fields from `ini`, and in fact just + // have a single function `getDeclaredContainer` which is suitable for all container types. + // However, this requires some major changes to how container types are represented in the + // InternPool, so that it is possible for their backing storage to be "reallocated" as needed + // during type resolution. + fields_len: u32, + nonexhaustive: bool, + /// For `enum(T)` this is `.explicit`. Otherwise this is `.none`. + int_tag_mode: BackingTypeMode, + }, +) Allocator.Error!WipContainerType.Result { + var gop = try ip.getOrPutKey(gpa, io, tid, .{ .enum_type = .{ .declared = .{ + .zir_index = ini.zir_index, + .captures = .{ .external = ini.captures }, + } } }); defer gop.deinit(); - if (gop == .existing) return gop.existing; + if (gop == .existing) return .{ .existing = gop.existing }; + const local = ip.getLocal(tid); const items = local.getMutableItems(gpa, io); const extra = local.getMutableExtra(gpa, io); try items.ensureUnusedCapacity(1); - assert(un.ty != .none); - assert(un.val != .none); + const tag: Tag, const have_values: bool = if (ini.nonexhaustive) + .{ .type_enum_nonexhaustive, true } + else if (ini.int_tag_mode == .explicit) + .{ .type_enum_explicit, true } + else + .{ .type_enum_auto, false }; + + const field_name_map = try ip.addMap(gpa, io, tid, ini.fields_len); + errdefer local.mutate.maps.len -= 1; + + const field_value_map = if (have_values) try ip.addMap(gpa, io, tid, ini.fields_len) else undefined; + errdefer local.mutate.maps.len -= @intFromBool(have_values); + + try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeEnum).@"struct".fields.len + + 1 + // zir_index + ini.captures.len + // capture + @intFromBool(have_values) + // field_value_map + ini.fields_len + // field_name + (if (have_values) ini.fields_len else 0)); // field_value + + const extra_index = addExtraAssumeCapacity(extra, Tag.TypeEnum{ + .bits = .{ + .captures_len = @enumFromInt(ini.captures.len), + .want_layout = false, + }, + .name = undefined, // set by `finish` + .name_nav = undefined, // set by `finish` + .namespace = undefined, // set by `finish` + .int_tag_type = .none, + .fields_len = ini.fields_len, + .field_name_map = field_name_map, + }); + extra.appendAssumeCapacity(.{@intFromEnum(ini.zir_index)}); // zir_index + extra.appendSliceAssumeCapacity(.{@ptrCast(ini.captures)}); // capture + if (have_values) extra.appendAssumeCapacity(.{@intFromEnum(field_value_map)}); // field_value_map + extra.appendNTimesAssumeCapacity(.{@intFromEnum(NullTerminatedString.empty)}, ini.fields_len); // field_name + if (have_values) extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_value items.appendAssumeCapacity(.{ - .tag = .union_value, - .data = try addExtra(extra, un), + .tag = tag, + .data = extra_index, }); - - return gop.put(); + return .{ .wip = .{ + .index = gop.put(), + .tid = tid, + .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name").?, + .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name_nav").?, + .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "namespace").?, + .field_names = undefined, + .field_types = undefined, + .field_values = undefined, + .field_aligns = undefined, + .field_is_comptime_bits = undefined, + } }; } -pub const UnionTypeInit = struct { - flags: packed struct { - runtime_tag: LoadedUnionType.RuntimeTag, - any_aligned_fields: bool, - layout: std.builtin.Type.ContainerLayout, - status: LoadedUnionType.Status, - requires_comptime: RequiresComptime, - assumed_runtime_bits: bool, - assumed_pointer_aligned: bool, - alignment: Alignment, - }, +pub fn getReifiedEnumType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct { + zir_index: TrackedInst.Index, + type_hash: u64, fields_len: u32, - enum_tag_ty: Index, - /// May have length 0 which leaves the values unset until later. - field_types: []const Index, - /// May have length 0 which leaves the values unset until later. - /// The logic for `any_aligned_fields` is asserted to have been done before - /// calling this function. - field_aligns: []const Alignment, - key: union(enum) { - declared: struct { - zir_index: TrackedInst.Index, - captures: []const CaptureValue, - }, - declared_owned_captures: struct { - zir_index: TrackedInst.Index, - captures: CaptureValue.Slice, - }, - reified: struct { - zir_index: TrackedInst.Index, - type_hash: u64, - }, - }, -}; - -pub fn getUnionType( - ip: *InternPool, - gpa: Allocator, - io: Io, - tid: Zcu.PerThread.Id, - ini: UnionTypeInit, - /// If it is known that there is an existing type with this key which is outdated, - /// this is passed as `true`, and the type is replaced with one at a fresh index. - replace_existing: bool, -) Allocator.Error!WipNamespaceType.Result { - const key: Key = .{ .union_type = switch (ini.key) { - .declared => |d| .{ .declared = .{ - .zir_index = d.zir_index, - .captures = .{ .external = d.captures }, - } }, - .declared_owned_captures => |d| .{ .declared = .{ - .zir_index = d.zir_index, - .captures = .{ .owned = d.captures }, - } }, - .reified => |r| .{ .reified = .{ - .zir_index = r.zir_index, - .type_hash = r.type_hash, - } }, - } }; - var gop = if (replace_existing) - ip.putKeyReplace(io, tid, key) - else - try ip.getOrPutKey(gpa, io, tid, key); + nonexhaustive: bool, + /// Explicitly specified int tag type, or `.none` if the int tag type is inferred. + int_tag_type: Index, +}) Allocator.Error!WipContainerType.Result { + var gop = try ip.getOrPutKey(gpa, io, tid, .{ .enum_type = .{ .reified = .{ + .zir_index = ini.zir_index, + .type_hash = ini.type_hash, + } } }); defer gop.deinit(); if (gop == .existing) return .{ .existing = gop.existing }; const local = ip.getLocal(tid); const items = local.getMutableItems(gpa, io); - try items.ensureUnusedCapacity(1); const extra = local.getMutableExtra(gpa, io); + try items.ensureUnusedCapacity(1); - const align_elements_len = if (ini.flags.any_aligned_fields) (ini.fields_len + 3) / 4 else 0; - const align_element: u32 = @bitCast([1]u8{@intFromEnum(Alignment.none)} ** 4); - try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeUnion).@"struct".fields.len + - // TODO: fmt bug - // zig fmt: off - switch (ini.key) { - inline .declared, .declared_owned_captures => |d| @intFromBool(d.captures.len != 0) + d.captures.len, - .reified => 2, // type_hash: PackedU64 - } + - // zig fmt: on - ini.fields_len + // field types - align_elements_len); + const tag: Tag, const have_values: bool = if (ini.nonexhaustive) + .{ .type_enum_nonexhaustive, true } + else if (ini.int_tag_type != .none) + .{ .type_enum_explicit, true } + else + .{ .type_enum_auto, false }; - const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnion{ - .flags = .{ - .any_captures = switch (ini.key) { - inline .declared, .declared_owned_captures => |d| d.captures.len != 0, - .reified => false, - }, - .runtime_tag = ini.flags.runtime_tag, - .any_aligned_fields = ini.flags.any_aligned_fields, - .layout = ini.flags.layout, - .status = ini.flags.status, - .requires_comptime = ini.flags.requires_comptime, - .assumed_runtime_bits = ini.flags.assumed_runtime_bits, - .assumed_pointer_aligned = ini.flags.assumed_pointer_aligned, - .alignment = ini.flags.alignment, - .is_reified = switch (ini.key) { - .declared, .declared_owned_captures => false, - .reified => true, - }, + const field_name_map = try ip.addMap(gpa, io, tid, ini.fields_len); + errdefer local.mutate.maps.len -= 1; + + const field_value_map = if (have_values) try ip.addMap(gpa, io, tid, ini.fields_len) else undefined; + errdefer local.mutate.maps.len -= @intFromBool(have_values); + + try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeEnum).@"struct".fields.len + + 1 + // zir_index + 2 + // type_hash + @intFromBool(have_values) + // field_value_map + ini.fields_len + // field_name + (if (have_values) ini.fields_len else 0)); // field_value + + const extra_index = addExtraAssumeCapacity(extra, Tag.TypeEnum{ + .bits = .{ + .captures_len = .reified, + .want_layout = false, }, - .fields_len = ini.fields_len, - .size = std.math.maxInt(u32), - .padding = std.math.maxInt(u32), .name = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` - .tag_ty = ini.enum_tag_ty, - .zir_index = switch (ini.key) { - inline else => |x| x.zir_index, - }, + .int_tag_type = ini.int_tag_type, + .fields_len = ini.fields_len, + .field_name_map = field_name_map, }); - + extra.appendAssumeCapacity(.{@intFromEnum(ini.zir_index)}); // zir_index + _ = addExtraAssumeCapacity(extra, PackedU64.init(ini.type_hash)); // type_hash + if (have_values) extra.appendAssumeCapacity(.{@intFromEnum(field_value_map)}); // field_value_map + const field_names_start = extra.mutate.len; + extra.appendNTimesAssumeCapacity(.{@intFromEnum(NullTerminatedString.empty)}, ini.fields_len); // field_name + const field_values_start = extra.mutate.len; + if (have_values) extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_value items.appendAssumeCapacity(.{ - .tag = .type_union, + .tag = tag, .data = extra_index, }); + return .{ .wip = .{ + .index = gop.put(), + .tid = tid, + .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name").?, + .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name_nav").?, + .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "namespace").?, + .field_names = .{ .tid = tid, .start = field_names_start, .len = ini.fields_len }, + .field_types = undefined, + .field_values = if (have_values) + .{ .tid = tid, .start = field_values_start, .len = ini.fields_len } + else + undefined, + .field_aligns = undefined, + .field_is_comptime_bits = undefined, + } }; +} + +pub fn getGeneratedEnumTagType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct { + /// The union type for which this enum is a generated tag. + union_type: Index, + /// For `union(enum(T))` this is `.explicit`. Otherwise this is `.none`. + int_tag_mode: BackingTypeMode, + fields_len: u32, +}) Allocator.Error!WipContainerType.Result { + var gop = try ip.getOrPutKey(gpa, io, tid, .{ .enum_type = .{ .generated_union_tag = ini.union_type } }); + defer gop.deinit(); + if (gop == .existing) return .{ .existing = gop.existing }; - switch (ini.key) { - .declared => |d| if (d.captures.len != 0) { - extra.appendAssumeCapacity(.{@intCast(d.captures.len)}); - extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}); + const local = ip.getLocal(tid); + const items = local.getMutableItems(gpa, io); + const extra = local.getMutableExtra(gpa, io); + try items.ensureUnusedCapacity(1); + + const field_name_map = try ip.addMap(gpa, io, tid, ini.fields_len); + errdefer local.mutate.maps.len -= 1; + + const have_values = switch (ini.int_tag_mode) { + .explicit => true, + .auto => false, + }; + + const field_value_map = if (have_values) try ip.addMap(gpa, io, tid, ini.fields_len) else undefined; + errdefer local.mutate.maps.len -= @intFromBool(have_values); + + try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeEnum).@"struct".fields.len + + 1 + // owner_union + @intFromBool(have_values) + // field_value_map + ini.fields_len + // field_name + (if (have_values) ini.fields_len else 0)); // field_value + + const extra_index = addExtraAssumeCapacity(extra, Tag.TypeEnum{ + .bits = .{ + .captures_len = .generated_union_tag, + .want_layout = false, }, - .declared_owned_captures => |d| if (d.captures.len != 0) { - extra.appendAssumeCapacity(.{@intCast(d.captures.len)}); - extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))}); + .name = undefined, // set by `finish` + .name_nav = undefined, // set by `finish` + .namespace = undefined, // set by `finish` + .int_tag_type = .none, + .fields_len = ini.fields_len, + .field_name_map = field_name_map, + }); + extra.appendAssumeCapacity(.{@intFromEnum(ini.union_type)}); // owner_union + if (have_values) extra.appendAssumeCapacity(.{@intFromEnum(field_value_map)}); + extra.appendNTimesAssumeCapacity(.{@intFromEnum(NullTerminatedString.empty)}, ini.fields_len); // field_name + if (have_values) extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); // field_value + items.appendAssumeCapacity(.{ + .tag = switch (ini.int_tag_mode) { + .auto => .type_enum_auto, + .explicit => .type_enum_explicit, }, - .reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)), - } + .data = extra_index, + }); + return .{ .wip = .{ + .index = gop.put(), + .tid = tid, + .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name").?, + .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name_nav").?, + .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "namespace").?, + .field_names = undefined, + .field_types = undefined, + .field_values = undefined, + .field_aligns = undefined, + .field_is_comptime_bits = undefined, + } }; +} - // field types - if (ini.field_types.len > 0) { - assert(ini.field_types.len == ini.fields_len); - extra.appendSliceAssumeCapacity(.{@ptrCast(ini.field_types)}); - } else { - extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); - } +pub fn getDeclaredOpaqueType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct { + zir_index: TrackedInst.Index, + captures: []const CaptureValue, +}) Allocator.Error!WipContainerType.Result { + var gop = try ip.getOrPutKey(gpa, io, tid, .{ .opaque_type = .{ .declared = .{ + .zir_index = ini.zir_index, + .captures = .{ .external = ini.captures }, + } } }); + defer gop.deinit(); + if (gop == .existing) return .{ .existing = gop.existing }; - // field alignments - if (ini.flags.any_aligned_fields) { - extra.appendNTimesAssumeCapacity(.{align_element}, align_elements_len); - if (ini.field_aligns.len > 0) { - assert(ini.field_aligns.len == ini.fields_len); - @memcpy((Alignment.Slice{ - .tid = tid, - .start = @intCast(extra.mutate.len - align_elements_len), - .len = @intCast(ini.field_aligns.len), - }).get(ip), ini.field_aligns); - } - } else { - assert(ini.field_aligns.len == 0); - } + const local = ip.getLocal(tid); + const items = local.getMutableItems(gpa, io); + const extra = local.getMutableExtra(gpa, io); + try items.ensureUnusedCapacity(1); + try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeOpaque).@"struct".fields.len + ini.captures.len); + const extra_index = addExtraAssumeCapacity(extra, Tag.TypeOpaque{ + .zir_index = ini.zir_index, + .captures_len = @intCast(ini.captures.len), + .name = undefined, // set by `finish` + .name_nav = undefined, // set by `finish` + .namespace = undefined, // set by `finish` + }); + extra.appendSliceAssumeCapacity(.{@ptrCast(ini.captures)}); + items.appendAssumeCapacity(.{ + .tag = .type_opaque, + .data = extra_index, + }); return .{ .wip = .{ - .tid = tid, .index = gop.put(), - .type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name").?, - .name_nav_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name_nav").?, - .namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "namespace").?, + .tid = tid, + .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "name").?, + .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "name_nav").?, + .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "namespace").?, + .field_names = undefined, + .field_types = undefined, + .field_values = undefined, + .field_aligns = undefined, + .field_is_comptime_bits = undefined, } }; } -pub const WipNamespaceType = struct { - tid: Zcu.PerThread.Id, +pub const WipContainerType = struct { index: Index, - type_name_extra_index: u32, - namespace_extra_index: u32, - name_nav_extra_index: u32, + tid: Zcu.PerThread.Id, + type_name_index: u32, + name_nav_index: u32, + namespace_index: u32, + + // These fields are only populated when creating reified types, because reified types populate + // field information immediately, with type resolution only handling validation. This is in + // contrast to declared types, where field information is populated by the type resolution + // process evaluating ZIR expressions. + field_names: NullTerminatedString.Slice, + field_types: Index.Slice, + field_values: Index.Slice, + field_aligns: Alignment.Slice, + field_is_comptime_bits: LoadedStructType.ComptimeBits, pub fn setName( - wip: WipNamespaceType, + wip: WipContainerType, ip: *InternPool, type_name: NullTerminatedString, /// This should be the `Nav` we are named after if we use the `.parent` name strategy; `.none` otherwise. @@ -8941,257 +9032,58 @@ pub const WipNamespaceType = struct { ) void { const extra = ip.getLocalShared(wip.tid).extra.acquire(); const extra_items = extra.view().items(.@"0"); - extra_items[wip.type_name_extra_index] = @intFromEnum(type_name); - extra_items[wip.name_nav_extra_index] = @intFromEnum(name_nav); + extra_items[wip.type_name_index] = @intFromEnum(type_name); + extra_items[wip.name_nav_index] = @intFromEnum(name_nav); } pub fn finish( - wip: WipNamespaceType, + wip: WipContainerType, ip: *InternPool, namespace: NamespaceIndex, ) Index { const extra = ip.getLocalShared(wip.tid).extra.acquire(); const extra_items = extra.view().items(.@"0"); - extra_items[wip.namespace_extra_index] = @intFromEnum(namespace); + extra_items[wip.namespace_index] = @intFromEnum(namespace); return wip.index; } - pub fn cancel(wip: WipNamespaceType, ip: *InternPool, tid: Zcu.PerThread.Id) void { + pub fn cancel(wip: WipContainerType, ip: *InternPool, tid: Zcu.PerThread.Id) void { ip.remove(tid, wip.index); } pub const Result = union(enum) { - wip: WipNamespaceType, - existing: Index, - }; -}; - -pub const StructTypeInit = struct { - layout: std.builtin.Type.ContainerLayout, - fields_len: u32, - known_non_opv: bool, - requires_comptime: RequiresComptime, - any_comptime_fields: bool, - any_default_inits: bool, - inits_resolved: bool, - any_aligned_fields: bool, - key: union(enum) { - declared: struct { - zir_index: TrackedInst.Index, - captures: []const CaptureValue, - }, - declared_owned_captures: struct { - zir_index: TrackedInst.Index, - captures: CaptureValue.Slice, - }, - reified: struct { - zir_index: TrackedInst.Index, - type_hash: u64, - }, - }, -}; - -pub fn getStructType( - ip: *InternPool, - gpa: Allocator, - io: Io, - tid: Zcu.PerThread.Id, - ini: StructTypeInit, - /// If it is known that there is an existing type with this key which is outdated, - /// this is passed as `true`, and the type is replaced with one at a fresh index. - replace_existing: bool, -) Allocator.Error!WipNamespaceType.Result { - const key: Key = .{ .struct_type = switch (ini.key) { - .declared => |d| .{ .declared = .{ - .zir_index = d.zir_index, - .captures = .{ .external = d.captures }, - } }, - .declared_owned_captures => |d| .{ .declared = .{ - .zir_index = d.zir_index, - .captures = .{ .owned = d.captures }, - } }, - .reified => |r| .{ .reified = .{ - .zir_index = r.zir_index, - .type_hash = r.type_hash, - } }, - } }; - var gop = if (replace_existing) - ip.putKeyReplace(io, tid, key) - else - try ip.getOrPutKey(gpa, io, tid, key); - defer gop.deinit(); - if (gop == .existing) return .{ .existing = gop.existing }; - - const local = ip.getLocal(tid); - const items = local.getMutableItems(gpa, io); - const extra = local.getMutableExtra(gpa, io); - - const names_map = try ip.addMap(gpa, io, tid, ini.fields_len); - errdefer local.mutate.maps.len -= 1; - - const zir_index = switch (ini.key) { - inline else => |x| x.zir_index, - }; - - const is_extern = switch (ini.layout) { - .auto => false, - .@"extern" => true, - .@"packed" => { - try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeStructPacked).@"struct".fields.len + - // TODO: fmt bug - // zig fmt: off - switch (ini.key) { - inline .declared, .declared_owned_captures => |d| @intFromBool(d.captures.len != 0) + d.captures.len, - .reified => 2, // type_hash: PackedU64 - } + - // zig fmt: on - ini.fields_len + // types - ini.fields_len + // names - ini.fields_len); // inits - const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStructPacked{ - .name = undefined, // set by `finish` - .name_nav = undefined, // set by `finish` - .zir_index = zir_index, - .fields_len = ini.fields_len, - .namespace = undefined, // set by `finish` - .backing_int_ty = .none, - .names_map = names_map, - .flags = .{ - .any_captures = switch (ini.key) { - inline .declared, .declared_owned_captures => |d| d.captures.len != 0, - .reified => false, - }, - .field_inits_wip = false, - .inits_resolved = ini.inits_resolved, - .is_reified = switch (ini.key) { - .declared, .declared_owned_captures => false, - .reified => true, - }, - }, - }); - try items.append(.{ - .tag = if (ini.any_default_inits) .type_struct_packed_inits else .type_struct_packed, - .data = extra_index, - }); - switch (ini.key) { - .declared => |d| if (d.captures.len != 0) { - extra.appendAssumeCapacity(.{@intCast(d.captures.len)}); - extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}); - }, - .declared_owned_captures => |d| if (d.captures.len != 0) { - extra.appendAssumeCapacity(.{@intCast(d.captures.len)}); - extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))}); - }, - .reified => |r| { - _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)); - }, - } - extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); - extra.appendNTimesAssumeCapacity(.{@intFromEnum(OptionalNullTerminatedString.none)}, ini.fields_len); - if (ini.any_default_inits) { - extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); - } - return .{ .wip = .{ - .tid = tid, - .index = gop.put(), - .type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name").?, - .name_nav_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name_nav").?, - .namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?, - } }; - }, + wip: WipContainerType, + existing: Index, }; +}; - const align_elements_len = if (ini.any_aligned_fields) (ini.fields_len + 3) / 4 else 0; - const align_element: u32 = @bitCast([1]u8{@intFromEnum(Alignment.none)} ** 4); - const comptime_elements_len = if (ini.any_comptime_fields) (ini.fields_len + 31) / 32 else 0; +pub fn getUnion( + ip: *InternPool, + gpa: Allocator, + io: Io, + tid: Zcu.PerThread.Id, + un: Key.Union, +) Allocator.Error!Index { + assert(un.ty != .none); + assert(un.val != .none); + assert(ip.loadUnionType(un.ty).layout != .@"packed"); - try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeStruct).@"struct".fields.len + - // TODO: fmt bug - // zig fmt: off - switch (ini.key) { - inline .declared, .declared_owned_captures => |d| @intFromBool(d.captures.len != 0) + d.captures.len, - .reified => 2, // type_hash: PackedU64 - } + - // zig fmt: on - (ini.fields_len * 5) + // types, names, inits, runtime order, offsets - align_elements_len + comptime_elements_len + - 1); // names_map - const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStruct{ - .name = undefined, // set by `finish` - .name_nav = undefined, // set by `finish` - .zir_index = zir_index, - .namespace = undefined, // set by `finish` - .fields_len = ini.fields_len, - .size = std.math.maxInt(u32), - .flags = .{ - .any_captures = switch (ini.key) { - inline .declared, .declared_owned_captures => |d| d.captures.len != 0, - .reified => false, - }, - .is_extern = is_extern, - .known_non_opv = ini.known_non_opv, - .requires_comptime = ini.requires_comptime, - .assumed_runtime_bits = false, - .assumed_pointer_aligned = false, - .any_comptime_fields = ini.any_comptime_fields, - .any_default_inits = ini.any_default_inits, - .any_aligned_fields = ini.any_aligned_fields, - .alignment = .none, - .alignment_wip = false, - .field_types_wip = false, - .layout_wip = false, - .layout_resolved = false, - .field_inits_wip = false, - .inits_resolved = ini.inits_resolved, - .fully_resolved = false, - .is_reified = switch (ini.key) { - .declared, .declared_owned_captures => false, - .reified => true, - }, - }, - }); - try items.append(.{ - .tag = .type_struct, - .data = extra_index, + var gop = try ip.getOrPutKey(gpa, io, tid, .{ .un = un }); + defer gop.deinit(); + if (gop == .existing) return gop.existing; + const local = ip.getLocal(tid); + const items = local.getMutableItems(gpa, io); + const extra = local.getMutableExtra(gpa, io); + try items.ensureUnusedCapacity(1); + + items.appendAssumeCapacity(.{ + .tag = .union_value, + .data = try addExtra(extra, un), }); - switch (ini.key) { - .declared => |d| if (d.captures.len != 0) { - extra.appendAssumeCapacity(.{@intCast(d.captures.len)}); - extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}); - }, - .declared_owned_captures => |d| if (d.captures.len != 0) { - extra.appendAssumeCapacity(.{@intCast(d.captures.len)}); - extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))}); - }, - .reified => |r| { - _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)); - }, - } - extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); - extra.appendAssumeCapacity(.{@intFromEnum(names_map)}); - extra.appendNTimesAssumeCapacity(.{@intFromEnum(OptionalNullTerminatedString.none)}, ini.fields_len); - if (ini.any_default_inits) { - extra.appendNTimesAssumeCapacity(.{@intFromEnum(Index.none)}, ini.fields_len); - } - if (ini.any_aligned_fields) { - extra.appendNTimesAssumeCapacity(.{align_element}, align_elements_len); - } - if (ini.any_comptime_fields) { - extra.appendNTimesAssumeCapacity(.{0}, comptime_elements_len); - } - if (ini.layout == .auto) { - extra.appendNTimesAssumeCapacity(.{@intFromEnum(LoadedStructType.RuntimeOrder.unresolved)}, ini.fields_len); - } - extra.appendNTimesAssumeCapacity(.{std.math.maxInt(u32)}, ini.fields_len); - return .{ .wip = .{ - .tid = tid, - .index = gop.put(), - .type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name").?, - .name_nav_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name_nav").?, - .namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "namespace").?, - } }; + + return gop.put(); } pub const TupleTypeInit = struct { @@ -9252,10 +9144,7 @@ pub const GetFuncTypeKey = struct { /// `null` means generic. cc: ?std.builtin.CallingConvention = .auto, is_var_args: bool = false, - is_generic: bool = false, is_noinline: bool = false, - section_is_generic: bool = false, - addrspace_is_generic: bool = false, }; pub fn getFuncType( @@ -9293,7 +9182,6 @@ pub fn getFuncType( .is_var_args = key.is_var_args, .has_comptime_bits = key.comptime_bits != 0, .has_noalias_bits = key.noalias_bits != 0, - .is_generic = key.is_generic, .is_noinline = key.is_noinline, }, }); @@ -9427,7 +9315,7 @@ pub fn getFuncDecl( const func_decl_extra_index = addExtraAssumeCapacity(extra, Tag.FuncDecl{ .analysis = .{ - .is_analyzed = false, + .want_runtime_analysis = false, .branch_hint = .none, .is_noinline = key.is_noinline, .has_error_trace = false, @@ -9480,7 +9368,6 @@ pub const GetFuncDeclIesKey = struct { /// null means generic. cc: ?std.builtin.CallingConvention, is_var_args: bool, - is_generic: bool, is_noinline: bool, zir_body_inst: TrackedInst.Index, lbrace_line: u32, @@ -9538,7 +9425,7 @@ pub fn getFuncDeclIes( const func_decl_extra_index = addExtraAssumeCapacity(extra, Tag.FuncDecl{ .analysis = .{ - .is_analyzed = false, + .want_runtime_analysis = false, .branch_hint = .none, .is_noinline = key.is_noinline, .has_error_trace = false, @@ -9564,7 +9451,6 @@ pub fn getFuncDeclIes( .is_var_args = key.is_var_args, .has_comptime_bits = key.comptime_bits != 0, .has_noalias_bits = key.noalias_bits != 0, - .is_generic = key.is_generic, .is_noinline = key.is_noinline, }, }); @@ -9737,7 +9623,7 @@ pub fn getFuncInstance( const func_extra_index = addExtraAssumeCapacity(extra, Tag.FuncInstance{ .analysis = .{ - .is_analyzed = false, + .want_runtime_analysis = false, .branch_hint = .none, .is_noinline = arg.is_noinline, .has_error_trace = false, @@ -9838,7 +9724,7 @@ fn getFuncInstanceIes( const func_extra_index = addExtraAssumeCapacity(extra, Tag.FuncInstance{ .analysis = .{ - .is_analyzed = false, + .want_runtime_analysis = false, .branch_hint = .none, .is_noinline = arg.is_noinline, .has_error_trace = false, @@ -9864,7 +9750,6 @@ fn getFuncInstanceIes( .is_var_args = false, .has_comptime_bits = false, .has_noalias_bits = arg.noalias_bits != 0, - .is_generic = false, .is_noinline = arg.is_noinline, }, }); @@ -9972,444 +9857,6 @@ fn finishFuncInstance( ] = @intFromEnum(nav_index); } -pub const EnumTypeInit = struct { - has_values: bool, - tag_mode: LoadedEnumType.TagMode, - fields_len: u32, - key: union(enum) { - declared: struct { - zir_index: TrackedInst.Index, - captures: []const CaptureValue, - }, - declared_owned_captures: struct { - zir_index: TrackedInst.Index, - captures: CaptureValue.Slice, - }, - reified: struct { - zir_index: TrackedInst.Index, - type_hash: u64, - }, - }, -}; - -pub const WipEnumType = struct { - tid: Zcu.PerThread.Id, - index: Index, - tag_ty_index: u32, - type_name_extra_index: u32, - namespace_extra_index: u32, - name_nav_extra_index: u32, - names_map: MapIndex, - names_start: u32, - values_map: OptionalMapIndex, - values_start: u32, - - pub fn setName( - wip: WipEnumType, - ip: *InternPool, - type_name: NullTerminatedString, - /// This should be the `Nav` we are named after if we use the `.parent` name strategy; `.none` otherwise. - name_nav: Nav.Index.Optional, - ) void { - const extra = ip.getLocalShared(wip.tid).extra.acquire(); - const extra_items = extra.view().items(.@"0"); - extra_items[wip.type_name_extra_index] = @intFromEnum(type_name); - extra_items[wip.name_nav_extra_index] = @intFromEnum(name_nav); - } - - pub fn prepare( - wip: WipEnumType, - ip: *InternPool, - namespace: NamespaceIndex, - ) void { - const extra = ip.getLocalShared(wip.tid).extra.acquire(); - const extra_items = extra.view().items(.@"0"); - - extra_items[wip.namespace_extra_index] = @intFromEnum(namespace); - } - - pub fn setTagTy(wip: WipEnumType, ip: *InternPool, tag_ty: Index) void { - assert(ip.isIntegerType(tag_ty)); - const extra = ip.getLocalShared(wip.tid).extra.acquire(); - extra.view().items(.@"0")[wip.tag_ty_index] = @intFromEnum(tag_ty); - } - - pub const FieldConflict = struct { - kind: enum { name, value }, - prev_field_idx: u32, - }; - - /// Returns the already-existing field with the same name or value, if any. - /// If the enum is automatially numbered, `value` must be `.none`. - /// Otherwise, the type of `value` must be the integer tag type of the enum. - pub fn nextField(wip: WipEnumType, ip: *InternPool, name: NullTerminatedString, value: Index) ?FieldConflict { - const unwrapped_index = wip.index.unwrap(ip); - const extra_list = ip.getLocalShared(unwrapped_index.tid).extra.acquire(); - const extra_items = extra_list.view().items(.@"0"); - if (ip.addFieldName(extra_list, wip.names_map, wip.names_start, name)) |conflict| { - return .{ .kind = .name, .prev_field_idx = conflict }; - } - if (value == .none) { - assert(wip.values_map == .none); - return null; - } - assert(ip.typeOf(value) == @as(Index, @enumFromInt(extra_items[wip.tag_ty_index]))); - const map = wip.values_map.unwrap().?.get(ip); - const field_index = map.count(); - const indexes = extra_items[wip.values_start..][0..field_index]; - const adapter: Index.Adapter = .{ .indexes = @ptrCast(indexes) }; - const gop = map.getOrPutAssumeCapacityAdapted(value, adapter); - if (gop.found_existing) { - return .{ .kind = .value, .prev_field_idx = @intCast(gop.index) }; - } - extra_items[wip.values_start + field_index] = @intFromEnum(value); - return null; - } - - pub fn cancel(wip: WipEnumType, ip: *InternPool, tid: Zcu.PerThread.Id) void { - ip.remove(tid, wip.index); - } - - pub const Result = union(enum) { - wip: WipEnumType, - existing: Index, - }; -}; - -pub fn getEnumType( - ip: *InternPool, - gpa: Allocator, - io: Io, - tid: Zcu.PerThread.Id, - ini: EnumTypeInit, - /// If it is known that there is an existing type with this key which is outdated, - /// this is passed as `true`, and the type is replaced with one at a fresh index. - replace_existing: bool, -) Allocator.Error!WipEnumType.Result { - const key: Key = .{ .enum_type = switch (ini.key) { - .declared => |d| .{ .declared = .{ - .zir_index = d.zir_index, - .captures = .{ .external = d.captures }, - } }, - .declared_owned_captures => |d| .{ .declared = .{ - .zir_index = d.zir_index, - .captures = .{ .owned = d.captures }, - } }, - .reified => |r| .{ .reified = .{ - .zir_index = r.zir_index, - .type_hash = r.type_hash, - } }, - } }; - var gop = if (replace_existing) - ip.putKeyReplace(io, tid, key) - else - try ip.getOrPutKey(gpa, io, tid, key); - defer gop.deinit(); - if (gop == .existing) return .{ .existing = gop.existing }; - - const local = ip.getLocal(tid); - const items = local.getMutableItems(gpa, io); - try items.ensureUnusedCapacity(1); - const extra = local.getMutableExtra(gpa, io); - - const names_map = try ip.addMap(gpa, io, tid, ini.fields_len); - errdefer local.mutate.maps.len -= 1; - - switch (ini.tag_mode) { - .auto => { - assert(!ini.has_values); - try extra.ensureUnusedCapacity(@typeInfo(EnumAuto).@"struct".fields.len + - // TODO: fmt bug - // zig fmt: off - switch (ini.key) { - inline .declared, .declared_owned_captures => |d| d.captures.len, - .reified => 2, // type_hash: PackedU64 - } + - // zig fmt: on - ini.fields_len); // field types - - const extra_index = addExtraAssumeCapacity(extra, EnumAuto{ - .name = undefined, // set by `prepare` - .name_nav = undefined, // set by `prepare` - .captures_len = switch (ini.key) { - inline .declared, .declared_owned_captures => |d| @intCast(d.captures.len), - .reified => std.math.maxInt(u32), - }, - .namespace = undefined, // set by `prepare` - .int_tag_type = .none, // set by `prepare` - .fields_len = ini.fields_len, - .names_map = names_map, - .zir_index = switch (ini.key) { - inline else => |x| x.zir_index, - }.toOptional(), - }); - items.appendAssumeCapacity(.{ - .tag = .type_enum_auto, - .data = extra_index, - }); - switch (ini.key) { - .declared => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}), - .declared_owned_captures => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))}), - .reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)), - } - const names_start = extra.mutate.len; - _ = extra.addManyAsSliceAssumeCapacity(ini.fields_len); - return .{ .wip = .{ - .tid = tid, - .index = gop.put(), - .tag_ty_index = extra_index + std.meta.fieldIndex(EnumAuto, "int_tag_type").?, - .type_name_extra_index = extra_index + std.meta.fieldIndex(EnumAuto, "name").?, - .name_nav_extra_index = extra_index + std.meta.fieldIndex(EnumAuto, "name_nav").?, - .namespace_extra_index = extra_index + std.meta.fieldIndex(EnumAuto, "namespace").?, - .names_map = names_map, - .names_start = @intCast(names_start), - .values_map = .none, - .values_start = undefined, - } }; - }, - .explicit, .nonexhaustive => { - const values_map: OptionalMapIndex = if (!ini.has_values) .none else m: { - const values_map = try ip.addMap(gpa, io, tid, ini.fields_len); - break :m values_map.toOptional(); - }; - errdefer if (ini.has_values) { - local.mutate.maps.len -= 1; - }; - - try extra.ensureUnusedCapacity(@typeInfo(EnumExplicit).@"struct".fields.len + - // TODO: fmt bug - // zig fmt: off - switch (ini.key) { - inline .declared, .declared_owned_captures => |d| d.captures.len, - .reified => 2, // type_hash: PackedU64 - } + - // zig fmt: on - ini.fields_len + // field types - ini.fields_len * @intFromBool(ini.has_values)); // field values - - const extra_index = addExtraAssumeCapacity(extra, EnumExplicit{ - .name = undefined, // set by `prepare` - .name_nav = undefined, // set by `prepare` - .captures_len = switch (ini.key) { - inline .declared, .declared_owned_captures => |d| @intCast(d.captures.len), - .reified => std.math.maxInt(u32), - }, - .namespace = undefined, // set by `prepare` - .int_tag_type = .none, // set by `prepare` - .fields_len = ini.fields_len, - .names_map = names_map, - .values_map = values_map, - .zir_index = switch (ini.key) { - inline else => |x| x.zir_index, - }.toOptional(), - }); - items.appendAssumeCapacity(.{ - .tag = switch (ini.tag_mode) { - .auto => unreachable, - .explicit => .type_enum_explicit, - .nonexhaustive => .type_enum_nonexhaustive, - }, - .data = extra_index, - }); - switch (ini.key) { - .declared => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}), - .declared_owned_captures => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))}), - .reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)), - } - const names_start = extra.mutate.len; - _ = extra.addManyAsSliceAssumeCapacity(ini.fields_len); - const values_start = extra.mutate.len; - if (ini.has_values) { - _ = extra.addManyAsSliceAssumeCapacity(ini.fields_len); - } - return .{ .wip = .{ - .tid = tid, - .index = gop.put(), - .tag_ty_index = extra_index + std.meta.fieldIndex(EnumExplicit, "int_tag_type").?, - .type_name_extra_index = extra_index + std.meta.fieldIndex(EnumExplicit, "name").?, - .name_nav_extra_index = extra_index + std.meta.fieldIndex(EnumExplicit, "name_nav").?, - .namespace_extra_index = extra_index + std.meta.fieldIndex(EnumExplicit, "namespace").?, - .names_map = names_map, - .names_start = @intCast(names_start), - .values_map = values_map, - .values_start = @intCast(values_start), - } }; - }, - } -} - -const GeneratedTagEnumTypeInit = struct { - name: NullTerminatedString, - owner_union_ty: Index, - tag_ty: Index, - names: []const NullTerminatedString, - values: []const Index, - tag_mode: LoadedEnumType.TagMode, - parent_namespace: NamespaceIndex, -}; - -/// Creates an enum type which was automatically-generated as the tag type of a -/// `union` with no explicit tag type. Since this is only called once per union -/// type, it asserts that no matching type yet exists. -pub fn getGeneratedTagEnumType( - ip: *InternPool, - gpa: Allocator, - io: Io, - tid: Zcu.PerThread.Id, - ini: GeneratedTagEnumTypeInit, -) Allocator.Error!Index { - assert(ip.isUnion(ini.owner_union_ty)); - assert(ip.isIntegerType(ini.tag_ty)); - for (ini.values) |val| assert(ip.typeOf(val) == ini.tag_ty); - - const local = ip.getLocal(tid); - const items = local.getMutableItems(gpa, io); - try items.ensureUnusedCapacity(1); - const extra = local.getMutableExtra(gpa, io); - - const names_map = try ip.addMap(gpa, io, tid, ini.names.len); - errdefer local.mutate.maps.len -= 1; - ip.addStringsToMap(names_map, ini.names); - - const fields_len: u32 = @intCast(ini.names.len); - - // Predict the index the enum will live at so we can construct the namespace before releasing the shard's mutex. - const enum_index = Index.Unwrapped.wrap(.{ - .tid = tid, - .index = items.mutate.len, - }, ip); - const parent_namespace = ip.namespacePtr(ini.parent_namespace); - const namespace = try ip.createNamespace(gpa, io, tid, .{ - .parent = ini.parent_namespace.toOptional(), - .owner_type = enum_index, - .file_scope = parent_namespace.file_scope, - .generation = parent_namespace.generation, - }); - errdefer ip.destroyNamespace(tid, namespace); - - const prev_extra_len = extra.mutate.len; - switch (ini.tag_mode) { - .auto => { - try extra.ensureUnusedCapacity(@typeInfo(EnumAuto).@"struct".fields.len + - 1 + // owner_union - fields_len); // field names - items.appendAssumeCapacity(.{ - .tag = .type_enum_auto, - .data = addExtraAssumeCapacity(extra, EnumAuto{ - .name = ini.name, - .name_nav = .none, - .captures_len = 0, - .namespace = namespace, - .int_tag_type = ini.tag_ty, - .fields_len = fields_len, - .names_map = names_map, - .zir_index = .none, - }), - }); - extra.appendAssumeCapacity(.{@intFromEnum(ini.owner_union_ty)}); - extra.appendSliceAssumeCapacity(.{@ptrCast(ini.names)}); - }, - .explicit, .nonexhaustive => { - try extra.ensureUnusedCapacity(@typeInfo(EnumExplicit).@"struct".fields.len + - 1 + // owner_union - fields_len + // field names - ini.values.len); // field values - - const values_map: OptionalMapIndex = if (ini.values.len != 0) m: { - const map = try ip.addMap(gpa, io, tid, ini.values.len); - ip.addIndexesToMap(map, ini.values); - break :m map.toOptional(); - } else .none; - // We don't clean up the values map on error! - errdefer @compileError("error path leaks values_map"); - - items.appendAssumeCapacity(.{ - .tag = switch (ini.tag_mode) { - .explicit => .type_enum_explicit, - .nonexhaustive => .type_enum_nonexhaustive, - .auto => unreachable, - }, - .data = addExtraAssumeCapacity(extra, EnumExplicit{ - .name = ini.name, - .name_nav = .none, - .captures_len = 0, - .namespace = namespace, - .int_tag_type = ini.tag_ty, - .fields_len = fields_len, - .names_map = names_map, - .values_map = values_map, - .zir_index = .none, - }), - }); - extra.appendAssumeCapacity(.{@intFromEnum(ini.owner_union_ty)}); - extra.appendSliceAssumeCapacity(.{@ptrCast(ini.names)}); - extra.appendSliceAssumeCapacity(.{@ptrCast(ini.values)}); - }, - } - errdefer extra.mutate.len = prev_extra_len; - errdefer switch (ini.tag_mode) { - .auto => {}, - .explicit, .nonexhaustive => if (ini.values.len != 0) { - local.mutate.maps.len -= 1; - }, - }; - - var gop = try ip.getOrPutKey(gpa, io, tid, .{ .enum_type = .{ - .generated_tag = .{ .union_type = ini.owner_union_ty }, - } }); - defer gop.deinit(); - assert(gop.put() == enum_index); - return enum_index; -} - -pub const OpaqueTypeInit = struct { - zir_index: TrackedInst.Index, - captures: []const CaptureValue, -}; - -pub fn getOpaqueType( - ip: *InternPool, - gpa: Allocator, - io: Io, - tid: Zcu.PerThread.Id, - ini: OpaqueTypeInit, -) Allocator.Error!WipNamespaceType.Result { - var gop = try ip.getOrPutKey(gpa, io, tid, .{ .opaque_type = .{ .declared = .{ - .zir_index = ini.zir_index, - .captures = .{ .external = ini.captures }, - } } }); - defer gop.deinit(); - if (gop == .existing) return .{ .existing = gop.existing }; - - const local = ip.getLocal(tid); - const items = local.getMutableItems(gpa, io); - const extra = local.getMutableExtra(gpa, io); - try items.ensureUnusedCapacity(1); - - try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeOpaque).@"struct".fields.len + ini.captures.len); - const extra_index = addExtraAssumeCapacity(extra, Tag.TypeOpaque{ - .name = undefined, // set by `finish` - .name_nav = undefined, // set by `finish` - .namespace = undefined, // set by `finish` - .zir_index = ini.zir_index, - .captures_len = @intCast(ini.captures.len), - }); - items.appendAssumeCapacity(.{ - .tag = .type_opaque, - .data = extra_index, - }); - extra.appendSliceAssumeCapacity(.{@ptrCast(ini.captures)}); - return .{ - .wip = .{ - .tid = tid, - .index = gop.put(), - .type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "name").?, - .name_nav_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "name_nav").?, - .namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "namespace").?, - }, - }; -} - pub fn getIfExists(ip: *const InternPool, key: Key) ?Index { const full_hash = key.hash64(ip); const hash: u32 = @truncate(full_hash >> 32); @@ -10427,28 +9874,15 @@ pub fn getIfExists(ip: *const InternPool, key: Key) ?Index { } } -fn addStringsToMap( - ip: *InternPool, - map_index: MapIndex, - strings: []const NullTerminatedString, -) void { - const map = map_index.get(ip); - const adapter: NullTerminatedString.Adapter = .{ .strings = strings }; - for (strings) |string| { - const gop = map.getOrPutAssumeCapacityAdapted(string, adapter); - assert(!gop.found_existing); - } -} - -fn addIndexesToMap( +fn addStringsToMap( ip: *InternPool, map_index: MapIndex, - indexes: []const Index, + strings: []const NullTerminatedString, ) void { const map = map_index.get(ip); - const adapter: Index.Adapter = .{ .indexes = indexes }; - for (indexes) |index| { - const gop = map.getOrPutAssumeCapacityAdapted(index, adapter); + const adapter: NullTerminatedString.Adapter = .{ .strings = strings }; + for (strings) |string| { + const gop = map.getOrPutAssumeCapacityAdapted(string, adapter); assert(!gop.found_existing); } } @@ -10545,7 +9979,9 @@ fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 { Tag.TypePointer.PackedOffset, Tag.TypeUnion.Flags, Tag.TypeStruct.Flags, - Tag.TypeStructPacked.Flags, + Tag.TypeStructPacked.Bits, + Tag.TypeUnionPacked.Bits, + Tag.TypeEnum.Bits, => @bitCast(@field(item, field.name)), else => @compileError("bad field type: " ++ @typeName(field.type)), @@ -10607,8 +10043,10 @@ fn extraDataTrail(extra: Local.Extra, comptime T: type, index: u32) struct { dat Tag.TypePointer.PackedOffset, Tag.TypeUnion.Flags, Tag.TypeStruct.Flags, - Tag.TypeStructPacked.Flags, FuncAnalysis, + Tag.TypeStructPacked.Bits, + Tag.TypeUnionPacked.Bits, + Tag.TypeEnum.Bits, => @bitCast(extra_item), else => @compileError("bad field type: " ++ @typeName(field.type)), @@ -10786,7 +10224,7 @@ pub fn getCoerced( .int => |int| switch (ip.indexToKey(new_ty)) { .enum_type => return ip.get(gpa, io, tid, .{ .enum_tag = .{ .ty = new_ty, - .int = try ip.getCoerced(gpa, io, tid, val, ip.loadEnumType(new_ty).tag_ty), + .int = try ip.getCoerced(gpa, io, tid, val, ip.loadEnumType(new_ty).int_tag_type), } }), .ptr_type => switch (int.storage) { inline .u64, .i64 => |int_val| return ip.get(gpa, io, tid, .{ .ptr = .{ @@ -10795,7 +10233,6 @@ pub fn getCoerced( .byte_offset = @intCast(int_val), } }), .big_int => unreachable, // must be a usize - .lazy_align, .lazy_size => {}, }, else => if (ip.isIntegerType(new_ty)) return ip.getCoercedInts(gpa, io, tid, int, new_ty), @@ -10825,11 +10262,11 @@ pub fn getCoerced( const index = enum_type.nameIndex(ip, enum_literal).?; return ip.get(gpa, io, tid, .{ .enum_tag = .{ .ty = new_ty, - .int = if (enum_type.values.len != 0) - enum_type.values.get(ip)[index] + .int = if (enum_type.field_values.len != 0) + enum_type.field_values.get(ip)[index] else try ip.get(gpa, io, tid, .{ .int = .{ - .ty = enum_type.tag_ty, + .ty = enum_type.int_tag_type, .storage = .{ .u64 = index }, } }), } }); @@ -11193,10 +10630,78 @@ pub fn dump(ip: *const InternPool) void { const stderr = std.debug.lockStderr(&buffer); defer std.debug.unlockStderr(); const w = &stderr.file_writer.interface; + dumpDependencyStatsFallible(ip, w) catch return; dumpStatsFallible(ip, w, std.heap.page_allocator) catch return; dumpAllFallible(ip, w) catch return; } +fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void { + const dep_entries_len = ip.dep_entries.items.len - ip.free_dep_entries.items.len; + const src_hash_deps_len = ip.src_hash_deps.count(); + const nav_val_deps_len = ip.nav_val_deps.count(); + const nav_ty_deps_len = ip.nav_ty_deps.count(); + const func_ies_deps_len = ip.func_ies_deps.count(); + const type_layout_deps_len = ip.type_layout_deps.count(); + const struct_defaults_deps_len = ip.struct_defaults_deps.count(); + const zon_file_deps_len = ip.zon_file_deps.count(); + const embed_file_deps_len = ip.embed_file_deps.count(); + const namespace_deps_len = ip.namespace_deps.count(); + const namespace_name_deps_len = ip.namespace_name_deps.count(); + const dep_entries_size = dep_entries_len * @sizeOf(DepEntry); + const src_hash_deps_size = src_hash_deps_len * 8; + const nav_val_deps_size = nav_val_deps_len * 8; + const nav_ty_deps_size = nav_ty_deps_len * 8; + const func_ies_deps_size = func_ies_deps_len * 8; + const type_layout_deps_size = type_layout_deps_len * 8; + const struct_defaults_deps_size = struct_defaults_deps_len * 8; + const zon_file_deps_size = zon_file_deps_len * 8; + const embed_file_deps_size = embed_file_deps_len * 8; + const namespace_deps_size = namespace_deps_len * 8; + const namespace_name_deps_size = namespace_name_deps_len * (@sizeOf(NamespaceNameKey) + 4); + + try w.print( + \\InternPool dependencies: {d} bytes + \\ {d} entries: {d} bytes + \\ {d} src_hash: {d} bytes + \\ {d} nav_val: {d} bytes + \\ {d} nav_ty: {d} bytes + \\ {d} func_ies: {d} bytes + \\ {d} type_layout: {d} bytes + \\ {d} struct_defaults: {d} bytes + \\ {d} zon_file: {d} bytes + \\ {d} embed_file: {d} bytes + \\ {d} namespace: {d} bytes + \\ {d} namespace_name: {d} bytes + \\ + , .{ + dep_entries_size + src_hash_deps_size + nav_val_deps_size + nav_ty_deps_size + + func_ies_deps_size + type_layout_deps_size + struct_defaults_deps_size + zon_file_deps_size + + embed_file_deps_size + namespace_deps_size + namespace_name_deps_size, + dep_entries_len, + dep_entries_size, + src_hash_deps_len, + src_hash_deps_size, + nav_val_deps_len, + nav_val_deps_size, + nav_ty_deps_len, + nav_ty_deps_size, + func_ies_deps_len, + func_ies_deps_size, + type_layout_deps_len, + type_layout_deps_size, + struct_defaults_deps_len, + struct_defaults_deps_size, + zon_file_deps_len, + zon_file_deps_size, + embed_file_deps_len, + embed_file_deps_size, + namespace_deps_len, + namespace_deps_size, + namespace_name_deps_len, + namespace_name_deps_size, + }); +} + fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !void { var items_len: usize = 0; var extra_len: usize = 0; @@ -11211,10 +10716,10 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo const limbs_size = 8 * limbs_len; // TODO: map overhead size is not taken into account - const total_size = @sizeOf(InternPool) + items_size + extra_size + limbs_size; + const total_size = items_size + extra_size + limbs_size; - std.debug.print( - \\InternPool size: {d} bytes + try w.print( + \\InternPool values: {d} bytes \\ {d} items: {d} bytes \\ {d} extra: {d} bytes \\ {d} limbs: {d} bytes @@ -11235,6 +10740,8 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo }; var counts = std.AutoArrayHashMap(Tag, TagStats).init(arena); for (ip.locals) |*local| { + // Early check for length 0, because `view()` is invalid if capacity is 0 + if (local.mutate.items.len == 0) continue; const items = local.shared.items.view().slice(); const extra_list = local.shared.extra; const extra_items = extra_list.view().items(.@"0"); @@ -11266,98 +10773,137 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo break :b @sizeOf(Tag.ErrorSet) + (@sizeOf(u32) * info.names_len); }, .type_inferred_error_set => 0, - .type_enum_explicit, .type_enum_nonexhaustive => b: { - const info = extraData(extra_list, EnumExplicit, data); - var ints = @typeInfo(EnumExplicit).@"struct".fields.len; - if (info.zir_index == .none) ints += 1; - ints += if (info.captures_len != std.math.maxInt(u32)) - info.captures_len - else - @typeInfo(PackedU64).@"struct".fields.len; - ints += info.fields_len; - if (info.values_map != .none) ints += info.fields_len; - break :b @sizeOf(u32) * ints; - }, - .type_enum_auto => b: { - const info = extraData(extra_list, EnumAuto, data); - const ints = @typeInfo(EnumAuto).@"struct".fields.len + info.captures_len + info.fields_len; - break :b @sizeOf(u32) * ints; + .type_tuple => b: { + const info = extraData(extra_list, TypeTuple, data); + break :b @sizeOf(TypeTuple) + (@sizeOf(u32) * 2 * info.fields_len); }, - .type_opaque => b: { - const info = extraData(extra_list, Tag.TypeOpaque, data); - const ints = @typeInfo(Tag.TypeOpaque).@"struct".fields.len + info.captures_len; - break :b @sizeOf(u32) * ints; + .type_function => b: { + const info = extraData(extra_list, Tag.TypeFunction, data); + break :b @sizeOf(Tag.TypeFunction) + + (@sizeOf(Index) * info.params_len) + + (@as(u32, 4) * @intFromBool(info.flags.has_comptime_bits)) + + (@as(u32, 4) * @intFromBool(info.flags.has_noalias_bits)); }, + .type_struct => b: { + var n: usize = @typeInfo(Tag.TypeStruct).@"struct".fields.len; const extra = extraDataTrail(extra_list, Tag.TypeStruct, data); - const info = extra.data; - var ints: usize = @typeInfo(Tag.TypeStruct).@"struct".fields.len; - if (info.flags.any_captures) { - const captures_len = extra_items[extra.end]; - ints += 1 + captures_len; + switch (extra.data.flags.any_captures) { + .reified => n += 2, // type_hash: PackedU64 + .true => { + n += 1; // captures_len: u32 + n += extra_items[extra.end]; // capture: CaptureValue + }, + .false => {}, + } + n += extra.data.fields_len; // field_name: NullTerminatedString + n += extra.data.fields_len; // field_type: Index + if (extra.data.flags.any_field_defaults) { + n += extra.data.fields_len; // field_default: Index + } + if (extra.data.flags.any_field_aligns) { + n += (extra.data.fields_len + 3) / 4; // field_align: Alignment } - ints += info.fields_len; // types - ints += 1; // names_map - ints += info.fields_len; // names - if (info.flags.any_default_inits) - ints += info.fields_len; // inits - if (info.flags.any_aligned_fields) - ints += (info.fields_len + 3) / 4; // aligns - if (info.flags.any_comptime_fields) - ints += (info.fields_len + 31) / 32; // comptime bits - if (!info.flags.is_extern) - ints += info.fields_len; // runtime order - ints += info.fields_len; // offsets - break :b @sizeOf(u32) * ints; + if (extra.data.flags.any_comptime_fields) { + n += (extra.data.fields_len + 31) / 32; // field_is_comptime_bits: u32 + } + if (extra.data.flags.layout == .auto) { + n += extra.data.fields_len; // field_runtime_order: RuntimeOrder + } + n += extra.data.fields_len; // field_offset: u32 + break :b n * @sizeOf(u32); }, - .type_struct_packed => b: { + .type_struct_packed_auto, .type_struct_packed_explicit => b: { + var n: usize = @typeInfo(Tag.TypeStructPacked).@"struct".fields.len; const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, data); - const captures_len = if (extra.data.flags.any_captures) - extra_items[extra.end] - else - 0; - break :b @sizeOf(u32) * (@typeInfo(Tag.TypeStructPacked).@"struct".fields.len + - @intFromBool(extra.data.flags.any_captures) + captures_len + - extra.data.fields_len * 2); + switch (extra.data.bits.captures_len) { + .reified => n += 2, // type_hash: PackedU64 + _ => |len| n += @intFromEnum(len), // capture: CaptureValue + } + n += extra.data.fields_len; // field_name: NullTerminatedString + n += extra.data.fields_len; // field_type: Index + break :b n * @sizeOf(u32); }, - .type_struct_packed_inits => b: { + .type_struct_packed_auto_defaults, .type_struct_packed_explicit_defaults => b: { + var n: usize = @typeInfo(Tag.TypeStructPacked).@"struct".fields.len; const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, data); - const captures_len = if (extra.data.flags.any_captures) - extra_items[extra.end] - else - 0; - break :b @sizeOf(u32) * (@typeInfo(Tag.TypeStructPacked).@"struct".fields.len + - @intFromBool(extra.data.flags.any_captures) + captures_len + - extra.data.fields_len * 3); - }, - .type_tuple => b: { - const info = extraData(extra_list, TypeTuple, data); - break :b @sizeOf(TypeTuple) + (@sizeOf(u32) * 2 * info.fields_len); + switch (extra.data.bits.captures_len) { + .reified => n += 2, // type_hash: PackedU64 + _ => |len| n += @intFromEnum(len), // capture: CaptureValue + } + n += extra.data.fields_len; // field_name: NullTerminatedString + n += extra.data.fields_len; // field_type: Index + n += extra.data.fields_len; // field_default: Index + break :b n * @sizeOf(u32); }, - .type_union => b: { + var n: usize = @typeInfo(Tag.TypeUnion).@"struct".fields.len; const extra = extraDataTrail(extra_list, Tag.TypeUnion, data); - const captures_len = if (extra.data.flags.any_captures) - extra_items[extra.end] - else - 0; - const per_field = @sizeOf(u32); // field type - // 1 byte per field for alignment, rounded up to the nearest 4 bytes - const alignments = if (extra.data.flags.any_aligned_fields) - ((extra.data.fields_len + 3) / 4) * 4 - else - 0; - break :b @sizeOf(Tag.TypeUnion) + - 4 * (@intFromBool(extra.data.flags.any_captures) + captures_len) + - (extra.data.fields_len * per_field) + alignments; + switch (extra.data.flags.any_captures) { + .reified => n += 2, // type_hash: PackedU64 + .true => { + n += 1; // captures_len: u32 + n += extra_items[extra.end]; // capture: CaptureValue + }, + .false => {}, + } + n += extra.data.fields_len; // field_type: Index + if (extra.data.flags.any_field_aligns) { + n += (extra.data.fields_len + 3) / 4; // field_align: Alignment + } + break :b n * @sizeOf(u32); }, - - .type_function => b: { - const info = extraData(extra_list, Tag.TypeFunction, data); - break :b @sizeOf(Tag.TypeFunction) + - (@sizeOf(Index) * info.params_len) + - (@as(u32, 4) * @intFromBool(info.flags.has_comptime_bits)) + - (@as(u32, 4) * @intFromBool(info.flags.has_noalias_bits)); + .type_union_packed_auto, .type_union_packed_explicit => b: { + var n: usize = @typeInfo(Tag.TypeUnionPacked).@"struct".fields.len; + const extra = extraDataTrail(extra_list, Tag.TypeUnionPacked, data); + switch (extra.data.bits.captures_len) { + .reified => n += 2, // type_hash: PackedU64 + _ => |len| n += @intFromEnum(len), // capture: CaptureValue + } + n += extra.data.fields_len; // field_type: Index + break :b n * @sizeOf(u32); + }, + .type_enum_auto => b: { + var n: usize = @typeInfo(Tag.TypeEnum).@"struct".fields.len; + const extra = extraData(extra_list, Tag.TypeEnum, data); + switch (extra.bits.captures_len) { + .generated_union_tag => n += 1, // owner_union: Index + .reified => { + n += 1; // zir_index: TrackedInst.Index, + n += 2; // type_hash: PackedU64 + }, + _ => |len| { + n += 1; // zir_index: TrackedInst.Index, + n += @intFromEnum(len); // capture: CaptureValue + }, + } + n += extra.fields_len; // field_name: NullTerminatedString + break :b n * @sizeOf(u32); + }, + .type_enum_explicit, .type_enum_nonexhaustive => b: { + var n: usize = @typeInfo(Tag.TypeEnum).@"struct".fields.len; + const extra = extraData(extra_list, Tag.TypeEnum, data); + switch (extra.bits.captures_len) { + .generated_union_tag => n += 1, // owner_union: Index + .reified => { + n += 1; // zir_index: TrackedInst.Index, + n += 2; // type_hash: PackedU64 + }, + _ => |len| { + n += 1; // zir_index: TrackedInst.Index, + n += @intFromEnum(len); // capture: CaptureValue + }, + } + n += 1; // field_value_map: MapIndex + n += extra.fields_len; // field_name: NullTerminatedString + n += extra.fields_len; // field_value: Index + break :b n * @sizeOf(u32); + }, + .type_opaque => b: { + var n: usize = @typeInfo(Tag.TypeEnum).@"struct".fields.len; + const extra = extraData(extra_list, Tag.TypeOpaque, data); + n += extra.captures_len; // capture: CaptureValue + break :b n * @sizeOf(u32); }, .undef => 0, @@ -11393,8 +10939,6 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo break :b @sizeOf(Int) + int.limbs_len * @sizeOf(Limb); }, - .int_lazy_align, .int_lazy_size => @sizeOf(IntLazy), - .error_set_error, .error_union_error => @sizeOf(Key.Error), .error_union_payload => @sizeOf(Tag.TypeValue), .enum_literal => 0, @@ -11432,6 +10976,7 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo .func_coerced => @sizeOf(Tag.FuncCoerced), .only_possible_value => 0, .union_value => @sizeOf(Key.Union), + .bitpack => 2 * @sizeOf(u32), .memoized_call => b: { const info = extraData(extra_list, MemoizedCall, data); @@ -11458,6 +11003,8 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void { for (ip.locals, 0..) |*local, tid| { + // Early check for length 0, because `view()` is invalid if capacity is 0 + if (local.mutate.items.len == 0) continue; const items = local.shared.items.view(); for ( items.items(.tag)[0..local.mutate.items.len], @@ -11484,16 +11031,20 @@ fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void { .type_anyerror_union, .type_error_set, .type_inferred_error_set, + .type_tuple, + .type_function, + .type_struct, + .type_struct_packed_auto, + .type_struct_packed_explicit, + .type_struct_packed_auto_defaults, + .type_struct_packed_explicit_defaults, + .type_union, + .type_union_packed_auto, + .type_union_packed_explicit, + .type_enum_auto, .type_enum_explicit, .type_enum_nonexhaustive, - .type_enum_auto, .type_opaque, - .type_struct, - .type_struct_packed, - .type_struct_packed_inits, - .type_tuple, - .type_union, - .type_function, .undef, .ptr_nav, .ptr_comptime_alloc, @@ -11517,8 +11068,6 @@ fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void { .int_small, .int_positive, .int_negative, - .int_lazy_align, - .int_lazy_size, .error_set_error, .error_union_error, .error_union_payload, @@ -11542,6 +11091,7 @@ fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void { .func_instance, .func_coerced, .union_value, + .bitpack, .memoized_call, => try w.print("{d}", .{data}), @@ -11581,7 +11131,7 @@ pub fn dumpGenericInstancesFallible(ip: *const InternPool, allocator: Allocator, const info = extraData(extra_list, Tag.FuncInstance, data); const gop = try instances.getOrPut(arena, info.generic_owner); - if (!gop.found_existing) gop.value_ptr.* = .{}; + if (!gop.found_existing) gop.value_ptr.* = .empty; try gop.value_ptr.append( arena, @@ -11722,6 +11272,7 @@ pub fn createDeclNav( .analysis = .{ .namespace = namespace, .zir_index = zir_index, + .wanted = false, }, .status = .unresolved, })); @@ -12245,16 +11796,20 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index { .type_anyerror_union, .type_error_set, .type_inferred_error_set, + .type_tuple, + .type_function, + .type_struct, + .type_struct_packed_auto, + .type_struct_packed_explicit, + .type_struct_packed_auto_defaults, + .type_struct_packed_explicit_defaults, + .type_union, + .type_union_packed_auto, + .type_union_packed_explicit, .type_enum_auto, .type_enum_explicit, .type_enum_nonexhaustive, .type_opaque, - .type_struct, - .type_struct_packed, - .type_struct_packed_inits, - .type_tuple, - .type_union, - .type_function, => .type_type, .undef, @@ -12278,8 +11833,6 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index { .opt_payload, .error_union_payload, .int_small, - .int_lazy_align, - .int_lazy_size, .error_set_error, .error_union_error, .enum_tag, @@ -12293,6 +11846,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index { .bytes, .aggregate, .repeated, + .bitpack, => |t| { const extra_list = unwrapped_index.getExtra(ip); return @enumFromInt(extra_list.view().items(.@"0")[item.data + std.meta.fieldIndex(t.Payload(), "ty").?]); @@ -12389,20 +11943,6 @@ pub fn funcTypeReturnType(ip: *const InternPool, ty: Index) Index { ]); } -pub fn isNoReturn(ip: *const InternPool, ty: Index) bool { - switch (ty) { - .noreturn_type => return true, - else => { - const unwrapped_ty = ty.unwrap(ip); - const ty_item = unwrapped_ty.getItem(ip); - return switch (ty_item.tag) { - .type_error_set => unwrapped_ty.getExtra(ip).view().items(.@"0")[ty_item.data + std.meta.fieldIndex(Tag.ErrorSet, "names_len").?] == 0, - else => false, - }; - }, - } -} - pub fn isUndef(ip: *const InternPool, val: Index) bool { return val == .undef or val.unwrap(ip).getTag(ip) == .undef; } @@ -12613,22 +12153,26 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId { .type_inferred_error_set, => .error_set, - .type_enum_auto, - .type_enum_explicit, - .type_enum_nonexhaustive, - => .@"enum", - .simple_type => unreachable, // handled via Index tag above - .type_opaque => .@"opaque", + .type_tuple => .@"struct", .type_struct, - .type_struct_packed, - .type_struct_packed_inits, - .type_tuple, + .type_struct_packed_auto, + .type_struct_packed_explicit, + .type_struct_packed_auto_defaults, + .type_struct_packed_explicit_defaults, => .@"struct", - - .type_union => .@"union", + .type_union, + .type_union_packed_auto, + .type_union_packed_explicit, + => .@"union", + .type_enum_auto, + .type_enum_explicit, + .type_enum_nonexhaustive, + => .@"enum", + .type_opaque, + => .@"opaque", .type_function => .@"fn", @@ -12658,8 +12202,6 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId { .int_small, .int_positive, .int_negative, - .int_lazy_align, - .int_lazy_size, .error_set_error, .error_union_error, .error_union_payload, @@ -12684,6 +12226,7 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId { .bytes, .aggregate, .repeated, + .bitpack, // memoization, not types .memoized_call, => unreachable, @@ -12871,22 +12414,42 @@ pub fn unwrapCoercedFunc(ip: *const InternPool, index: Index) Index { }; } -/// Returns the already-existing field with the same name, if any. +/// Puts `name` into `names_slice` at the next index (that being the current length of `map`). +/// Also inserts the name into `map`. If there is an existing field with this name, its index +/// is returned. Otherwise, `null` is returned. pub fn addFieldName( ip: *InternPool, - extra: Local.Extra, - names_map: MapIndex, - names_start: u32, + names: NullTerminatedString.Slice, + map: MapIndex, name: NullTerminatedString, ) ?u32 { - const extra_items = extra.view().items(.@"0"); - const map = names_map.get(ip); - const field_index = map.count(); - const strings = extra_items[names_start..][0..field_index]; - const adapter: NullTerminatedString.Adapter = .{ .strings = @ptrCast(strings) }; - const gop = map.getOrPutAssumeCapacityAdapted(name, adapter); + const m = map.get(ip); + const field_idx = m.count(); + const names_slice = names.get(ip); + names_slice[field_idx] = name; + const adapter: NullTerminatedString.Adapter = .{ .strings = names_slice[0..field_idx] }; + const gop = m.getOrPutAssumeCapacityAdapted(name, adapter); if (gop.found_existing) return @intCast(gop.index); - extra_items[names_start + field_index] = @intFromEnum(name); + assert(gop.index == field_idx); + return null; +} + +/// Like `addFieldName`, but instead of adding a field name to a struct, union, or enum, adds a +/// field tag value for an enum. +pub fn addFieldTagValue( + ip: *InternPool, + values: Index.Slice, + map: MapIndex, + value: Index, +) ?u32 { + const m = map.get(ip); + const field_idx = m.count(); + const values_slice = values.get(ip); + values_slice[field_idx] = value; + const adapter: Index.Adapter = .{ .indexes = values_slice[0..field_idx] }; + const gop = m.getOrPutAssumeCapacityAdapted(value, adapter); + if (gop.found_existing) return @intCast(gop.index); + assert(gop.index == field_idx); return null; } @@ -13169,3 +12732,275 @@ const PackedCallingConvention = packed struct(u18) { }; } }; + +/// Asserts that `struct_type` is a non-packed struct type. +/// As well as calling this function, the caller must also populate these arrays: +/// * `field_types` +/// * `field_aligns` +/// * `field_runtime_order` +/// * `field_offsets` +pub fn resolveStructLayout( + ip: *InternPool, + io: Io, + struct_type: Index, + size: u32, + alignment: Alignment, + class: TypeClass, +) void { + const unwrapped_index = struct_type.unwrap(ip); + + const local = ip.getLocal(unwrapped_index.tid); + local.mutate.extra.mutex.lockUncancelable(io); + defer local.mutate.extra.mutex.unlock(io); + + const extra_items = local.shared.extra.view().items(.@"0"); + const item = unwrapped_index.getItem(ip); + assert(item.tag == .type_struct); + + extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "size").?] = size; + const flags: *Tag.TypeStruct.Flags = @ptrCast(&extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "flags").?]); + flags.class = class; + flags.alignment = alignment; +} + +/// Asserts that `union_type` is a non-packed union type. +/// As well as calling this function, the caller must also populate these arrays: +/// * `field_types` +/// * `field_aligns` +pub fn resolveUnionLayout( + ip: *InternPool, + io: Io, + union_type: Index, + enum_tag_type: Index, + class: TypeClass, + has_runtime_tag: bool, + size: u32, + padding: u32, + alignment: Alignment, +) void { + const unwrapped_index = union_type.unwrap(ip); + + const local = ip.getLocal(unwrapped_index.tid); + local.mutate.extra.mutex.lockUncancelable(io); + defer local.mutate.extra.mutex.unlock(io); + + const extra_items = local.shared.extra.view().items(.@"0"); + const item = unwrapped_index.getItem(ip); + assert(item.tag == .type_union); + + extra_items[item.data + std.meta.fieldIndex(Tag.TypeUnion, "enum_tag_type").?] = @intFromEnum(enum_tag_type); + extra_items[item.data + std.meta.fieldIndex(Tag.TypeUnion, "size").?] = size; + extra_items[item.data + std.meta.fieldIndex(Tag.TypeUnion, "padding").?] = padding; + const flags: *Tag.TypeUnion.Flags = @ptrCast(&extra_items[item.data + std.meta.fieldIndex(Tag.TypeUnion, "flags").?]); + flags.class = class; + flags.has_runtime_tag = has_runtime_tag; + flags.alignment = alignment; +} + +/// Asserts that `struct_type` is a packed struct type. +pub fn resolvePackedStructLayout( + ip: *InternPool, + io: Io, + struct_type: Index, + backing_int_type: Index, +) void { + const unwrapped_index = struct_type.unwrap(ip); + + const local = ip.getLocal(unwrapped_index.tid); + local.mutate.extra.mutex.lockUncancelable(io); + defer local.mutate.extra.mutex.unlock(io); + + const extra_items = local.shared.extra.view().items(.@"0"); + const item = unwrapped_index.getItem(ip); + switch (item.tag) { + .type_struct_packed_auto, + .type_struct_packed_explicit, + .type_struct_packed_auto_defaults, + .type_struct_packed_explicit_defaults, + => {}, + else => unreachable, + } + + extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "backing_int_type").?] = @intFromEnum(backing_int_type); +} + +/// Asserts that `union_type` is a packed union type. +pub fn resolvePackedUnionLayout( + ip: *InternPool, + io: Io, + union_type: Index, + enum_tag_type: Index, + backing_int_type: Index, +) void { + const unwrapped_index = union_type.unwrap(ip); + + const local = ip.getLocal(unwrapped_index.tid); + local.mutate.extra.mutex.lockUncancelable(io); + defer local.mutate.extra.mutex.unlock(io); + + const extra_items = local.shared.extra.view().items(.@"0"); + const item = unwrapped_index.getItem(ip); + switch (item.tag) { + .type_union_packed_auto, + .type_union_packed_explicit, + => {}, + else => unreachable, + } + + extra_items[item.data + std.meta.fieldIndex(Tag.TypeUnionPacked, "enum_tag_type").?] = @intFromEnum(enum_tag_type); + extra_items[item.data + std.meta.fieldIndex(Tag.TypeUnionPacked, "backing_int_type").?] = @intFromEnum(backing_int_type); +} + +/// Asserts that `enum_type` is an enum type. +pub fn resolveEnumLayout( + ip: *InternPool, + io: Io, + enum_type: Index, + int_tag_type: Index, +) void { + const unwrapped_index = enum_type.unwrap(ip); + + const local = ip.getLocal(unwrapped_index.tid); + local.mutate.extra.mutex.lockUncancelable(io); + defer local.mutate.extra.mutex.unlock(io); + + const extra_items = local.shared.extra.view().items(.@"0"); + const item = unwrapped_index.getItem(ip); + switch (item.tag) { + .type_enum_auto, + .type_enum_explicit, + .type_enum_nonexhaustive, + => {}, + else => unreachable, + } + + extra_items[item.data + std.meta.fieldIndex(Tag.TypeEnum, "int_tag_type").?] = @intFromEnum(int_tag_type); +} + +/// Sets the "want_layout" flag on the given struct, union, or enum type. Returns true if the flag +/// was *not* already set, meaning we have just discovered the first reference to this type's +/// layout. This flag is never reset to false, and exists purely as an optimization; for details, +/// see doc comments in `LoadedStructType`. +pub fn setWantTypeLayout(ip: *InternPool, io: Io, container_type: Index) bool { + const unwrapped_index = container_type.unwrap(ip); + + const local = ip.getLocal(unwrapped_index.tid); + local.mutate.extra.mutex.lockUncancelable(io); + defer local.mutate.extra.mutex.unlock(io); + + const extra_items = local.shared.extra.view().items(.@"0"); + const item = unwrapped_index.getItem(ip); + switch (item.tag) { + .type_struct_packed_auto, + .type_struct_packed_explicit, + .type_struct_packed_auto_defaults, + .type_struct_packed_explicit_defaults, + => { + const bits: *Tag.TypeStructPacked.Bits = @ptrCast(&extra_items[ + item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "bits").? + ]); + if (bits.want_layout) { + return false; + } else { + bits.want_layout = true; + return true; + } + }, + + .type_struct => { + const flags: *Tag.TypeStruct.Flags = @ptrCast(&extra_items[ + item.data + std.meta.fieldIndex(Tag.TypeStruct, "flags").? + ]); + if (flags.want_layout) { + return false; + } else { + flags.want_layout = true; + return true; + } + }, + + .type_union_packed_auto, + .type_union_packed_explicit, + => { + const bits: *Tag.TypeUnionPacked.Bits = @ptrCast(&extra_items[ + item.data + std.meta.fieldIndex(Tag.TypeUnionPacked, "bits").? + ]); + if (bits.want_layout) { + return false; + } else { + bits.want_layout = true; + return true; + } + }, + + .type_union => { + const flags: *Tag.TypeUnion.Flags = @ptrCast(&extra_items[ + item.data + std.meta.fieldIndex(Tag.TypeUnion, "flags").? + ]); + if (flags.want_layout) { + return false; + } else { + flags.want_layout = true; + return true; + } + }, + + .type_enum_auto, + .type_enum_explicit, + .type_enum_nonexhaustive, + => { + const bits: *Tag.TypeEnum.Bits = @ptrCast(&extra_items[ + item.data + std.meta.fieldIndex(Tag.TypeEnum, "bits").? + ]); + if (bits.want_layout) { + return false; + } else { + bits.want_layout = true; + return true; + } + }, + + else => unreachable, + } +} + +/// Like `setWantTypeLayout`, but for runtime analysis of a function body, using the +/// `FuncAnalysis.want_runtime_analysis` flag. +pub fn setWantRuntimeFnAnalysis(ip: *InternPool, io: Io, func_index: Index) bool { + const unwrapped_index = func_index.unwrap(ip); + + const local = ip.getLocal(unwrapped_index.tid); + local.mutate.extra.mutex.lockUncancelable(io); + defer local.mutate.extra.mutex.unlock(io); + + const a = funcAnalysisPtr(ip, func_index); + if (a.want_runtime_analysis) { + return false; + } else { + a.want_runtime_analysis = true; + return true; + } +} + +/// Like `setWantTypeLayout`, but for runtime analysis of a `Nav`, using the `Nav.analysis.wanted` flag. +pub fn setWantNavAnalysis(ip: *InternPool, io: Io, nav_index: Nav.Index) bool { + const unwrapped = nav_index.unwrap(ip); + + const local = ip.getLocal(unwrapped.tid); + local.mutate.extra.mutex.lockUncancelable(io); + defer local.mutate.extra.mutex.unlock(io); + + const navs = local.shared.navs.view(); + + if (navs.items(.analysis_namespace)[unwrapped.index] == .none) { + return false; + } + + const bits = &navs.items(.bits)[unwrapped.index]; + if (bits.want_analysis) { + return false; + } else { + bits.want_analysis = true; + return true; + } +} diff --git a/src/Package/Manifest.zig b/src/Package/Manifest.zig @@ -66,7 +66,7 @@ pub fn parse(gpa: Allocator, ast: *const Ast, rng: std.Random, options: ParseOpt .gpa = gpa, .ast = ast.*, .arena = arena_instance.allocator(), - .errors = .{}, + .errors = .empty, .name = undefined, .id = 0, @@ -74,10 +74,10 @@ pub fn parse(gpa: Allocator, ast: *const Ast, rng: std.Random, options: ParseOpt .version_node = undefined, .dependencies = .{}, .dependencies_node = .none, - .paths = .{}, + .paths = .empty, .allow_missing_paths_field = options.allow_missing_paths_field, .minimum_zig_version = null, - .buf = .{}, + .buf = .empty, }; defer p.buf.deinit(gpa); defer p.errors.deinit(gpa); diff --git a/src/Sema.zig b/src/Sema.zig @@ -173,13 +173,20 @@ const ComptimeAlloc = struct { runtime_index: RuntimeIndex, }; +/// Asserts that `ty` is not an OPV type. /// `src` may be `null` if `is_const` will be set. fn newComptimeAlloc(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, alignment: Alignment) !ComptimeAllocIndex { const pt = sema.pt; - const init_val = try sema.typeHasOnePossibleValue(ty) orelse try pt.undefValue(ty); + + switch (ty.classify(pt.zcu)) { + .no_possible_value => unreachable, + .one_possible_value => unreachable, + else => {}, + } + const idx = sema.comptime_allocs.items.len; try sema.comptime_allocs.append(sema.gpa, .{ - .val = .{ .interned = init_val.toIntern() }, + .val = .{ .interned = (try pt.undefValue(ty)).toIntern() }, .is_const = false, .src = src, .alignment = alignment, @@ -393,7 +400,7 @@ pub const Block = struct { /// The name of the current "context" for naming namespace types. /// The interpretation of this depends on the name strategy in ZIR, but the name /// is always incorporated into the type name somehow. - /// See `Sema.createTypeName`. + /// See `Sema.setTypeName`. type_name_ctx: InternPool.NullTerminatedString, /// Create a `LazySrcLoc` based on an `Offset` from the code being analyzed in this block. @@ -409,7 +416,7 @@ pub const Block = struct { return block.comptime_reason != null; } - fn builtinCallArgSrc(block: *Block, builtin_call_node: std.zig.Ast.Node.Offset, arg_index: u32) LazySrcLoc { + pub fn builtinCallArgSrc(block: *Block, builtin_call_node: std.zig.Ast.Node.Offset, arg_index: u32) LazySrcLoc { return block.src(.{ .node_offset_builtin_call_arg = .{ .builtin_call_node = builtin_call_node, .arg_index = arg_index, @@ -1082,7 +1089,7 @@ fn analyzeInlineBody( // This control flow goes further up the stack. return error.ComptimeBreak; } - return try sema.resolveInst(break_inst.data.@"break".operand); + return sema.resolveInst(break_inst.data.@"break".operand); } /// Like `analyzeInlineBody`, but if the body does not break with a value, returns @@ -1154,7 +1161,7 @@ fn analyzeBodyInner( }, inst }); } - const air_inst: Air.Inst.Ref = inst: switch (tags[@intFromEnum(inst)]) { + const air_ref: Air.Inst.Ref = inst: switch (tags[@intFromEnum(inst)]) { // zig fmt: off .alloc => try sema.zirAlloc(block, inst), .alloc_inferred => try sema.zirAllocInferred(block, true), @@ -1382,10 +1389,10 @@ fn analyzeBodyInner( const extended = datas[@intFromEnum(inst)].extended; break :ext switch (extended.opcode) { // zig fmt: off - .struct_decl => try sema.zirStructDecl( block, extended, inst), - .enum_decl => try sema.zirEnumDecl( block, extended, inst), - .union_decl => try sema.zirUnionDecl( block, extended, inst), - .opaque_decl => try sema.zirOpaqueDecl( block, extended, inst), + .struct_decl => try sema.zirStructDecl( block, inst), + .enum_decl => try sema.zirEnumDecl( block, inst), + .union_decl => try sema.zirUnionDecl( block, inst), + .opaque_decl => try sema.zirOpaqueDecl( block, inst), .tuple_decl => try sema.zirTupleDecl( block, extended), .this => try sema.zirThis( block, extended), .ret_addr => try sema.zirRetAddr( block, extended), @@ -1869,7 +1876,7 @@ fn analyzeBodyInner( const break_data = opt_break_data orelse break; if (inst == break_data.block_inst) { - break :blk try sema.resolveInst(break_data.operand); + break :blk sema.resolveInst(break_data.operand); } else { // `comptime_break_inst` preserved from `analyzeBodyInner` above. return error.ComptimeBreak; @@ -1890,7 +1897,7 @@ fn analyzeBodyInner( extra.end + then_body.len, extra.data.else_body_len, ); - const uncasted_cond = try sema.resolveInst(extra.data.condition); + const uncasted_cond = sema.resolveInst(extra.data.condition); const cond = try sema.coerce(block, .bool, uncasted_cond, cond_src); const cond_val = try sema.resolveConstDefinedValue( block, @@ -1916,7 +1923,7 @@ fn analyzeBodyInner( const operand_src = block.src(.{ .node_offset_try_operand = inst_data.src_node }); const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); - const err_union = try sema.resolveInst(extra.data.operand); + const err_union = sema.resolveInst(extra.data.operand); const err_union_ty = sema.typeOf(err_union); if (err_union_ty.zigTypeTag(zcu) != .error_union) { return sema.failWithOwnedErrorMsg(block, msg: { @@ -1942,7 +1949,7 @@ fn analyzeBodyInner( const operand_src = block.src(.{ .node_offset_try_operand = inst_data.src_node }); const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); - const operand = try sema.resolveInst(extra.data.operand); + const operand = sema.resolveInst(extra.data.operand); const err_union = try sema.analyzeLoad(block, src, operand, operand_src); const is_non_err_val = (try sema.resolveIsNonErrVal(block, operand_src, err_union)).?; if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, operand_src, null); @@ -1971,7 +1978,7 @@ fn analyzeBodyInner( const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].defer_err_code; const extra = sema.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data; const defer_body = sema.code.bodySlice(extra.index, extra.len); - const err_code = try sema.resolveInst(inst_data.err_code); + const err_code = sema.resolveInst(inst_data.err_code); try map.ensureSpaceForInstructions(sema.gpa, defer_body); map.putAssumeCapacity(extra.remapped_err_code, err_code); if (sema.analyzeBodyInner(block, defer_body)) { @@ -1987,18 +1994,35 @@ fn analyzeBodyInner( break :blk .void_value; }, }; - if (sema.isNoReturn(air_inst)) { - // We're going to assume that the body itself is noreturn, so let's ensure that now - assert(block.instructions.items.len > 0); - assert(sema.isNoReturn(block.instructions.items[block.instructions.items.len - 1].toRef())); - break; - } - map.putAssumeCapacity(inst, air_inst); + + const is_inferred_alloc = if (air_ref.toIndex()) |air_inst| switch (sema.air_instructions.items(.tag)[@intFromEnum(air_inst)]) { + .inferred_alloc, .inferred_alloc_comptime => true, + else => false, + } else false; + // We must resolve the layout of a type before creating a value of that type. Therefore, + // the layout of the type of `air_ref` must already be resolved. The call to `classify` + // doubles as an assertion of this. + if (!is_inferred_alloc) switch (sema.typeOf(air_ref).classify(zcu)) { + .no_possible_value => { + // The instruction result was noreturn, which should mean that the body itself now + // ends with a noreturn instruction. Let's confirm that. + const last_inst = block.instructions.items[block.instructions.items.len - 1]; + const last_inst_ty = sema.typeOf(last_inst.toRef()); + assert(last_inst_ty.classify(zcu) == .no_possible_value); + break; + }, + .one_possible_value => assert(air_ref.toInterned() != null), // the value should be comptime-known + .partially_comptime => assert(air_ref.toInterned() != null), // the value should be comptime-known + .fully_comptime => assert(air_ref.toInterned() != null), // the value should be comptime-known + .runtime => {}, + }; + + map.putAssumeCapacity(inst, air_ref); i += 1; } } -pub fn resolveInstAllowNone(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref { +fn resolveInstAllowNone(sema: *Sema, zir_ref: Zir.Inst.Ref) Air.Inst.Ref { if (zir_ref == .none) { return .none; } else { @@ -2006,7 +2030,7 @@ pub fn resolveInstAllowNone(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref { } } -pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref { +fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) Air.Inst.Ref { assert(zir_ref != .none); if (zir_ref.toIndex()) |i| { return sema.inst_map.get(i).?; @@ -2023,7 +2047,7 @@ fn resolveConstBool( zir_ref: Zir.Inst.Ref, reason: ComptimeReason, ) !bool { - const air_inst = try sema.resolveInst(zir_ref); + const air_inst = sema.resolveInst(zir_ref); const wanted_type: Type = .bool; const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason); @@ -2039,7 +2063,7 @@ fn resolveConstString( /// being comptime-resolved is that the block is being comptime-evaluated. reason: ?ComptimeReason, ) ![]u8 { - const air_inst = try sema.resolveInst(zir_ref); + const air_inst = sema.resolveInst(zir_ref); return sema.toConstString(block, src, air_inst, reason); } @@ -2066,7 +2090,7 @@ pub fn resolveConstStringIntern( zir_ref: Zir.Inst.Ref, reason: ComptimeReason, ) !InternPool.NullTerminatedString { - const air_inst = try sema.resolveInst(zir_ref); + const air_inst = sema.resolveInst(zir_ref); const wanted_type: Type = .slice_const_u8; const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason); @@ -2074,8 +2098,8 @@ pub fn resolveConstStringIntern( } fn resolveTypeOrPoison(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !?Type { - const air_inst = try sema.resolveInst(zir_ref); - const ty = try sema.analyzeAsType(block, src, air_inst); + const air_inst = sema.resolveInst(zir_ref); + const ty = try sema.analyzeAsType(block, src, .type, air_inst); if (ty.isGenericPoison()) return null; return ty; } @@ -2168,7 +2192,7 @@ fn genericPoisonReason(sema: *Sema, block: *Block, ref: Zir.Inst.Ref) GenericPoi // There are two cases here: the pointer type may already have been // generic poison, or it may have been an anyopaque pointer. const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; - const operand_ref = try sema.resolveInst(un_node.operand); + const operand_ref = sema.resolveInst(un_node.operand); const operand_val = operand_ref.toInterned() orelse return .unknown; if (operand_val == .generic_poison_type) { // The pointer was generic poison - keep looking. @@ -2190,15 +2214,16 @@ fn genericPoisonReason(sema: *Sema, block: *Block, ref: Zir.Inst.Ref) GenericPoi } } -fn analyzeAsType( +pub fn analyzeAsType( sema: *Sema, block: *Block, src: LazySrcLoc, + reason: std.zig.SimpleComptimeReason, air_inst: Air.Inst.Ref, ) !Type { const wanted_type: Type = .type; const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); - const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, .{ .simple = .type }); + const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, .{ .simple = reason }); return val.toType(); } @@ -2227,7 +2252,6 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) // var st: StackTrace = undefined; const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace); - try stack_trace_ty.resolveFields(pt); const st_ptr = try err_trace_block.addTy(.alloc, try pt.singleMutPtrType(stack_trace_ty)); // st.instruction_addresses = &addrs; @@ -2247,14 +2271,10 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) } /// Return the Value corresponding to a given AIR ref, or `null` if it refers to a runtime value. -fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value { +fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) ?Value { const zcu = sema.pt.zcu; assert(inst != .none); - if (try sema.typeHasOnePossibleValue(sema.typeOf(inst))) |opv| { - return opv; - } - if (inst.toInterned()) |ip_index| { const val: Value = .fromInterned(ip_index); assert(val.getVariable(zcu) == null); @@ -2267,12 +2287,21 @@ fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value { .inferred_alloc_comptime => unreachable, // assertion failure else => {}, } + // LLVM fails to eliminate this `classify` call in ReleaseFast, which hurts performance, so + // we must explicitly check for `std.debug.runtime_safety`. + if (std.debug.runtime_safety) switch (sema.typeOf(inst).classify(zcu)) { + .no_possible_value => unreachable, // values of this type do not exist + .one_possible_value => unreachable, // the value should be comptime-known + .partially_comptime => unreachable, // the value should be comptime-known + .fully_comptime => unreachable, // the value should be comptime-known + .runtime => {}, + }; return null; } } /// Like `resolveValue`, but emits an error if the value is not comptime-known. -fn resolveConstValue( +pub fn resolveConstValue( sema: *Sema, block: *Block, src: LazySrcLoc, @@ -2281,7 +2310,8 @@ fn resolveConstValue( /// being comptime-resolved is that the block is being comptime-evaluated. reason: ?ComptimeReason, ) CompileError!Value { - return try sema.resolveValue(inst) orelse { + assert(reason != null or block.isComptime()); + return sema.resolveValue(inst) orelse { return sema.failWithNeededComptime(block, src, reason); }; } @@ -2295,13 +2325,13 @@ fn resolveDefinedValue( ) CompileError!?Value { const pt = sema.pt; const zcu = pt.zcu; - const val = try sema.resolveValue(air_ref) orelse return null; + const val = sema.resolveValue(air_ref) orelse return null; if (val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, null); return val; } /// Like `resolveValue`, but emits an error if the value is not comptime-known or is undefined. -fn resolveConstDefinedValue( +pub fn resolveConstDefinedValue( sema: *Sema, block: *Block, src: LazySrcLoc, @@ -2315,11 +2345,6 @@ fn resolveConstDefinedValue( return val; } -/// Like `resolveValue`, but recursively resolves lazy values before returning. -fn resolveValueResolveLazy(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value { - return try sema.resolveLazyValue((try sema.resolveValue(inst)) orelse return null); -} - /// Value Tag may be `undef` or `variable`. pub fn resolveFinalDeclValue( sema: *Sema, @@ -2439,13 +2464,14 @@ fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, non fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { const pt = sema.pt; + const zcu = pt.zcu; const msg = msg: { const msg = try sema.errMsg(src, "type '{f}' does not support array initialization syntax", .{ ty.fmt(pt), }); errdefer msg.destroy(sema.gpa); - if (ty.isSlice(pt.zcu)) { - try sema.errNote(src, msg, "inferred array length is specified with an underscore: '[_]{f}'", .{ty.elemType2(pt.zcu).fmt(pt)}); + if (ty.isSlice(zcu)) { + try sema.errNote(src, msg, "inferred array length is specified with an underscore: '[_]{f}'", .{ty.childType(zcu).fmt(pt)}); } break :msg msg; }; @@ -2644,7 +2670,7 @@ pub fn fail( src: LazySrcLoc, comptime format: []const u8, args: anytype, -) CompileError { +) SemaError { const err_msg = try sema.errMsg(src, format, args); inline for (args) |arg| { if (@TypeOf(arg) == Type.Formatter) { @@ -2772,7 +2798,7 @@ fn resolveAlign( src: LazySrcLoc, zir_ref: Zir.Inst.Ref, ) !Alignment { - const air_ref = try sema.resolveInst(zir_ref); + const air_ref = sema.resolveInst(zir_ref); return sema.analyzeAsAlign(block, src, air_ref); } @@ -2784,7 +2810,7 @@ fn resolveInt( dest_ty: Type, reason: ComptimeReason, ) !u64 { - const air_ref = try sema.resolveInst(zir_ref); + const air_ref = sema.resolveInst(zir_ref); return sema.analyzeAsInt(block, src, air_ref, dest_ty, reason); } @@ -2798,27 +2824,26 @@ fn analyzeAsInt( ) !u64 { const coerced = try sema.coerce(block, dest_ty, air_ref, src); const val = try sema.resolveConstDefinedValue(block, src, coerced, reason); - return try val.toUnsignedIntSema(sema.pt); + return val.toUnsignedInt(sema.pt.zcu); } fn analyzeValueAsCallconv( sema: *Sema, block: *Block, src: LazySrcLoc, - unresolved_val: Value, + val: Value, ) !std.builtin.CallingConvention { - return interpretBuiltinType(sema, block, src, unresolved_val, std.builtin.CallingConvention); + return interpretBuiltinType(sema, block, src, val, std.builtin.CallingConvention); } fn interpretBuiltinType( sema: *Sema, block: *Block, src: LazySrcLoc, - unresolved_val: Value, + val: Value, comptime T: type, ) !T { - const resolved_val = try sema.resolveLazyValue(unresolved_val); - return resolved_val.interpret(T, sema.pt) catch |err| switch (err) { + return val.interpret(T, sema.pt) catch |err| switch (err) { error.OutOfMemory => |e| return e, error.UndefinedValue => return sema.failWithUseOfUndef(block, src, null), error.TypeMismatch => @panic("std.builtin is corrupt"), @@ -2864,7 +2889,7 @@ fn zirTupleDecl( field_ty.* = field_type.toIntern(); field_init.* = init: { if (zir_field_init != .none) { - const uncoerced_field_init = try sema.resolveInst(zir_field_init); + const uncoerced_field_init = sema.resolveInst(zir_field_init); const coerced_field_init = try sema.coerce(block, field_type, uncoerced_field_init, init_src); const field_init_val = try sema.resolveConstDefinedValue(block, init_src, coerced_field_init, .{ .simple = .tuple_field_default_value }); if (field_init_val.canMutateComptimeVarState(zcu)) { @@ -2913,7 +2938,13 @@ fn validateTupleFieldType( /// Given a ZIR extra index which points to a list of `Zir.Inst.Capture`, /// resolves this into a list of `InternPool.CaptureValue` allocated by `arena`. -fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: usize, captures_len: u32) ![]InternPool.CaptureValue { +fn getCaptures( + sema: *Sema, + block: *Block, + type_src: LazySrcLoc, + zir_captures: []const Zir.Inst.Capture, + zir_capture_names: []const Zir.NullTerminatedString, +) ![]InternPool.CaptureValue { const pt = sema.pt; const zcu = pt.zcu; const comp = zcu.comp; @@ -2924,41 +2955,38 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us const parent_ty: Type = .fromInterned(zcu.namespacePtr(block.namespace).owner_type); const parent_captures: InternPool.CaptureValue.Slice = parent_ty.getCaptures(zcu); - const captures = try sema.arena.alloc(InternPool.CaptureValue, captures_len); + const captures = try sema.arena.alloc(InternPool.CaptureValue, zir_captures.len); - for (sema.code.extra[extra_index..][0..captures_len], sema.code.extra[extra_index + captures_len ..][0..captures_len], captures) |raw, raw_name, *capture| { - const zir_capture: Zir.Inst.Capture = @bitCast(raw); - const zir_name: Zir.NullTerminatedString = @enumFromInt(raw_name); + for (zir_captures, zir_capture_names, captures) |zir_capture, zir_name, *capture| { const zir_name_slice = sema.code.nullTerminatedString(zir_name); capture.* = switch (zir_capture.unwrap()) { .nested => |parent_idx| parent_captures.get(ip)[parent_idx], - .instruction_load => |ptr_inst| InternPool.CaptureValue.wrap(capture: { - const ptr_ref = try sema.resolveInst(ptr_inst.toRef()); - const ptr_val = try sema.resolveValue(ptr_ref) orelse { - break :capture .{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() }; + .instruction_load => |ptr_inst| capture: { + const ptr_ref = sema.resolveInst(ptr_inst.toRef()); + const ptr_val = sema.resolveValue(ptr_ref) orelse { + break :capture .wrap(.{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() }); }; // TODO: better source location - const unresolved_loaded_val = try sema.pointerDeref(block, type_src, ptr_val, sema.typeOf(ptr_ref)) orelse { - break :capture .{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() }; + const loaded_val = try sema.pointerDeref(block, type_src, ptr_val, sema.typeOf(ptr_ref)) orelse { + break :capture .wrap(.{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() }); }; - const loaded_val = try sema.resolveLazyValue(unresolved_loaded_val); if (loaded_val.canMutateComptimeVarState(zcu)) { const field_name = try ip.getOrPutString(gpa, io, pt.tid, zir_name_slice, .no_embedded_nulls); return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", loaded_val); } - break :capture .{ .@"comptime" = loaded_val.toIntern() }; - }), - .instruction => |inst| InternPool.CaptureValue.wrap(capture: { - const air_ref = try sema.resolveInst(inst.toRef()); - if (try sema.resolveValueResolveLazy(air_ref)) |val| { + break :capture .wrap(.{ .@"comptime" = loaded_val.toIntern() }); + }, + .instruction => |inst| capture: { + const air_ref = sema.resolveInst(inst.toRef()); + if (sema.resolveValue(air_ref)) |val| { if (val.canMutateComptimeVarState(zcu)) { const field_name = try ip.getOrPutString(gpa, io, pt.tid, zir_name_slice, .no_embedded_nulls); return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", val); } - break :capture .{ .@"comptime" = val.toIntern() }; + break :capture .wrap(.{ .@"comptime" = val.toIntern() }); } - break :capture .{ .runtime = sema.typeOf(air_ref).toIntern() }; - }), + break :capture .wrap(.{ .runtime = sema.typeOf(air_ref).toIntern() }); + }, .decl_val => |str| capture: { const decl_name = try ip.getOrPutString( gpa, @@ -2968,7 +2996,7 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us .no_embedded_nulls, ); const nav = try sema.lookupIdentifier(block, decl_name); - break :capture InternPool.CaptureValue.wrap(.{ .nav_val = nav }); + break :capture .wrap(.{ .nav_val = nav }); }, .decl_ref => |str| capture: { const decl_name = try ip.getOrPutString( @@ -2987,952 +3015,335 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us return captures; } -fn zirStructDecl( +fn zirErrorSetDecl( sema: *Sema, - block: *Block, - extended: Zir.Inst.Extended.InstData, inst: Zir.Inst.Index, ) CompileError!Air.Inst.Ref { + const tracy = trace(@src()); + defer tracy.end(); + const pt = sema.pt; const zcu = pt.zcu; const comp = zcu.comp; const gpa = comp.gpa; const io = comp.io; - const ip = &zcu.intern_pool; - - const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); - const extra = sema.code.extraData(Zir.Inst.StructDecl, extended.operand); - const tracked_inst = try block.trackZir(inst); - const src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, - .offset = LazySrcLoc.Offset.nodeOffset(.zero), - }; + const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; + const extra = sema.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index); - var extra_index = extra.end; + var names: InferredErrorSet.NameMap = .{}; + try names.ensureUnusedCapacity(sema.arena, extra.data.fields_len); - const captures_len = if (small.has_captures_len) blk: { - const captures_len = sema.code.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - const fields_len = if (small.has_fields_len) blk: { - const fields_len = sema.code.extra[extra_index]; - extra_index += 1; - break :blk fields_len; - } else 0; - const decls_len = if (small.has_decls_len) blk: { - const decls_len = sema.code.extra[extra_index]; - extra_index += 1; - break :blk decls_len; - } else 0; + var extra_index: u32 = @intCast(extra.end); + const extra_index_end = extra_index + extra.data.fields_len; + while (extra_index < extra_index_end) : (extra_index += 1) { + const name_index: Zir.NullTerminatedString = @enumFromInt(sema.code.extra[extra_index]); + const name = sema.code.nullTerminatedString(name_index); + const name_ip = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls); + _ = try pt.getErrorValue(name_ip); + const result = names.getOrPutAssumeCapacity(name_ip); + assert(!result.found_existing); // verified in AstGen + } - const captures = try sema.getCaptures(block, src, extra_index, captures_len); - extra_index += captures_len * 2; + return Air.internedToRef((try pt.errorSetFromUnsortedNames(names.keys())).toIntern()); +} - if (small.has_backing_int) { - const backing_int_body_len = sema.code.extra[extra_index]; - extra_index += 1; // backing_int_body_len - if (backing_int_body_len == 0) { - extra_index += 1; // backing_int_ref - } else { - extra_index += backing_int_body_len; // backing_int_body_inst - } - } - - const struct_init: InternPool.StructTypeInit = .{ - .layout = small.layout, - .fields_len = fields_len, - .known_non_opv = small.known_non_opv, - .requires_comptime = if (small.known_comptime_only) .yes else .unknown, - .any_comptime_fields = small.any_comptime_fields, - .any_default_inits = small.any_default_inits, - .inits_resolved = false, - .any_aligned_fields = small.any_aligned_fields, - .key = .{ .declared = .{ - .zir_index = tracked_inst, - .captures = captures, - } }, - }; - const wip_ty = switch (try ip.getStructType(gpa, io, pt.tid, struct_init, false)) { - .existing => |ty| { - const new_ty = try pt.ensureTypeUpToDate(ty); +fn zirRetPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const tracy = trace(@src()); + defer tracy.end(); - // Make sure we update the namespace if the declaration is re-analyzed, to pick - // up on e.g. changed comptime decls. - try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu)); + const pt = sema.pt; + const zcu = pt.zcu; - try sema.declareDependency(.{ .interned = new_ty }); - try sema.addTypeReferenceEntry(src, new_ty); - return Air.internedToRef(new_ty); - }, - .wip => |wip| wip, - }; - errdefer wip_ty.cancel(ip, pt.tid); + const src = block.nodeOffset(sema.code.instructions.items(.data)[@intFromEnum(inst)].node); - const type_name = try sema.createTypeName( - block, - small.name_strategy, - "struct", - inst, - wip_ty.index, - ); - wip_ty.setName(ip, type_name.name, type_name.nav); + if (block.isComptime() or sema.fn_ret_ty.comptimeOnly(zcu)) { + return sema.analyzeComptimeAlloc(block, src, sema.fn_ret_ty, .none); + } - const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ - .parent = block.namespace.toOptional(), - .owner_type = wip_ty.index, - .file_scope = block.getFileScopeIndex(zcu), - .generation = zcu.generation, + const target = zcu.getTarget(); + const ptr_type = try pt.ptrType(.{ + .child = sema.fn_ret_ty.toIntern(), + .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, }); - errdefer pt.destroyNamespace(new_namespace_index); - if (pt.zcu.comp.config.incremental) { - try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst }); + if (block.inlining != null) { + // We are inlining a function call; this should be emitted as an alloc, not a ret_ptr. + // TODO when functions gain result location support, the inlining struct in + // Block should contain the return pointer, and we would pass that through here. + return block.addTy(.alloc, ptr_type); } - const decls = sema.code.bodySlice(extra_index, decls_len); - try pt.scanNamespace(new_namespace_index, decls); - - try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); - codegen_type: { - if (zcu.comp.config.use_llvm) break :codegen_type; - if (block.ownerModule().strip) break :codegen_type; - // This job depends on any resolve_type_fully jobs queued up before it. - zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); - try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); - } - try sema.declareDependency(.{ .interned = wip_ty.index }); - try sema.addTypeReferenceEntry(src, wip_ty.index); - if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); - return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); + return block.addTy(.ret_ptr, ptr_type); } -pub fn createTypeName( - sema: *Sema, - block: *Block, - name_strategy: Zir.Inst.NameStrategy, - anon_prefix: []const u8, - inst: ?Zir.Inst.Index, - /// This is used purely to give the type a unique name in the `anon` case. - type_index: InternPool.Index, -) CompileError!struct { - name: InternPool.NullTerminatedString, - nav: InternPool.Nav.Index.Optional, -} { - const pt = sema.pt; - const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = comp.gpa; - const io = comp.io; - const ip = &zcu.intern_pool; - - switch (name_strategy) { - .anon => {}, // handled after switch - .parent => return .{ - .name = block.type_name_ctx, - .nav = sema.owner.unwrap().nav_val.toOptional(), - }, - .func => func_strat: { - const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index).resolve(ip) orelse return error.AnalysisFail); - const zir_tags = sema.code.instructions.items(.tag); +fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const tracy = trace(@src()); + defer tracy.end(); - var aw: std.Io.Writer.Allocating = .init(gpa); - defer aw.deinit(); - const w = &aw.writer; - w.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory; + const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok; + const operand = sema.resolveInst(inst_data.operand); + return sema.analyzeRef(block, block.tokenOffset(inst_data.src_tok), operand, .none); +} - var arg_i: usize = 0; - for (fn_info.param_body) |zir_inst| switch (zir_tags[@intFromEnum(zir_inst)]) { - .param, .param_comptime, .param_anytype, .param_anytype_comptime => { - const arg = sema.inst_map.get(zir_inst).?; - // If this is being called in a generic function then analyzeCall will - // have already resolved the args and this will work. - // If not then this is a struct type being returned from a non-generic - // function and the name doesn't matter since it will later - // result in a compile error. - const arg_val = try sema.resolveValue(arg) orelse break :func_strat; // fall through to anon strat +fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { + const tracy = trace(@src()); + defer tracy.end(); - if (arg_i != 0) w.writeByte(',') catch return error.OutOfMemory; + const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; + const operand = sema.resolveInst(inst_data.operand); + const src = block.nodeOffset(inst_data.src_node); - // Limiting the depth here helps avoid type names getting too long, which - // in turn helps to avoid unreasonably long symbol names for namespaced - // symbols. Such names should ideally be human-readable, and additionally, - // some tooling may not support very long symbol names. - w.print("{f}", .{Value.fmtValueSemaFull(.{ - .val = arg_val, - .pt = pt, - .opt_sema = sema, - .depth = 1, - })}) catch return error.OutOfMemory; + return sema.ensureResultUsed(block, sema.typeOf(operand), src); +} - arg_i += 1; - continue; - }, - else => continue, +fn ensureResultUsed( + sema: *Sema, + block: *Block, + ty: Type, + src: LazySrcLoc, +) CompileError!void { + const pt = sema.pt; + const zcu = pt.zcu; + switch (ty.zigTypeTag(zcu)) { + .void, .noreturn => return, + .error_set => return sema.fail(block, src, "error set is ignored", .{}), + .error_union => { + const msg = msg: { + const msg = try sema.errMsg(src, "error union is ignored", .{}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); + break :msg msg; }; - - w.writeByte(')') catch return error.OutOfMemory; - return .{ - .name = try ip.getOrPutString(gpa, io, pt.tid, aw.written(), .no_embedded_nulls), - .nav = .none, + return sema.failWithOwnedErrorMsg(block, msg); + }, + else => { + const msg = msg: { + const msg = try sema.errMsg(src, "value of type '{f}' ignored", .{ty.fmt(pt)}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(src, msg, "all non-void values must be used", .{}); + try sema.errNote(src, msg, "to discard the value, assign it to '_'", .{}); + break :msg msg; }; + return sema.failWithOwnedErrorMsg(block, msg); }, - .dbg_var => { - // TODO: this logic is questionable. We ideally should be traversing the `Block` rather than relying on the order of AstGen instructions. - const ref = inst.?.toRef(); - const zir_tags = sema.code.instructions.items(.tag); - const zir_data = sema.code.instructions.items(.data); - for (@intFromEnum(inst.?)..zir_tags.len) |i| switch (zir_tags[i]) { - .dbg_var_ptr, .dbg_var_val => if (zir_data[i].str_op.operand == ref) { - return .{ - .name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.{s}", .{ - block.type_name_ctx.fmt(ip), zir_data[i].str_op.getStr(sema.code), - }, .no_embedded_nulls), - .nav = .none, - }; - }, - else => {}, + } +} + +fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { + const tracy = trace(@src()); + defer tracy.end(); + + const pt = sema.pt; + const zcu = pt.zcu; + const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; + const operand = sema.resolveInst(inst_data.operand); + const src = block.nodeOffset(inst_data.src_node); + const operand_ty = sema.typeOf(operand); + switch (operand_ty.zigTypeTag(zcu)) { + .error_set => return sema.fail(block, src, "error set is discarded", .{}), + .error_union => { + const msg = msg: { + const msg = try sema.errMsg(src, "error union is discarded", .{}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); + break :msg msg; }; - // fall through to anon strat + return sema.failWithOwnedErrorMsg(block, msg); }, + else => return, + } +} + +fn zirEnsureErrUnionPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { + const tracy = trace(@src()); + defer tracy.end(); + + const pt = sema.pt; + const zcu = pt.zcu; + const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; + const src = block.nodeOffset(inst_data.src_node); + const operand = sema.resolveInst(inst_data.operand); + const operand_ty = sema.typeOf(operand); + const err_union_ty = if (operand_ty.zigTypeTag(zcu) == .pointer) + operand_ty.childType(zcu) + else + operand_ty; + if (err_union_ty.zigTypeTag(zcu) != .error_union) return; + const payload_ty = err_union_ty.errorUnionPayload(zcu).zigTypeTag(zcu); + if (payload_ty != .void and payload_ty != .noreturn) { + const msg = msg: { + const msg = try sema.errMsg(src, "error union payload is ignored", .{}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(src, msg, "payload value can be explicitly ignored with '|_|'", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); } +} - // anon strat handling +fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const tracy = trace(@src()); + defer tracy.end(); - // It would be neat to have "struct:line:column" but this name has - // to survive incremental updates, where it may have been shifted down - // or up to a different line, but unchanged, and thus not unnecessarily - // semantically analyzed. - // TODO: that would be possible, by detecting line number changes and renaming - // types appropriately. However, `@typeName` becomes a problem then. If we remove - // that builtin from the language, we can consider this. + const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; + const src = block.nodeOffset(inst_data.src_node); + const object = sema.resolveInst(inst_data.operand); - return .{ - .name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}__{s}_{d}", .{ - block.type_name_ctx.fmt(ip), anon_prefix, @intFromEnum(type_index), - }, .no_embedded_nulls), - .nav = .none, - }; + return indexablePtrLen(sema, block, src, object); } -fn zirEnumDecl( +fn indexablePtrLen( sema: *Sema, block: *Block, - extended: Zir.Inst.Extended.InstData, - inst: Zir.Inst.Index, + src: LazySrcLoc, + object: Air.Inst.Ref, ) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - const pt = sema.pt; const zcu = pt.zcu; const comp = zcu.comp; const gpa = comp.gpa; const io = comp.io; - const ip = &zcu.intern_pool; - - const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small); - const extra = sema.code.extraData(Zir.Inst.EnumDecl, extended.operand); - var extra_index: usize = extra.end; - - const tracked_inst = try block.trackZir(inst); - const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) }; - - const tag_type_ref = if (small.has_tag_type) blk: { - const tag_type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); - extra_index += 1; - break :blk tag_type_ref; - } else .none; - - const captures_len = if (small.has_captures_len) blk: { - const captures_len = sema.code.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - - const body_len = if (small.has_body_len) blk: { - const body_len = sema.code.extra[extra_index]; - extra_index += 1; - break :blk body_len; - } else 0; - - const fields_len = if (small.has_fields_len) blk: { - const fields_len = sema.code.extra[extra_index]; - extra_index += 1; - break :blk fields_len; - } else 0; - - const decls_len = if (small.has_decls_len) blk: { - const decls_len = sema.code.extra[extra_index]; - extra_index += 1; - break :blk decls_len; - } else 0; - - const captures = try sema.getCaptures(block, src, extra_index, captures_len); - extra_index += captures_len * 2; - - const decls = sema.code.bodySlice(extra_index, decls_len); - extra_index += decls_len; - - const body = sema.code.bodySlice(extra_index, body_len); - extra_index += body.len; - - const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; - const body_end = extra_index; - extra_index += bit_bags_count; - - const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { - if (bag != 0) break true; - } else false; - - const enum_init: InternPool.EnumTypeInit = .{ - .has_values = any_values, - .tag_mode = if (small.nonexhaustive) - .nonexhaustive - else if (tag_type_ref == .none) - .auto - else - .explicit, - .fields_len = fields_len, - .key = .{ .declared = .{ - .zir_index = tracked_inst, - .captures = captures, - } }, - }; - const wip_ty = switch (try ip.getEnumType(gpa, io, pt.tid, enum_init, false)) { - .existing => |ty| { - const new_ty = try pt.ensureTypeUpToDate(ty); - - // Make sure we update the namespace if the declaration is re-analyzed, to pick - // up on e.g. changed comptime decls. - try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu)); - - try sema.declareDependency(.{ .interned = new_ty }); - try sema.addTypeReferenceEntry(src, new_ty); - - // Since this is an enum, it has to be resolved immediately. - // `ensureTypeUpToDate` has resolved the new type if necessary. - // We just need to check for resolution failures. - const ty_unit: AnalUnit = .wrap(.{ .type = new_ty }); - if (zcu.failed_analysis.contains(ty_unit) or zcu.transitive_failed_analysis.contains(ty_unit)) { - return error.AnalysisFail; - } - - return Air.internedToRef(new_ty); - }, - .wip => |wip| wip, - }; - - // Once this is `true`, we will not delete the decl or type even upon failure, since we - // have finished constructing the type and are in the process of analyzing it. - var done = false; - - errdefer if (!done) wip_ty.cancel(ip, pt.tid); - - const type_name = try sema.createTypeName( - block, - small.name_strategy, - "enum", - inst, - wip_ty.index, - ); - wip_ty.setName(ip, type_name.name, type_name.nav); - - const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ - .parent = block.namespace.toOptional(), - .owner_type = wip_ty.index, - .file_scope = block.getFileScopeIndex(zcu), - .generation = zcu.generation, - }); - errdefer if (!done) pt.destroyNamespace(new_namespace_index); - - try pt.scanNamespace(new_namespace_index, decls); - - try sema.declareDependency(.{ .interned = wip_ty.index }); - try sema.addTypeReferenceEntry(src, wip_ty.index); - - // We've finished the initial construction of this type, and are about to perform analysis. - // Set the namespace appropriately, and don't destroy anything on failure. - if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); - wip_ty.prepare(ip, new_namespace_index); - done = true; - - { - const tracked_unit = zcu.trackUnitSema(type_name.name.toSlice(ip), null); - defer tracked_unit.end(zcu); - try Sema.resolveDeclaredEnum( - pt, - wip_ty, - inst, - tracked_inst, - new_namespace_index, - type_name.name, - small, - body, - tag_type_ref, - any_values, - fields_len, - sema.code, - body_end, - ); - } - - codegen_type: { - if (zcu.comp.config.use_llvm) break :codegen_type; - if (block.ownerModule().strip) break :codegen_type; - // This job depends on any resolve_type_fully jobs queued up before it. - zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); - try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); - } - return Air.internedToRef(wip_ty.index); + const object_ty = sema.typeOf(object); + const is_pointer_to = object_ty.isSinglePointer(zcu); + const indexable_ty = if (is_pointer_to) object_ty.childType(zcu) else object_ty; + try sema.checkIndexable(block, src, indexable_ty); + const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls); + return sema.fieldVal(block, src, object, field_name, src); } -fn zirUnionDecl( +fn indexablePtrLenOrNone( sema: *Sema, block: *Block, - extended: Zir.Inst.Extended.InstData, - inst: Zir.Inst.Index, + src: LazySrcLoc, + operand: Air.Inst.Ref, ) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - const pt = sema.pt; const zcu = pt.zcu; const comp = zcu.comp; const gpa = comp.gpa; const io = comp.io; - const ip = &zcu.intern_pool; - - const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); - const extra = sema.code.extraData(Zir.Inst.UnionDecl, extended.operand); - var extra_index: usize = extra.end; - - const tracked_inst = try block.trackZir(inst); - const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) }; - - extra_index += @intFromBool(small.has_tag_type); - const captures_len = if (small.has_captures_len) blk: { - const captures_len = sema.code.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - extra_index += @intFromBool(small.has_body_len); - const fields_len = if (small.has_fields_len) blk: { - const fields_len = sema.code.extra[extra_index]; - extra_index += 1; - break :blk fields_len; - } else 0; - - const decls_len = if (small.has_decls_len) blk: { - const decls_len = sema.code.extra[extra_index]; - extra_index += 1; - break :blk decls_len; - } else 0; - - const captures = try sema.getCaptures(block, src, extra_index, captures_len); - extra_index += captures_len * 2; - - const union_init: InternPool.UnionTypeInit = .{ - .flags = .{ - .layout = small.layout, - .status = .none, - .runtime_tag = if (small.has_tag_type or small.auto_enum_tag) - .tagged - else if (small.layout != .auto) - .none - else switch (block.wantSafeTypes()) { - true => .safety, - false => .none, - }, - .any_aligned_fields = small.any_aligned_fields, - .requires_comptime = .unknown, - .assumed_runtime_bits = false, - .assumed_pointer_aligned = false, - .alignment = .none, - }, - .fields_len = fields_len, - .enum_tag_ty = .none, // set later - .field_types = &.{}, // set later - .field_aligns = &.{}, // set later - .key = .{ .declared = .{ - .zir_index = tracked_inst, - .captures = captures, - } }, - }; - const wip_ty = switch (try ip.getUnionType(gpa, io, pt.tid, union_init, false)) { - .existing => |ty| { - const new_ty = try pt.ensureTypeUpToDate(ty); - - // Make sure we update the namespace if the declaration is re-analyzed, to pick - // up on e.g. changed comptime decls. - try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu)); - - try sema.declareDependency(.{ .interned = new_ty }); - try sema.addTypeReferenceEntry(src, new_ty); - return Air.internedToRef(new_ty); - }, - .wip => |wip| wip, - }; - errdefer wip_ty.cancel(ip, pt.tid); - - const type_name = try sema.createTypeName( - block, - small.name_strategy, - "union", - inst, - wip_ty.index, - ); - wip_ty.setName(ip, type_name.name, type_name.nav); - - const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ - .parent = block.namespace.toOptional(), - .owner_type = wip_ty.index, - .file_scope = block.getFileScopeIndex(zcu), - .generation = zcu.generation, - }); - errdefer pt.destroyNamespace(new_namespace_index); - - if (pt.zcu.comp.config.incremental) { - try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst }); - } - - const decls = sema.code.bodySlice(extra_index, decls_len); - try pt.scanNamespace(new_namespace_index, decls); - - try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); - codegen_type: { - if (zcu.comp.config.use_llvm) break :codegen_type; - if (block.ownerModule().strip) break :codegen_type; - // This job depends on any resolve_type_fully jobs queued up before it. - zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); - try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); + const operand_ty = sema.typeOf(operand); + try checkMemOperand(sema, block, src, operand_ty); + switch (operand_ty.ptrSize(zcu)) { + .many, .c => return .none, + .one, .slice => {}, } - try sema.declareDependency(.{ .interned = wip_ty.index }); - try sema.addTypeReferenceEntry(src, wip_ty.index); - if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); - return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); + const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls); + return sema.fieldVal(block, src, operand, field_name, src); } -fn zirOpaqueDecl( +fn zirAllocExtended( sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, - inst: Zir.Inst.Index, ) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - const pt = sema.pt; const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = comp.gpa; - const io = comp.io; - const ip = &zcu.intern_pool; + const gpa = sema.gpa; + const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand); + const var_src = block.nodeOffset(extra.data.src_node); + const ty_src = block.src(.{ .node_offset_var_decl_ty = extra.data.src_node }); + const align_src = block.src(.{ .node_offset_var_decl_align = extra.data.src_node }); + const small: Zir.Inst.AllocExtended.Small = @bitCast(extended.small); - const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small); - const extra = sema.code.extraData(Zir.Inst.OpaqueDecl, extended.operand); var extra_index: usize = extra.end; - const tracked_inst = try block.trackZir(inst); - const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) }; - - const captures_len = if (small.has_captures_len) blk: { - const captures_len = sema.code.extra[extra_index]; + const var_ty: Type = if (small.has_type) blk: { + const type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); extra_index += 1; - break :blk captures_len; - } else 0; + break :blk try sema.resolveType(block, ty_src, type_ref); + } else undefined; - const decls_len = if (small.has_decls_len) blk: { - const decls_len = sema.code.extra[extra_index]; + const alignment = if (small.has_align) blk: { + const align_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); extra_index += 1; - break :blk decls_len; - } else 0; - - const captures = try sema.getCaptures(block, src, extra_index, captures_len); - extra_index += captures_len * 2; - - const opaque_init: InternPool.OpaqueTypeInit = .{ - .zir_index = tracked_inst, - .captures = captures, - }; - const wip_ty = switch (try ip.getOpaqueType(gpa, io, pt.tid, opaque_init)) { - .existing => |ty| { - // Make sure we update the namespace if the declaration is re-analyzed, to pick - // up on e.g. changed comptime decls. - try pt.ensureNamespaceUpToDate(Type.fromInterned(ty).getNamespaceIndex(zcu)); + break :blk try sema.resolveAlign(block, align_src, align_ref); + } else .none; - try sema.declareDependency(.{ .interned = ty }); - try sema.addTypeReferenceEntry(src, ty); - return Air.internedToRef(ty); - }, - .wip => |wip| wip, - }; - errdefer wip_ty.cancel(ip, pt.tid); + if (small.has_type) { + try sema.ensureLayoutResolved(var_ty, var_src, if (small.is_const) .constant else .variable); + if (block.isComptime() or small.is_comptime or var_ty.comptimeOnly(zcu)) { + return sema.analyzeComptimeAlloc(block, var_src, var_ty, alignment); + } + if (!small.is_const) { + try sema.validateVarType(block, ty_src, var_ty, false); + } + const target = pt.zcu.getTarget(); + if (sema.func_is_naked and var_ty.hasRuntimeBits(zcu)) { + const store_src = block.src(.{ .node_offset_store_ptr = extra.data.src_node }); + return sema.fail(block, store_src, "local variable in naked function", .{}); + } + const ptr_type = try pt.ptrType(.{ + .child = var_ty.toIntern(), + .flags = .{ + .alignment = alignment, + .address_space = target_util.defaultAddressSpace(target, .local), + }, + }); + const ptr = try block.addTy(.alloc, ptr_type); + if (small.is_const) { + const ptr_inst = ptr.toIndex().?; + try sema.maybe_comptime_allocs.put(gpa, ptr_inst, .{ .runtime_index = block.runtime_index }); + try sema.base_allocs.put(gpa, ptr_inst, ptr_inst); + } + return ptr; + } - const type_name = try sema.createTypeName( - block, - small.name_strategy, - "opaque", - inst, - wip_ty.index, - ); - wip_ty.setName(ip, type_name.name, type_name.nav); + if (block.isComptime() or small.is_comptime) { + const iac_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); + try sema.air_instructions.append(gpa, .{ + .tag = .inferred_alloc_comptime, + .data = .{ .inferred_alloc_comptime = .{ + .alignment = alignment, + .is_const = small.is_const, + .ptr = undefined, + } }, + }); + return iac_index.toRef(); + } - const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ - .parent = block.namespace.toOptional(), - .owner_type = wip_ty.index, - .file_scope = block.getFileScopeIndex(zcu), - .generation = zcu.generation, + const result_index = try block.addInstAsIndex(.{ + .tag = .inferred_alloc, + .data = .{ .inferred_alloc = .{ + .alignment = alignment, + .is_const = small.is_const, + } }, }); - errdefer pt.destroyNamespace(new_namespace_index); - - const decls = sema.code.bodySlice(extra_index, decls_len); - try pt.scanNamespace(new_namespace_index, decls); - - codegen_type: { - if (zcu.comp.config.use_llvm) break :codegen_type; - if (block.ownerModule().strip) break :codegen_type; - // This job depends on any resolve_type_fully jobs queued up before it. - zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); - try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); + try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); + if (small.is_const) { + try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index }); + try sema.base_allocs.put(gpa, result_index, result_index); } - try sema.addTypeReferenceEntry(src, wip_ty.index); - if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); - return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); + return result_index.toRef(); } -fn zirErrorSetDecl( - sema: *Sema, - inst: Zir.Inst.Index, -) CompileError!Air.Inst.Ref { +fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); - const pt = sema.pt; - const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = comp.gpa; - const io = comp.io; - - const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; - const extra = sema.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index); - - var names: InferredErrorSet.NameMap = .{}; - try names.ensureUnusedCapacity(sema.arena, extra.data.fields_len); - - var extra_index: u32 = @intCast(extra.end); - const extra_index_end = extra_index + extra.data.fields_len; - while (extra_index < extra_index_end) : (extra_index += 1) { - const name_index: Zir.NullTerminatedString = @enumFromInt(sema.code.extra[extra_index]); - const name = sema.code.nullTerminatedString(name_index); - const name_ip = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls); - _ = try pt.getErrorValue(name_ip); - const result = names.getOrPutAssumeCapacity(name_ip); - assert(!result.found_existing); // verified in AstGen - } - - return Air.internedToRef((try pt.errorSetFromUnsortedNames(names.keys())).toIntern()); + const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; + const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); + const var_src = block.nodeOffset(inst_data.src_node); + const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); + try sema.ensureLayoutResolved(var_ty, var_src, .variable); + return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none); } -fn zirRetPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - +fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const pt = sema.pt; + const zcu = pt.zcu; + const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; + const alloc = sema.resolveInst(inst_data.operand); + const alloc_ty = sema.typeOf(alloc); + const ptr_info = alloc_ty.ptrInfo(zcu); + const elem_ty: Type = .fromInterned(ptr_info.child); - const src = block.nodeOffset(sema.code.instructions.items(.data)[@intFromEnum(inst)].node); - - if (block.isComptime() or try sema.fn_ret_ty.comptimeOnlySema(pt)) { - try sema.fn_ret_ty.resolveFields(pt); - return sema.analyzeComptimeAlloc(block, src, sema.fn_ret_ty, .none); - } - - const target = pt.zcu.getTarget(); - const ptr_type = try pt.ptrTypeSema(.{ - .child = sema.fn_ret_ty.toIntern(), - .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, - }); - - if (block.inlining != null) { - // We are inlining a function call; this should be emitted as an alloc, not a ret_ptr. - // TODO when functions gain result location support, the inlining struct in - // Block should contain the return pointer, and we would pass that through here. - return block.addTy(.alloc, ptr_type); - } - - return block.addTy(.ret_ptr, ptr_type); -} - -fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - - const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok; - const operand = try sema.resolveInst(inst_data.operand); - return sema.analyzeRef(block, block.tokenOffset(inst_data.src_tok), operand); -} - -fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { - const tracy = trace(@src()); - defer tracy.end(); - - const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; - const operand = try sema.resolveInst(inst_data.operand); - const src = block.nodeOffset(inst_data.src_node); - - return sema.ensureResultUsed(block, sema.typeOf(operand), src); -} - -fn ensureResultUsed( - sema: *Sema, - block: *Block, - ty: Type, - src: LazySrcLoc, -) CompileError!void { - const pt = sema.pt; - const zcu = pt.zcu; - switch (ty.zigTypeTag(zcu)) { - .void, .noreturn => return, - .error_set => return sema.fail(block, src, "error set is ignored", .{}), - .error_union => { - const msg = msg: { - const msg = try sema.errMsg(src, "error union is ignored", .{}); - errdefer msg.destroy(sema.gpa); - try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - }, - else => { - const msg = msg: { - const msg = try sema.errMsg(src, "value of type '{f}' ignored", .{ty.fmt(pt)}); - errdefer msg.destroy(sema.gpa); - try sema.errNote(src, msg, "all non-void values must be used", .{}); - try sema.errNote(src, msg, "to discard the value, assign it to '_'", .{}); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - }, - } -} - -fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { - const tracy = trace(@src()); - defer tracy.end(); - - const pt = sema.pt; - const zcu = pt.zcu; - const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; - const operand = try sema.resolveInst(inst_data.operand); - const src = block.nodeOffset(inst_data.src_node); - const operand_ty = sema.typeOf(operand); - switch (operand_ty.zigTypeTag(zcu)) { - .error_set => return sema.fail(block, src, "error set is discarded", .{}), - .error_union => { - const msg = msg: { - const msg = try sema.errMsg(src, "error union is discarded", .{}); - errdefer msg.destroy(sema.gpa); - try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - }, - else => return, - } -} - -fn zirEnsureErrUnionPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { - const tracy = trace(@src()); - defer tracy.end(); - - const pt = sema.pt; - const zcu = pt.zcu; - const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; - const src = block.nodeOffset(inst_data.src_node); - const operand = try sema.resolveInst(inst_data.operand); - const operand_ty = sema.typeOf(operand); - const err_union_ty = if (operand_ty.zigTypeTag(zcu) == .pointer) - operand_ty.childType(zcu) - else - operand_ty; - if (err_union_ty.zigTypeTag(zcu) != .error_union) return; - const payload_ty = err_union_ty.errorUnionPayload(zcu).zigTypeTag(zcu); - if (payload_ty != .void and payload_ty != .noreturn) { - const msg = msg: { - const msg = try sema.errMsg(src, "error union payload is ignored", .{}); - errdefer msg.destroy(sema.gpa); - try sema.errNote(src, msg, "payload value can be explicitly ignored with '|_|'", .{}); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } -} - -fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - - const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; - const src = block.nodeOffset(inst_data.src_node); - const object = try sema.resolveInst(inst_data.operand); - - return indexablePtrLen(sema, block, src, object); -} - -fn indexablePtrLen( - sema: *Sema, - block: *Block, - src: LazySrcLoc, - object: Air.Inst.Ref, -) CompileError!Air.Inst.Ref { - const pt = sema.pt; - const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = comp.gpa; - const io = comp.io; - const object_ty = sema.typeOf(object); - const is_pointer_to = object_ty.isSinglePointer(zcu); - const indexable_ty = if (is_pointer_to) object_ty.childType(zcu) else object_ty; - try sema.checkIndexable(block, src, indexable_ty); - const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls); - return sema.fieldVal(block, src, object, field_name, src); -} - -fn indexablePtrLenOrNone( - sema: *Sema, - block: *Block, - src: LazySrcLoc, - operand: Air.Inst.Ref, -) CompileError!Air.Inst.Ref { - const pt = sema.pt; - const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = comp.gpa; - const io = comp.io; - const operand_ty = sema.typeOf(operand); - try checkMemOperand(sema, block, src, operand_ty); - switch (operand_ty.ptrSize(zcu)) { - .many, .c => return .none, - .one, .slice => {}, - } - const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls); - return sema.fieldVal(block, src, operand, field_name, src); -} - -fn zirAllocExtended( - sema: *Sema, - block: *Block, - extended: Zir.Inst.Extended.InstData, -) CompileError!Air.Inst.Ref { - const pt = sema.pt; - const gpa = sema.gpa; - const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand); - const var_src = block.nodeOffset(extra.data.src_node); - const ty_src = block.src(.{ .node_offset_var_decl_ty = extra.data.src_node }); - const align_src = block.src(.{ .node_offset_var_decl_align = extra.data.src_node }); - const small: Zir.Inst.AllocExtended.Small = @bitCast(extended.small); - - var extra_index: usize = extra.end; - - const var_ty: Type = if (small.has_type) blk: { - const type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); - extra_index += 1; - break :blk try sema.resolveType(block, ty_src, type_ref); - } else undefined; - - const alignment = if (small.has_align) blk: { - const align_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); - extra_index += 1; - break :blk try sema.resolveAlign(block, align_src, align_ref); - } else .none; - - if (block.isComptime() or small.is_comptime) { - if (small.has_type) { - return sema.analyzeComptimeAlloc(block, var_src, var_ty, alignment); - } else { - try sema.air_instructions.append(gpa, .{ - .tag = .inferred_alloc_comptime, - .data = .{ .inferred_alloc_comptime = .{ - .alignment = alignment, - .is_const = small.is_const, - .ptr = undefined, - } }, - }); - return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef(); - } - } - - if (small.has_type and try var_ty.comptimeOnlySema(pt)) { - return sema.analyzeComptimeAlloc(block, var_src, var_ty, alignment); - } - - if (small.has_type) { - if (!small.is_const) { - try sema.validateVarType(block, ty_src, var_ty, false); - } - const target = pt.zcu.getTarget(); - try var_ty.resolveLayout(pt); - if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) { - const store_src = block.src(.{ .node_offset_store_ptr = extra.data.src_node }); - return sema.fail(block, store_src, "local variable in naked function", .{}); - } - const ptr_type = try sema.pt.ptrTypeSema(.{ - .child = var_ty.toIntern(), - .flags = .{ - .alignment = alignment, - .address_space = target_util.defaultAddressSpace(target, .local), - }, - }); - const ptr = try block.addTy(.alloc, ptr_type); - if (small.is_const) { - const ptr_inst = ptr.toIndex().?; - try sema.maybe_comptime_allocs.put(gpa, ptr_inst, .{ .runtime_index = block.runtime_index }); - try sema.base_allocs.put(gpa, ptr_inst, ptr_inst); - } - return ptr; - } - - const result_index = try block.addInstAsIndex(.{ - .tag = .inferred_alloc, - .data = .{ .inferred_alloc = .{ - .alignment = alignment, - .is_const = small.is_const, - } }, - }); - try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); - if (small.is_const) { - try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index }); - try sema.base_allocs.put(gpa, result_index, result_index); - } - return result_index.toRef(); -} - -fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - - const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; - const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); - const var_src = block.nodeOffset(inst_data.src_node); - const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); - return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none); -} - -fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const pt = sema.pt; - const zcu = pt.zcu; - const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; - const alloc = try sema.resolveInst(inst_data.operand); - const alloc_ty = sema.typeOf(alloc); - const ptr_info = alloc_ty.ptrInfo(zcu); - const elem_ty: Type = .fromInterned(ptr_info.child); - - // If the alloc was created in a comptime scope, we already created a comptime alloc for it. - // However, if the final constructed value does not reference comptime-mutable memory, we wish - // to promote it to an anon decl. - already_ct: { - const ptr_val = try sema.resolveValue(alloc) orelse break :already_ct; + // If the alloc was created in a comptime scope, we already created a comptime alloc for it. + // However, if the final constructed value does not reference comptime-mutable memory, we wish + // to promote it to an anon decl. + already_ct: { + const ptr_val = sema.resolveValue(alloc) orelse break :already_ct; // If this was a comptime inferred alloc, then `storeToInferredAllocComptime` // might have already done our job and created an anon decl ref. @@ -3978,7 +3389,7 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro return sema.makePtrConst(block, Air.internedToRef(ptr_val)); } - if (try elem_ty.comptimeOnlySema(pt)) { + if (elem_ty.comptimeOnly(zcu)) { // The value was initialized through RLS, so we didn't detect the runtime condition earlier. // TODO: source location of runtime control flow const init_src = block.src(.{ .node_offset_var_decl_init = inst_data.src_node }); @@ -4001,20 +3412,23 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, const alloc_ty = resolved_alloc_ty orelse sema.typeOf(alloc); const ptr_info = alloc_ty.ptrInfo(zcu); const elem_ty: Type = .fromInterned(ptr_info.child); + elem_ty.assertHasLayout(zcu); const alloc_inst = alloc.toIndex() orelse return null; const comptime_info = sema.maybe_comptime_allocs.fetchRemove(alloc_inst) orelse return null; const stores = comptime_info.value.stores.items(.inst); + // If the elem type is OPV, no need to faff about with `stores`; just use the OPV. + if (try elem_ty.onePossibleValue(pt)) |opv| { + return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, opv.toIntern(), null, alloc_inst, comptime_info.value); + } + + // Since the elem type isn't OPV, there should have been at least one store. + assert(stores.len > 0); + // Since the entry existed in `maybe_comptime_allocs`, the allocation is comptime-known. // We will resolve and return its value. - // We expect to have emitted at least one store, unless the elem type is OPV. - if (stores.len == 0) { - const val = (try sema.typeHasOnePossibleValue(elem_ty)).?.toIntern(); - return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, null, alloc_inst, comptime_info.value); - } - // In general, we want to create a comptime alloc of the correct type and // apply the stores to that alloc in order. However, before going to all // that effort, let's optimize for the common case of a single store. @@ -4115,10 +3529,10 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, Air.Bin, tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_pl.payload, ).data; - const idx_val = (try sema.resolveValue(data.rhs)).?; + const idx_val = sema.resolveValue(data.rhs).?; break :blk .{ data.lhs, - .{ .elem = try idx_val.toUnsignedIntSema(pt) }, + .{ .elem = idx_val.toUnsignedInt(zcu) }, }; }, .bitcast => .{ @@ -4150,7 +3564,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, // If the payload is OPV, we must use that value instead of undef. const opt_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu); const payload_ty = opt_ty.optionalChild(zcu); - const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty); + const payload_val = try payload_ty.onePossibleValue(pt) orelse try pt.undefValue(payload_ty); const opt_val = try pt.intern(.{ .opt = .{ .ty = opt_ty.toIntern(), .val = payload_val.toIntern(), @@ -4163,7 +3577,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, // If the payload is OPV, we must use that value instead of undef. const eu_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu); const payload_ty = eu_ty.errorUnionPayload(zcu); - const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty); + const payload_val = try payload_ty.onePossibleValue(pt) orelse try pt.undefValue(payload_ty); const eu_val = try pt.intern(.{ .error_union = .{ .ty = eu_ty.toIntern(), .val = .{ .payload = payload_val.toIntern() }, @@ -4173,18 +3587,31 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, }, .field => |idx| ptr: { const maybe_union_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu); - if (zcu.typeToUnion(maybe_union_ty)) |union_obj| { + if (zcu.typeToUnion(maybe_union_ty)) |union_obj| if (union_obj.layout == .auto) { // As this is a union field, we must store to the pointer now to set the tag. // The payload value will be stored later, so undef is a sufficent payload for now. const payload_ty: Type = .fromInterned(union_obj.field_types.get(&zcu.intern_pool)[idx]); const payload_val = try pt.undefValue(payload_ty); - const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), idx); + const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_type), idx); const store_val = try pt.unionValue(maybe_union_ty, tag_val, payload_val); try sema.storePtrVal(block, .unneeded, .fromInterned(decl_parent_ptr), store_val, maybe_union_ty); - } + }; break :ptr (try Value.fromInterned(decl_parent_ptr).ptrField(idx, pt)).toIntern(); }, - .elem => |idx| (try Value.fromInterned(decl_parent_ptr).ptrElem(idx, pt)).toIntern(), + .elem => |idx| ptr: { + const parent_ptr_val: Value = .fromInterned(decl_parent_ptr); + if (parent_ptr_val.typeOf(zcu).childType(zcu).zigTypeTag(zcu) == .vector) { + const elem_ptr_ty: Type = .fromInterned(new_ptr_ty); + // Vectors are a bit weird; see logic in `elemPtrVector`. + if (elem_ptr_ty.ptrInfo(zcu).flags.vector_index != .none) { + break :ptr (try pt.getCoerced(parent_ptr_val, elem_ptr_ty)).toIntern(); + } else { + const bit_offset = idx * @divExact(elem_ptr_ty.childType(zcu).bitSize(zcu), 8); + break :ptr (try parent_ptr_val.getOffsetPtr(bit_offset, elem_ptr_ty, pt)).toIntern(); + } + } + break :ptr (try parent_ptr_val.ptrElem(idx, pt)).toIntern(); + }, }; try ptr_mapping.put(air_ptr, new_ptr); } @@ -4207,14 +3634,14 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, const tag_val: Value = .fromInterned(store_inst.data.bin_op.rhs.toInterned().?); const union_ty = union_ptr_val.typeOf(zcu).childType(zcu); const field_ty = union_ty.unionFieldType(tag_val, zcu).?; - if (try sema.typeHasOnePossibleValue(field_ty)) |payload_val| { + if (try field_ty.onePossibleValue(pt)) |payload_val| { const new_union_val = try pt.unionValue(union_ty, tag_val, payload_val); try sema.storePtrVal(block, .unneeded, union_ptr_val, new_union_val, union_ty); } }, .store, .store_safe => { const air_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?; - const store_val = (try sema.resolveValue(store_inst.data.bin_op.rhs)).?; + const store_val = sema.resolveValue(store_inst.data.bin_op.rhs).?; const new_ptr = ptr_mapping.get(air_ptr_inst).?; try sema.storePtrVal(block, .unneeded, .fromInterned(new_ptr), store_val, store_val.typeOf(zcu)); }, @@ -4289,7 +3716,7 @@ fn finishResolveComptimeKnownAllocPtr( fn makePtrTyConst(sema: *Sema, ptr_ty: Type) CompileError!Type { var ptr_info = ptr_ty.ptrInfo(sema.pt.zcu); ptr_info.flags.is_const = true; - return sema.pt.ptrTypeSema(ptr_info); + return sema.pt.ptrType(ptr_info); } fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref { @@ -4297,7 +3724,7 @@ fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Ai const const_ptr_ty = try sema.makePtrTyConst(alloc_ty); // Detect if a comptime value simply needs to have its type changed. - if (try sema.resolveValue(alloc)) |val| { + if (sema.resolveValue(alloc)) |val| { return Air.internedToRef((try sema.pt.getCoerced(val, const_ptr_ty)).toIntern()); } @@ -4326,21 +3753,23 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I defer tracy.end(); const pt = sema.pt; + const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); const var_src = block.nodeOffset(inst_data.src_node); const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); - if (block.isComptime() or try var_ty.comptimeOnlySema(pt)) { + try sema.ensureLayoutResolved(var_ty, var_src, .constant); + if (block.isComptime() or var_ty.comptimeOnly(zcu)) { return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none); } - if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) { + if (sema.func_is_naked and var_ty.hasRuntimeBits(zcu)) { const mut_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node }); return sema.fail(block, mut_src, "local variable in naked function", .{}); } - const target = pt.zcu.getTarget(); - const ptr_type = try pt.ptrTypeSema(.{ + const target = zcu.getTarget(); + const ptr_type = try pt.ptrType(.{ .child = var_ty.toIntern(), .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, }); @@ -4356,21 +3785,24 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai defer tracy.end(); const pt = sema.pt; + const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); const var_src = block.nodeOffset(inst_data.src_node); + const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); + try sema.ensureLayoutResolved(var_ty, var_src, .variable); if (block.isComptime()) { return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none); } - if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) { + if (sema.func_is_naked and var_ty.hasRuntimeBits(zcu)) { const store_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node }); return sema.fail(block, store_src, "local variable in naked function", .{}); } try sema.validateVarType(block, ty_src, var_ty, false); - const target = pt.zcu.getTarget(); - const ptr_type = try pt.ptrTypeSema(.{ + const target = zcu.getTarget(); + const ptr_type = try pt.ptrType(.{ .child = var_ty.toIntern(), .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, }); @@ -4424,14 +3856,15 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); - const ptr = try sema.resolveInst(inst_data.operand); + const ptr = sema.resolveInst(inst_data.operand); const ptr_inst = ptr.toIndex().?; const target = zcu.getTarget(); switch (sema.air_instructions.items(.tag)[@intFromEnum(ptr_inst)]) { .inferred_alloc_comptime => { - // The work was already done for us by `Sema.storeToInferredAllocComptime`. - // All we need to do is return the pointer. + // The work was already done for us by `Sema.storeToInferredAllocComptime`. Also, since + // we had a value of the exact correct type to store, the result type's layout must be + // already resolved. So all we need to do here is return the pointer. const iac = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].inferred_alloc_comptime; const resolved_ptr = iac.ptr; @@ -4450,7 +3883,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com }; if (zcu.intern_pool.isFuncBody(val)) { const ty: Type = .fromInterned(zcu.intern_pool.typeOf(val)); - if (try ty.fnHasRuntimeBitsSema(pt)) { + if (ty.fnHasRuntimeBits(zcu)) { const orig_fn_index = zcu.intern_pool.unwrapCoercedFunc(val); try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index })); try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index); @@ -4469,8 +3902,10 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com peer_val.* = bin_op.rhs; } const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_vals, .none); + // The layout of the peers is already resolved, so the layout of `final_elem_ty` is too. + final_elem_ty.assertHasLayout(zcu); - const final_ptr_ty = try pt.ptrTypeSema(.{ + const final_ptr_ty = try pt.ptrType(.{ .child = final_elem_ty.toIntern(), .flags = .{ .alignment = ia1.alignment, @@ -4484,21 +3919,16 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com const const_ptr_ty = try sema.makePtrTyConst(final_ptr_ty); const new_const_ptr = try pt.getCoerced(Value.fromInterned(ptr_val), const_ptr_ty); - // Unless the block is comptime, `alloc_inferred` always produces - // a runtime constant. The final inferred type needs to be - // fully resolved so it can be lowered in codegen. - try final_elem_ty.resolveFully(pt); - return Air.internedToRef(new_const_ptr.toIntern()); } - if (try final_elem_ty.comptimeOnlySema(pt)) { + if (final_elem_ty.comptimeOnly(zcu)) { // The alloc wasn't comptime-known per the above logic, so the // type cannot be comptime-only. // TODO: source location of runtime control flow return sema.fail(block, src, "value with comptime-only type '{f}' depends on runtime control flow", .{final_elem_ty.fmt(pt)}); } - if (sema.func_is_naked and try final_elem_ty.hasRuntimeBitsSema(pt)) { + if (sema.func_is_naked and final_elem_ty.hasRuntimeBits(zcu)) { const mut_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node }); return sema.fail(block, mut_src, "local variable in naked function", .{}); } @@ -4591,7 +4021,7 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const arg_len_uncoerced = if (zir_arg_pair[1] == .none) l: { // This argument is an indexable. - const object = try sema.resolveInst(zir_arg_pair[0]); + const object = sema.resolveInst(zir_arg_pair[0]); const object_ty = sema.typeOf(object); if (!object_ty.isIndexable(zcu)) { // Instead of using checkIndexable we customize this error. @@ -4612,8 +4042,8 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. break :l try sema.fieldVal(block, arg_src, object, try ip.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls), arg_src); } else l: { // This argument is a range. - const range_start = try sema.resolveInst(zir_arg_pair[0]); - const range_end = try sema.resolveInst(zir_arg_pair[1]); + const range_start = sema.resolveInst(zir_arg_pair[0]); + const range_end = sema.resolveInst(zir_arg_pair[1]); if (try sema.resolveDefinedValue(block, arg_src, range_start)) |start| { if (try sema.valuesEqual(start, .zero_usize, .usize)) break :l range_end; } @@ -4663,7 +4093,7 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const i: u32 = @intCast(i_usize); if (zir_arg_pair[0] == .none) continue; if (zir_arg_pair[1] != .none) continue; - const object = try sema.resolveInst(zir_arg_pair[0]); + const object = sema.resolveInst(zir_arg_pair[0]); const object_ty = sema.typeOf(object); const arg_src = block.src(.{ .for_input = .{ .for_node_offset = inst_data.src_node, @@ -4701,9 +4131,11 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. /// or error union pointed to, initializing these pointers along the way. /// Given a `*E!?T`, returns a (valid) `*T`. /// May invalidate already-stored payload data. +/// Asserts that the layout of the pointer child type is already resolved. fn optEuBasePtrInit(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, src: LazySrcLoc) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; + sema.typeOf(ptr).childType(zcu).assertHasLayout(zcu); var base_ptr = ptr; while (true) switch (sema.typeOf(base_ptr).childType(zcu).zigTypeTag(zcu)) { .error_union => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true), @@ -4716,8 +4148,10 @@ fn optEuBasePtrInit(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, src: LazySrcL fn zirOptEuBasePtrInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; - const ptr = try sema.resolveInst(un_node.operand); - return sema.optEuBasePtrInit(block, ptr, block.nodeOffset(un_node.src_node)); + const ptr = sema.resolveInst(un_node.operand); + const src = block.nodeOffset(un_node.src_node); + try sema.ensureLayoutResolved(sema.typeOf(ptr).childType(sema.pt.zcu), src, .init); + return sema.optEuBasePtrInit(block, ptr, src); } fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -4726,7 +4160,7 @@ fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; const src = block.nodeOffset(pl_node.src_node); const extra = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data; - const uncoerced_val = try sema.resolveInst(extra.rhs); + const uncoerced_val = sema.resolveInst(extra.rhs); const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, extra.lhs) orelse return uncoerced_val; const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu); assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction @@ -4812,7 +4246,7 @@ fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: boo if (is_ref) { var ptr_info = operand_ty.ptrInfo(zcu); ptr_info.child = eu_ty.toIntern(); - const eu_ptr_ty = try pt.ptrTypeSema(ptr_info); + const eu_ptr_ty = try pt.ptrType(ptr_info); return Air.internedToRef(eu_ptr_ty.toIntern()); } else { return Air.internedToRef(eu_ty.toIntern()); @@ -4842,7 +4276,7 @@ fn zirValidateConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(un_node.src_node); - const init_ref = try sema.resolveInst(un_node.operand); + const init_ref = sema.resolveInst(un_node.operand); if (!try sema.isComptimeKnown(init_ref)) { return sema.failWithNeededComptime(block, src, null); } @@ -4935,7 +4369,6 @@ fn validateArrayInitTy( return; }, .@"struct" => if (ty.isTuple(zcu)) { - try ty.resolveFields(pt); const array_len = ty.arrayLen(zcu); if (init_count > array_len) { return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{ @@ -4986,7 +4419,7 @@ fn zirValidatePtrStructInit( const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len); const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(instrs[0])].pl_node; const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; - const object_ptr = try sema.resolveInst(field_ptr_extra.lhs); + const object_ptr = sema.resolveInst(field_ptr_extra.lhs); const agg_ty = sema.typeOf(object_ptr).childType(zcu).optEuBaseType(zcu); switch (agg_ty.zigTypeTag(zcu)) { .@"struct" => return sema.validateStructInit( @@ -5097,12 +4530,16 @@ fn validateStructInit( errdefer if (root_msg) |msg| msg.destroy(sema.gpa); for (found_fields, 0..) |explicit, i_usize| { - if (explicit) continue; const i: u32 = @intCast(i_usize); - try struct_ty.resolveStructFieldInits(pt); - const default_val = struct_ty.structFieldDefaultValue(i, zcu); - if (default_val.toIntern() == .unreachable_value) { + if (explicit) continue; + if (struct_ty.structFieldIsComptime(i, zcu)) continue; + + if (!struct_ty.isTuple(zcu)) { + try sema.ensureStructDefaultsResolved(struct_ty, init_src); + } + + const default_val = struct_ty.structFieldDefaultValue(i, zcu) orelse { const field_name = struct_ty.structFieldName(i, zcu).unwrap() orelse { const template = "missing tuple field with index {d}"; if (root_msg) |msg| { @@ -5120,13 +4557,10 @@ fn validateStructInit( root_msg = try sema.errMsg(init_src, template, args); } continue; - } + }; const field_src = init_src; // TODO better source location - const default_field_ptr = if (struct_ty.isTuple(zcu)) - try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true) - else - try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty); + const default_field_ptr = try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty); try sema.checkKnownAllocPtr(block, struct_ptr, default_field_ptr); try sema.storePtr2(block, init_src, default_field_ptr, init_src, .fromValue(default_val), field_src, .store); } @@ -5151,7 +4585,7 @@ fn zirValidatePtrArrayInit( const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len); const first_elem_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(instrs[0])].pl_node; const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data; - const array_ptr = try sema.resolveInst(elem_ptr_extra.ptr); + const array_ptr = sema.resolveInst(elem_ptr_extra.ptr); const array_ty = sema.typeOf(array_ptr).childType(zcu).optEuBaseType(zcu); const array_len = array_ty.arrayLen(zcu); @@ -5166,11 +4600,9 @@ fn zirValidatePtrArrayInit( var root_msg: ?*Zcu.ErrorMsg = null; errdefer if (root_msg) |msg| msg.destroy(sema.gpa); - try array_ty.resolveStructFieldInits(pt); var i = instrs.len; while (i < array_len) : (i += 1) { - const default_val = array_ty.structFieldDefaultValue(i, zcu).toIntern(); - if (default_val == .unreachable_value) { + if (array_ty.structFieldDefaultValue(i, zcu) == null) { const template = "missing tuple field with index {d}"; if (root_msg) |msg| { try sema.errNote(init_src, msg, template, .{i}); @@ -5213,7 +4645,7 @@ fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); if (operand_ty.zigTypeTag(zcu) != .pointer) { @@ -5224,40 +4656,14 @@ fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr .slice => return sema.fail(block, src, "index syntax required for slice type '{f}'", .{operand_ty.fmt(pt)}), } - if ((try sema.typeHasOnePossibleValue(operand_ty.childType(zcu))) != null) { - // No need to validate the actual pointer value, we don't need it! - return; - } - - const elem_ty = operand_ty.elemType2(zcu); - if (try sema.resolveValue(operand)) |val| { - if (val.isUndef(zcu)) { + if (sema.resolveValue(operand)) |val| { + // Error for deref of undef pointer, unless the pointee is OPV in which case it's legal. + if (val.isUndef(zcu) and operand_ty.childType(zcu).classify(zcu) != .one_possible_value) { return sema.fail(block, src, "cannot dereference undefined value", .{}); } - } else if (try elem_ty.comptimeOnlySema(pt)) { - const msg = msg: { - const msg = try sema.errMsg( - src, - "values of type '{f}' must be comptime-known, but operand value is runtime-known", - .{elem_ty.fmt(pt)}, - ); - errdefer msg.destroy(sema.gpa); - - try sema.explainWhyTypeIsComptime(msg, src, elem_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); } } -fn typeIsDestructurable(ty: Type, zcu: *const Zcu) bool { - return switch (ty.zigTypeTag(zcu)) { - .array, .vector => true, - .@"struct" => ty.isTuple(zcu), - else => false, - }; -} - fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { const pt = sema.pt; const zcu = pt.zcu; @@ -5265,17 +4671,17 @@ fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp const extra = sema.code.extraData(Zir.Inst.ValidateDestructure, inst_data.payload_index).data; const src = block.nodeOffset(inst_data.src_node); const destructure_src = block.nodeOffset(extra.destructure_node); - const operand = try sema.resolveInst(extra.operand); + const operand = sema.resolveInst(extra.operand); const operand_ty = sema.typeOf(operand); - if (!typeIsDestructurable(operand_ty, zcu)) { + if (!operand_ty.destructurable(zcu)) { return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(src, "type '{f}' cannot be destructured", .{operand_ty.fmt(pt)}); errdefer msg.destroy(sema.gpa); try sema.errNote(destructure_src, msg, "result destructured here", .{}); if (operand_ty.zigTypeTag(pt.zcu) == .error_union) { const base_op_ty = operand_ty.errorUnionPayload(zcu); - if (typeIsDestructurable(base_op_ty, zcu)) + if (base_op_ty.destructurable(zcu)) try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); } break :msg msg; @@ -5373,7 +4779,7 @@ fn failWithBadUnionFieldAccess( return sema.failWithOwnedErrorMsg(block, msg); } -fn addDeclaredHereNote(sema: *Sema, parent: *Zcu.ErrorMsg, decl_ty: Type) !void { +pub fn addDeclaredHereNote(sema: *Sema, parent: *Zcu.ErrorMsg, decl_ty: Type) !void { const zcu = sema.pt.zcu; const src_loc = decl_ty.srcLocOrNull(zcu) orelse return; const category = switch (decl_ty.zigTypeTag(zcu)) { @@ -5393,8 +4799,8 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; const src = block.nodeOffset(pl_node.src_node); const bin = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data; - const ptr = try sema.resolveInst(bin.lhs); - const operand = try sema.resolveInst(bin.rhs); + const ptr = sema.resolveInst(bin.lhs); + const operand = sema.resolveInst(bin.rhs); const ptr_inst = ptr.toIndex().?; const air_datas = sema.air_instructions.items(.data); @@ -5440,17 +4846,19 @@ fn storeToInferredAllocComptime( const operand_ty = sema.typeOf(operand); // There will be only one store_to_inferred_ptr because we are running at comptime. // The alloc will turn into a Decl or a ComptimeAlloc. - const operand_val = try sema.resolveValue(operand) orelse { + const operand_val = sema.resolveValue(operand) orelse { return sema.failWithNeededComptime(block, src, .{ .simple = .stored_to_comptime_var }); }; - const alloc_ty = try pt.ptrTypeSema(.{ + const alloc_ty = try pt.ptrType(.{ .child = operand_ty.toIntern(), .flags = .{ .alignment = iac.alignment, .is_const = iac.is_const, }, }); - if (iac.is_const and !operand_val.canMutateComptimeVarState(zcu)) { + if (operand_ty.classify(zcu) == .one_possible_value or + (iac.is_const and !operand_val.canMutateComptimeVarState(zcu))) + { iac.ptr = try pt.intern(.{ .ptr = .{ .ty = alloc_ty.toIntern(), .base_addr = .{ .uav = .{ @@ -5487,8 +4895,8 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v const inst_data = zir_datas[@intFromEnum(inst)].pl_node; const src = block.nodeOffset(inst_data.src_node); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const ptr = try sema.resolveInst(extra.lhs); - const operand = try sema.resolveInst(extra.rhs); + const ptr = sema.resolveInst(extra.lhs); + const operand = sema.resolveInst(extra.rhs); const is_ret = if (extra.lhs.toIndex()) |ptr_index| zir_tags[@intFromEnum(ptr_index)] == .ret_ptr @@ -5535,11 +4943,11 @@ pub fn addStrLit(sema: *Sema, string: InternPool.String, len: u64) CompileError! .ty = array_ty.toIntern(), .storage = .{ .bytes = string }, } }); - return sema.uavRef(val); + return sema.uavRef(.fromInterned(val)); } -fn uavRef(sema: *Sema, val: InternPool.Index) CompileError!Air.Inst.Ref { - return Air.internedToRef(try sema.pt.refValue(val)); +fn uavRef(sema: *Sema, val: Value) CompileError!Air.Inst.Ref { + return .fromValue(try sema.pt.uavValue(val)); } fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -5622,9 +5030,9 @@ fn zirCompileLog( for (args, 0..) |arg_ref, i| { if (i != 0) writer.writeAll(", ") catch return error.OutOfMemory; - const arg = try sema.resolveInst(arg_ref); + const arg = sema.resolveInst(arg_ref); const arg_ty = sema.typeOf(arg); - if (try sema.resolveValueResolveLazy(arg)) |val| { + if (sema.resolveValue(arg)) |val| { writer.print("@as({f}, {f})", .{ arg_ty.fmt(pt), val.fmtValueSema(pt, sema), }) catch return error.OutOfMemory; @@ -5672,7 +5080,7 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); - const msg_inst = try sema.resolveInst(inst_data.operand); + const msg_inst = sema.resolveInst(inst_data.operand); const arg_src = block.builtinCallArgSrc(inst_data.src_node, 0); const coerced_msg = try sema.coerce(block, .slice_const_u8, msg_inst, arg_src); @@ -5752,9 +5160,9 @@ fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError var label: Block.Label = .{ .zir_block = inst, .merges = .{ - .src_locs = .{}, - .results = .{}, - .br_list = .{}, + .src_locs = .empty, + .results = .empty, + .br_list = .empty, .block_inst = block_inst, }, }; @@ -5826,7 +5234,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr .parent = parent_block, .sema = sema, .namespace = parent_block.namespace, - .instructions = .{}, + .instructions = .empty, .inlining = parent_block.inlining, .comptime_reason = .{ .reason = .{ .src = src, @@ -5927,11 +5335,10 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr pt.updateFile(new_file_index, zcu.fileByIndex(new_file_index)) catch |err| return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); - try pt.ensureFileAnalyzed(new_file_index); - const ty = zcu.fileRootType(new_file_index); - try sema.declareDependency(.{ .interned = ty }); + try pt.ensureFilePopulated(new_file_index); + const ty: Type = .fromInterned(zcu.fileRootType(new_file_index)); try sema.addTypeReferenceEntry(src, ty); - return Air.internedToRef(ty); + return .fromType(ty); } fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -5962,9 +5369,9 @@ fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErro var label: Block.Label = .{ .zir_block = inst, .merges = .{ - .src_locs = .{}, - .results = .{}, - .br_list = .{}, + .src_locs = .empty, + .results = .empty, + .br_list = .empty, .block_inst = block_inst, }, }; @@ -5973,7 +5380,7 @@ fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErro .parent = parent_block, .sema = sema, .namespace = parent_block.namespace, - .instructions = .{}, + .instructions = .empty, .label = &label, .inlining = parent_block.inlining, .comptime_reason = parent_block.comptime_reason, @@ -6043,7 +5450,7 @@ fn resolveBlockBody( const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break"; const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; if (extra.block_inst == body_inst) { - return try sema.resolveInst(break_data.operand); + return sema.resolveInst(break_data.operand); } else { return error.ComptimeBreak; } @@ -6134,7 +5541,7 @@ fn resolveAnalyzedBlock( // Okay, we need a runtime block. If the value is comptime-known, the // block should just return void, and we return the merge result // directly. Otherwise, we can defer to the logic below. - if (try sema.resolveValue(merges.results.items[0])) |result_val| { + if (sema.resolveValue(merges.results.items[0])) |result_val| { // Create a block containing all instruction from the body. try parent_block.instructions.append(gpa, merges.block_inst); switch (block_tag) { @@ -6177,10 +5584,11 @@ fn resolveAnalyzedBlock( // to emit a jump instruction to after the block when it encounters the break. try parent_block.instructions.append(gpa, merges.block_inst); const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items, .{ .override = merges.src_locs.items }); + resolved_ty.assertHasLayout(zcu); // TODO add note "missing else causes void value" const type_src = src; // TODO: better source location - if (try resolved_ty.comptimeOnlySema(pt)) { + if (resolved_ty.comptimeOnly(zcu)) { const msg = msg: { const msg = try sema.errMsg(type_src, "value with comptime-only type '{f}' depends on runtime control flow", .{resolved_ty.fmt(pt)}); errdefer msg.destroy(sema.gpa); @@ -6274,10 +5682,7 @@ fn resolveAnalyzedBlock( }); } - if (try sema.typeHasOnePossibleValue(resolved_ty)) |block_only_value| { - return Air.internedToRef(block_only_value.toIntern()); - } - + if (try resolved_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); return merges.block_inst.toRef(); } @@ -6295,7 +5700,7 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 0); const options_src = block.builtinCallArgSrc(inst_data.src_node, 1); - const ptr = try sema.resolveInst(extra.exported); + const ptr = sema.resolveInst(extra.exported); const ptr_val = try sema.resolveConstDefinedValue(block, ptr_src, ptr, .{ .simple = .export_target }); const ptr_ty = ptr_val.typeOf(zcu); @@ -6314,91 +5719,95 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void } } + const export_ty = ptr_ty.childType(zcu); + try sema.ensureLayoutResolved(export_ty, src, .@"export"); + if (!export_ty.validateExtern(.other, zcu)) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)}); + errdefer msg.destroy(sema.gpa); + try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other); + try sema.addDeclaredHereNote(msg, export_ty); + break :msg msg; + }); + } + const ptr_info = ip.indexToKey(ptr_val.toIntern()).ptr; - switch (ptr_info.base_addr) { + const target: Zcu.Exported = switch (ptr_info.base_addr) { .comptime_alloc, .int, .comptime_field => return sema.fail(block, ptr_src, "export target must be a global variable or a comptime-known constant", .{}), .eu_payload, .opt_payload, .field, .arr_elem => return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}), - .uav => |uav| { - if (ptr_info.byte_offset != 0) { - return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}); - } - if (zcu.llvm_object != null and options.linkage == .internal) return; - const export_ty = Value.fromInterned(uav.val).typeOf(zcu); - if (!try sema.validateExternType(export_ty, .other)) { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)}); - errdefer msg.destroy(sema.gpa); - try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other); - try sema.addDeclaredHereNote(msg, export_ty); - break :msg msg; - }); - } - try sema.exports.append(zcu.gpa, .{ - .opts = options, - .src = src, - .exported = .{ .uav = uav.val }, - .status = .in_progress, - }); - }, - .nav => |nav| { - if (ptr_info.byte_offset != 0) { - return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}); + .uav => |uav| .{ .uav = uav.val }, + .nav => |orig_nav| target: { + try sema.ensureNavResolved(block, src, orig_nav, .fully); + const export_nav = switch (ip.indexToKey(ip.getNav(orig_nav).status.fully_resolved.val)) { + .variable => |v| v.owner_nav, + .@"extern" => |e| e.owner_nav, + .func => |f| f.owner_nav, + else => orig_nav, + }; + if (ip.getNav(export_nav).getExtern(ip) != null) { + return sema.fail(block, src, "export target cannot be extern", .{}); } - try sema.analyzeExport(block, src, options, nav); + try sema.maybeQueueFuncBodyAnalysis(block, src, export_nav); + break :target .{ .nav = export_nav }; }, + }; + if (ptr_info.byte_offset != 0) { + return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}); } + if (zcu.llvm_object != null and options.linkage == .internal) return; + try sema.exports.append(zcu.gpa, .{ + .opts = options, + .src = src, + .exported = target, + .status = .in_progress, + }); } -pub fn analyzeExport( +/// Asserts that `sema.owner` is a `.nav_val` whose value is resolved. +/// +/// Exports that `Nav` by the given name with all other options set to default. +pub fn analyzeExportSelfNav( sema: *Sema, block: *Block, src: LazySrcLoc, - options: Zcu.Export.Options, - orig_nav_index: InternPool.Nav.Index, + name: InternPool.NullTerminatedString, ) !void { const gpa = sema.gpa; const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - if (zcu.llvm_object != null and options.linkage == .internal) - return; - - try sema.ensureNavResolved(block, src, orig_nav_index, .fully); - - const exported_nav_index = switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) { - .variable => |v| v.owner_nav, - .@"extern" => |e| e.owner_nav, - .func => |f| f.owner_nav, - else => orig_nav_index, - }; - - const exported_nav = ip.getNav(exported_nav_index); - const export_ty: Type = .fromInterned(exported_nav.typeOf(ip)); + const orig_nav = sema.owner.unwrap().nav_val; + const export_val: Value = .fromInterned(ip.getNav(orig_nav).status.fully_resolved.val); + const export_ty = export_val.typeOf(zcu); - if (!try sema.validateExternType(export_ty, .other)) { + if (!export_ty.validateExtern(.other, zcu)) { return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)}); errdefer msg.destroy(gpa); - try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other); - try sema.addDeclaredHereNote(msg, export_ty); break :msg msg; }); } - // TODO: some backends might support re-exporting extern decls - if (exported_nav.getExtern(ip) != null) { - return sema.fail(block, src, "export target cannot be extern", .{}); - } - - try sema.maybeQueueFuncBodyAnalysis(block, src, exported_nav_index); + const export_nav = switch (ip.indexToKey(export_val.toIntern())) { + .variable => |v| v.owner_nav, + .@"extern" => |e| e.owner_nav, + .func => |f| export_nav: { + assert(export_ty.fnHasRuntimeBits(zcu)); // otherwise `validateExtern` failed above + const orig_fn_index = ip.unwrapCoercedFunc(export_val.toIntern()); + try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index })); + try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index); + break :export_nav f.owner_nav; + }, + else => orig_nav, + }; try sema.exports.append(gpa, .{ - .opts = options, + .opts = .{ .name = name }, .src = src, - .exported = .{ .nav = exported_nav_index }, + .exported = .{ .nav = export_nav }, .status = .in_progress, }); } @@ -6413,7 +5822,8 @@ fn zirDisableInstrumentation(sema: *Sema) CompileError!void { .@"comptime", .nav_val, .nav_ty, - .type, + .type_layout, + .struct_defaults, .memoized_state, => return, // does nothing outside a function }; @@ -6431,7 +5841,8 @@ fn zirDisableIntrinsics(sema: *Sema) CompileError!void { .@"comptime", .nav_val, .nav_ty, - .type, + .type_layout, + .struct_defaults, .memoized_state, => return, // does nothing outside a function }; @@ -6457,7 +5868,7 @@ fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"break"; const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data; - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const zir_block = extra.block_inst; var block = start_block; @@ -6491,7 +5902,7 @@ fn zirSwitchContinue(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) Com const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"break"; const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data; const operand_src = start_block.nodeOffset(extra.operand_src_node.unwrap().?); - const uncoerced_operand = try sema.resolveInst(inst_data.operand); + const uncoerced_operand = sema.resolveInst(inst_data.operand); const switch_inst = extra.block_inst; switch (sema.code.instructions.items(.tag)[@intFromEnum(switch_inst)]) { @@ -6500,7 +5911,7 @@ fn zirSwitchContinue(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) Com else => unreachable, // assertion failure } - const operand_ty = (try sema.resolveInst(switch_inst.toRef())).toType(); + const operand_ty = (sema.resolveInst(switch_inst.toRef())).toType(); const operand = try sema.coerce(start_block, operand_ty, uncoerced_operand, operand_src); try sema.validateRuntimeValue(start_block, operand_src, operand); @@ -6567,7 +5978,7 @@ fn zirDbgVar( air_tag: Air.Inst.Tag, ) CompileError!void { const str_op = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_op; - const operand = try sema.resolveInst(str_op.operand); + const operand = sema.resolveInst(str_op.operand); const name = str_op.getStr(sema.code); try sema.addDbgVar(block, operand, air_tag, name); } @@ -6589,9 +6000,9 @@ fn addDbgVar( .dbg_var_val, .dbg_arg_inline => operand_ty, else => unreachable, }; - if (try val_ty.comptimeOnlySema(pt)) return; - if (!(try val_ty.hasRuntimeBitsSema(pt))) return; - if (try sema.resolveValue(operand)) |operand_val| { + if (val_ty.comptimeOnly(zcu)) return; + if (!val_ty.hasRuntimeBits(zcu)) return; + if (sema.resolveValue(operand)) |operand_val| { if (operand_val.canMutateComptimeVarState(zcu)) return; } @@ -6730,7 +6141,7 @@ fn funcDeclSrcInst(sema: *Sema, func_inst: Air.Inst.Ref) !?InternPool.TrackedIns const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - const func_val = try sema.resolveValue(func_inst) orelse return null; + const func_val = sema.resolveValue(func_inst) orelse return null; if (func_val.isUndef(zcu)) return null; const nav = switch (ip.indexToKey(func_val.toIntern())) { .@"extern" => |e| e.owner_nav, @@ -6759,7 +6170,6 @@ pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref if (!block.ownerModule().error_tracing) return .none; const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace); - try stack_trace_ty.resolveFields(pt); const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls); const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, LazySrcLoc.unneeded) catch |err| switch (err) { error.AnalysisFail => @panic("std.builtin.StackTrace is corrupt"), @@ -6803,11 +6213,10 @@ fn popErrorReturnTrace( // the result is comptime-known to be a non-error. Either way, pop unconditionally. const stack_trace_ty = try sema.getBuiltinType(src, .StackTrace); - try stack_trace_ty.resolveFields(pt); const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty); const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls); - const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty, true); + const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty); try sema.storePtr2(block, src, field_ptr, src, saved_error_trace_index, src, .store); } else if (is_non_error == null) { // The result might be an error. If it is, we leave the error trace alone. If it isn't, we need @@ -6829,11 +6238,10 @@ fn popErrorReturnTrace( // If non-error, then pop the error return trace by restoring the index. const stack_trace_ty = try sema.getBuiltinType(src, .StackTrace); - try stack_trace_ty.resolveFields(pt); const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty); const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty); const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls); - const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty, true); + const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty); try sema.storePtr2(&then_block, src, field_ptr, src, saved_error_trace_index, src, .store); _ = try then_block.addBr(cond_block_inst, .void_value); @@ -6905,9 +6313,9 @@ fn zirCall( const pop_error_return_trace = extra.data.flags.pop_error_return_trace; const callee: ResolvedFieldCallee = switch (kind) { - .direct => .{ .direct = try sema.resolveInst(extra.data.callee) }, + .direct => .{ .direct = sema.resolveInst(extra.data.callee) }, .field => blk: { - const object_ptr = try sema.resolveInst(extra.data.obj_ptr); + const object_ptr = sema.resolveInst(extra.data.obj_ptr); const field_name = try zcu.intern_pool.getOrPutString( gpa, io, @@ -6969,7 +6377,6 @@ fn zirCall( // need to clean-up our own trace if we were passed to a non-error-handling expression. if (input_is_error or (pop_error_return_trace and return_ty.isError(zcu))) { const stack_trace_ty = try sema.getBuiltinType(call_src, .StackTrace); - try stack_trace_ty.resolveFields(pt); const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls); const field_index = try sema.structFieldIndex(block, stack_trace_ty, field_name, call_src); @@ -7255,7 +6662,7 @@ const CallArgsInfo = union(enum) { return sema.failWithNeededComptime(block, cai.argSrc(block, arg_index), null); } - if (sema.typeOf(uncoerced_arg).zigTypeTag(zcu) == .noreturn) { + if (sema.typeOf(uncoerced_arg).classify(zcu) == .no_possible_value) { // This terminates resolution of arguments. The caller should // propagate this. return uncoerced_arg; @@ -7318,6 +6725,21 @@ fn analyzeCall( } else func_src; const func_ty_info = zcu.typeToFunc(func_ty).?; + + for (func_ty_info.param_types.get(ip), 0..) |param_ty_ip, param_index| { + const arg_src = args_info.argSrc(block, param_index); + try sema.ensureLayoutResolved(.fromInterned(param_ty_ip), arg_src, .init); + } + try sema.ensureLayoutResolved(.fromInterned(func_ty_info.return_type), func_ret_ty_src, .return_type); + try sema.validateResolvedFuncType( + block, + func_ty_info.cc, + func_ty_info.param_types.get(ip), + .fromInterned(func_ty_info.return_type), + func_src, + maybe_func_inst, + ); + if (!callConvIsCallable(func_ty_info.cc)) { return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg( @@ -7334,6 +6756,28 @@ fn analyzeCall( }); } + const any_comptime_params = func_ty_info.comptime_bits != 0 or ct: { + for (func_ty_info.param_types.get(ip)) |param_ty| { + if (Type.fromInterned(param_ty).comptimeOnly(zcu)) break :ct true; + } + break :ct Type.fromInterned(func_ty_info.return_type).comptimeOnly(zcu); + }; + const any_generic_types = generic: { + for (func_ty_info.param_types.get(ip)) |param_ty| { + if (param_ty == .generic_poison_type) break :generic true; + } + const ret_ty: Type = .fromInterned(func_ty_info.return_type); + if (ret_ty.toIntern() == .generic_poison_type) { + break :generic true; + } + if (ret_ty.zigTypeTag(zcu) == .error_union and + ret_ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type) + { + break :generic true; + } + break :generic false; + }; + // We need this value in a few code paths. const callee_val = try sema.resolveDefinedValue(block, call_src, callee); // If the callee is a comptime-known *non-extern* function, `func_val` is populated. @@ -7353,7 +6797,7 @@ fn analyzeCall( else => unreachable, } else .{ null, false }; - if (func_ty_info.is_generic and func_val == null) { + if ((any_generic_types or any_comptime_params) and func_val == null) { return sema.failWithNeededComptime(block, func_src, .{ .simple = .generic_call_target }); } @@ -7369,19 +6813,18 @@ fn analyzeCall( .src = call_src, .r = .{ .simple = .comptime_call_modifier }, } }; - } else if (!inline_requested and try Type.fromInterned(func_ty_info.return_type).comptimeOnlySema(pt)) { - block.comptime_reason = .{ - .reason = .{ + } else if (!inline_requested) { + const ret_ty: Type = .fromInterned(func_ty_info.return_type); + if (ret_ty.comptimeOnly(zcu)) { + block.comptime_reason = .{ .reason = .{ .src = call_src, - .r = .{ - .comptime_only_ret_ty = .{ - .ty = .fromInterned(func_ty_info.return_type), - .is_generic_inst = false, - .ret_ty_src = func_ret_ty_src, - }, - }, - }, - }; + .r = .{ .comptime_only_ret_ty = .{ + .ty = .fromInterned(func_ty_info.return_type), + .is_generic_inst = false, + .ret_ty_src = func_ret_ty_src, + } }, + } }; + } } } @@ -7403,13 +6846,13 @@ fn analyzeCall( // This is the `inst_map` used when evaluating generic parameters and return types. var generic_inst_map: InstMap = .{}; defer generic_inst_map.deinit(gpa); - if (func_ty_info.is_generic) { + if (any_generic_types) { try generic_inst_map.ensureSpaceForInstructions(gpa, fn_zir_info.param_body); } // This exists so that `generic_block` below can include a "called from here" note back to this // call site when analyzing generic parameter/return types. - var generic_inlining: Block.Inlining = if (func_ty_info.is_generic) .{ + var generic_inlining: Block.Inlining = if (any_generic_types) .{ .call_block = block, .call_src = call_src, .func = func_val.?.toIntern(), @@ -7422,18 +6865,18 @@ fn analyzeCall( // This is the block in which we evaluate generic function components: that is, generic parameter // types and the generic return type. This must not be used if the function is not generic. // `comptime_reason` is set as needed. - var generic_block: Block = if (func_ty_info.is_generic) .{ + var generic_block: Block = if (any_generic_types) .{ .parent = null, .sema = sema, .namespace = fn_nav.analysis.?.namespace, - .instructions = .{}, + .instructions = .empty, .inlining = &generic_inlining, .src_base_inst = fn_nav.analysis.?.zir_index, .type_name_ctx = fn_nav.fqn, } else undefined; - defer if (func_ty_info.is_generic) generic_block.instructions.deinit(gpa); + defer if (any_generic_types) generic_block.instructions.deinit(gpa); - if (func_ty_info.is_generic) { + if (any_generic_types) { // We certainly depend on the generic owner's signature! try sema.declareDependency(.{ .src_hash = fn_tracked_inst }); } @@ -7445,7 +6888,7 @@ fn analyzeCall( if (raw != .generic_poison_type) break :ty .fromInterned(raw); // We must discover the generic parameter type. - assert(func_ty_info.is_generic); + assert(any_generic_types); const param_inst_idx = fn_zir_info.param_body[arg_idx]; const param_inst = fn_zir.instructions.get(@intFromEnum(param_inst_idx)); switch (param_inst.tag) { @@ -7476,7 +6919,7 @@ fn analyzeCall( } }; const ty_ref = try sema.resolveInlineBody(&generic_block, body, param_inst_idx); - const param_ty = try sema.analyzeAsType(&generic_block, param_src, ty_ref); + const param_ty = try sema.analyzeAsType(&generic_block, param_src, .fn_param_types, ty_ref); if (!param_ty.isValidParamType(zcu)) { const opaque_str = if (param_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; @@ -7490,15 +6933,15 @@ fn analyzeCall( arg.* = try args_info.analyzeArg(sema, block, arg_idx, param_ty, func_ty_info, callee, maybe_func_inst); const arg_ty = sema.typeOf(arg.*); - if (arg_ty.zigTypeTag(zcu) == .noreturn) { + if (arg_ty.classify(zcu) == .no_possible_value) { return arg.*; // terminate analysis here } - if (func_ty_info.is_generic) { + if (any_generic_types) { // We need to put the argument into `generic_inst_map` so that other parameters can refer to it. const param_inst_idx = fn_zir_info.param_body[arg_idx]; const declared_comptime = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsComptime(i) else false; - const param_is_comptime = declared_comptime or try arg_ty.comptimeOnlySema(pt); + const param_is_comptime = declared_comptime or arg_ty.comptimeOnly(zcu); // We allow comptime-known arguments to propagate to generic types not only for comptime // parameters, but if the call is known to be inline. if (param_is_comptime or early_known_inline) { @@ -7516,6 +6959,10 @@ fn analyzeCall( ); } generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, arg.*); + } else if (try arg_ty.onePossibleValue(pt)) |opv| { + // The argument is comptime-known, even though this is a generic instantiation (as + // opposed to an inline call), because the parameter type is OPV. + generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, .fromValue(opv)); } else { // We need a dummy instruction with this type. It doesn't actually need to be in any block, // since it will never be referenced at runtime! @@ -7532,7 +6979,7 @@ fn analyzeCall( // calls (where it should be the IES of the instantiation). However, it's how we print this // in error messages. const resolved_ret_ty: Type = ret_ty: { - if (!func_ty_info.is_generic) break :ret_ty .fromInterned(func_ty_info.return_type); + if (!any_generic_types) break :ret_ty .fromInterned(func_ty_info.return_type); const maybe_poison_bare = if (fn_zir_info.inferred_error_set) maybe_poison: { break :maybe_poison ip.errorUnionPayload(func_ty_info.return_type); @@ -7542,7 +6989,7 @@ fn analyzeCall( // Evaluate the generic return type. As with generic parameters, we switch out `sema.code` and `sema.inst_map`. - assert(func_ty_info.is_generic); + assert(any_generic_types); const old_code = sema.code; const old_inst_map = sema.inst_map; @@ -7565,7 +7012,7 @@ fn analyzeCall( } else bare: { assert(fn_zir_info.ret_ty_body.len != 0); const ty_ref = try sema.resolveInlineBody(&generic_block, fn_zir_info.ret_ty_body, fn_zir_inst); - break :bare try sema.analyzeAsType(&generic_block, func_ret_ty_src, ty_ref); + break :bare try sema.analyzeAsType(&generic_block, func_ret_ty_src, .fn_ret_ty, ty_ref); }; assert(bare_ty.toIntern() != .generic_poison_type); @@ -7584,10 +7031,11 @@ fn analyzeCall( break :ret_ty full_ty; }; + try sema.ensureLayoutResolved(resolved_ret_ty, func_ret_ty_src, .return_type); // If we've discovered after evaluating arguments that a generic function instantiation is // comptime-only, then we can mark the block as comptime *now*. - if (!inline_requested and !block.isComptime() and try resolved_ret_ty.comptimeOnlySema(pt)) { + if (!inline_requested and !block.isComptime() and resolved_ret_ty.comptimeOnly(zcu)) { block.comptime_reason = .{ .reason = .{ .src = call_src, @@ -7618,15 +7066,23 @@ fn analyzeCall( }); if (func_ty_info.cc == .auto) { switch (sema.owner.unwrap()) { - .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {}, + .@"comptime", + .nav_ty, + .nav_val, + .type_layout, + .struct_defaults, + .memoized_state, + => {}, + .func => |owner_func| ip.funcSetHasErrorTrace(io, owner_func, true), } } for (args, 0..) |arg, arg_idx| { - try sema.validateRuntimeValue(block, args_info.argSrc(block, arg_idx), arg); + const arg_src = args_info.argSrc(block, arg_idx); + try sema.validateRuntimeValue(block, arg_src, arg); } const runtime_func: Air.Inst.Ref, const runtime_args: []const Air.Inst.Ref = func: { - if (!func_ty_info.is_generic) break :func .{ callee, args }; + if (!any_generic_types and !any_comptime_params) break :func .{ callee, args }; // Instantiate the generic function! @@ -7648,13 +7104,13 @@ fn analyzeCall( break :c true; } } - break :c try arg_ty.comptimeOnlySema(pt); + break :c arg_ty.comptimeOnly(zcu); }; const is_noalias = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsNoalias(i) else false; if (is_comptime) { // We already emitted an error if the argument isn't comptime-known. - comptime_arg.* = (try sema.resolveValue(arg)).?.toIntern(); + comptime_arg.* = sema.resolveValue(arg).?.toIntern(); } else { comptime_arg.* = .none; if (is_noalias) { @@ -7695,7 +7151,7 @@ fn analyzeCall( }; ref_func: { - const runtime_func_val = try sema.resolveValue(runtime_func) orelse break :ref_func; + const runtime_func_val = sema.resolveValue(runtime_func) orelse break :ref_func; if (!ip.isFuncBody(runtime_func_val.toIntern())) break :ref_func; const orig_fn_index = ip.unwrapCoercedFunc(runtime_func_val.toIntern()); try sema.addReferenceEntry(block, call_src, .wrap(.{ .func = orig_fn_index })); @@ -7714,7 +7170,7 @@ fn analyzeCall( }; try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).@"struct".fields.len + runtime_args.len); - const maybe_opv = try block.addInst(.{ + const call_ref = try block.addInst(.{ .tag = call_tag, .data = .{ .pl_op = .{ .operand = runtime_func, @@ -7725,8 +7181,10 @@ fn analyzeCall( }); sema.appendRefsAssumeCapacity(runtime_args); + const actual_ret_ty = sema.typeOf(call_ref); + if (ensure_result_used) { - try sema.ensureResultUsed(block, sema.typeOf(maybe_opv), call_src); + try sema.ensureResultUsed(block, actual_ret_ty, call_src); } if (call_tag == .call_always_tail) { @@ -7736,29 +7194,32 @@ fn analyzeCall( .pointer => func_or_ptr_ty.childType(zcu), else => unreachable, }; - return sema.handleTailCall(block, call_src, runtime_func_ty, maybe_opv); + return sema.handleTailCall(block, call_src, runtime_func_ty, call_ref); } - if (ip.isNoReturn(resolved_ret_ty.toIntern())) { - const want_check = c: { - if (!block.wantSafety()) break :c false; - if (func_val != null) break :c false; - break :c true; - }; - if (want_check) { - try sema.safetyPanic(block, call_src, .noreturn_returned); - } else { - _ = try block.addNoOp(.unreach); - } - return .unreachable_value; + switch (actual_ret_ty.classify(zcu)) { + .no_possible_value => { + const want_check = c: { + if (!block.wantSafety()) break :c false; + if (func_val != null) break :c false; + break :c true; + }; + if (want_check) { + try sema.safetyPanic(block, call_src, .noreturn_returned); + } else { + _ = try block.addNoOp(.unreach); + } + return .unreachable_value; + }, + .one_possible_value => { + return .fromValue((try actual_ret_ty.onePossibleValue(pt)).?); + }, + .runtime => { + return call_ref; + }, + .partially_comptime => unreachable, + .fully_comptime => unreachable, } - - const result: Air.Inst.Ref = if (try sema.typeHasOnePossibleValue(sema.typeOf(maybe_opv))) |opv| - .fromValue(opv) - else - maybe_opv; - - return result; } // This is an inline call. The function must be comptime-known. We will analyze its body directly using this `Sema`. @@ -7836,14 +7297,14 @@ fn analyzeCall( if (zcu.comp.config.incremental) break :m false; if (!block.isComptime()) break :m false; for (args) |a| { - const val = (try sema.resolveValue(a)).?; + const val = sema.resolveValue(a).?; if (val.canMutateComptimeVarState(zcu)) break :m false; } break :m true; }; const memoized_arg_values: []const InternPool.Index = if (want_memoize) arg_vals: { const vals = try sema.arena.alloc(InternPool.Index, args.len); - for (vals, args) |*v, a| v.* = (try sema.resolveValue(a)).?.toIntern(); + for (vals, args) |*v, a| v.* = sema.resolveValue(a).?.toIntern(); break :arg_vals vals; } else undefined; if (want_memoize) memoize: { @@ -7927,7 +7388,7 @@ fn analyzeCall( .parent = null, .sema = sema, .namespace = fn_nav.analysis.?.namespace, - .instructions = .{}, + .instructions = .empty, .inlining = &inlining, .is_typeof = block.is_typeof, .comptime_reason = if (block.isComptime()) .inlining_parent else null, @@ -8000,7 +7461,11 @@ fn analyzeCall( break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, &inlining.merges, need_debug_scope); }; - const maybe_opv: Air.Inst.Ref = if (try sema.resolveValue(result_raw)) |result_val| r: { + if (sema.typeOf(result_raw).isNoReturn(zcu)) { + return .unreachable_value; + } + + const maybe_opv: Air.Inst.Ref = if (sema.resolveValue(result_raw)) |result_val| r: { const val_resolved = try sema.resolveAdHocInferredErrorSet(block, call_src, result_val.toIntern()); break :r Air.internedToRef(val_resolved); } else r: { @@ -8012,7 +7477,7 @@ fn analyzeCall( }; if (block.isComptime()) { - const result_val = (try sema.resolveValue(maybe_opv)).?; + const result_val = sema.resolveValue(maybe_opv).?; if (want_memoize and sema.allow_memoize and !result_val.canMutateComptimeVarState(zcu)) { _ = try pt.intern(.{ .memoized_call = .{ .func = func_val.?.toIntern(), @@ -8081,15 +7546,12 @@ fn zirArrayInitElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin; const maybe_wrapped_indexable_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, bin.lhs) orelse return .generic_poison_type; const indexable_ty = maybe_wrapped_indexable_ty.optEuBaseType(zcu); - try indexable_ty.resolveFields(pt); assert(indexable_ty.isIndexable(zcu)); // validated by a previous instruction - if (indexable_ty.zigTypeTag(zcu) == .@"struct") { - const elem_type = indexable_ty.fieldType(@intFromEnum(bin.rhs), zcu); - return Air.internedToRef(elem_type.toIntern()); - } else { - const elem_type = indexable_ty.elemType2(zcu); - return Air.internedToRef(elem_type.toIntern()); - } + const elem_ty = switch (indexable_ty.zigTypeTag(zcu)) { + .@"struct" => indexable_ty.fieldType(@intFromEnum(bin.rhs), zcu), + else => indexable_ty.indexableElem(zcu), + }; + return .fromType(elem_ty); } fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -8190,7 +7652,7 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil const len = try sema.resolveInt(block, len_src, extra.len, .usize, .{ .simple = .array_length }); const elem_type = try sema.resolveType(block, elem_src, extra.elem_type); try sema.validateArrayElemType(block, elem_type, elem_src); - const uncasted_sentinel = try sema.resolveInst(extra.sentinel); + const uncasted_sentinel = sema.resolveInst(extra.sentinel); const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src); const sentinel_val = try sema.resolveConstDefinedValue(block, sentinel_src, sentinel, .{ .simple = .array_sentinel }); if (sentinel_val.canMutateComptimeVarState(zcu)) { @@ -8306,11 +7768,11 @@ fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src = block.nodeOffset(extra.node); const operand_src = block.builtinCallArgSrc(extra.node, 0); - const uncasted_operand = try sema.resolveInst(extra.operand); + const uncasted_operand = sema.resolveInst(extra.operand); const operand = try sema.coerce(block, .anyerror, uncasted_operand, operand_src); const err_int_ty = try pt.errorIntType(); - if (try sema.resolveValue(operand)) |val| { + if (sema.resolveValue(operand)) |val| { if (val.isUndef(zcu)) { return pt.undefRef(err_int_ty); } @@ -8350,12 +7812,12 @@ fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src = block.nodeOffset(extra.node); const operand_src = block.builtinCallArgSrc(extra.node, 0); - const uncasted_operand = try sema.resolveInst(extra.operand); + const uncasted_operand = sema.resolveInst(extra.operand); const err_int_ty = try pt.errorIntType(); const operand = try sema.coerce(block, err_int_ty, uncasted_operand, operand_src); if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| { - const int = try sema.usizeCast(block, operand_src, try value.toUnsignedIntSema(pt)); + const int = try sema.usizeCast(block, operand_src, value.toUnsignedInt(zcu)); if (int > len: { const mutate = &ip.global_error_set.mutate; mutate.map.mutex.lockUncancelable(io); @@ -8397,8 +7859,8 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); - const lhs = try sema.resolveInst(extra.lhs); - const rhs = try sema.resolveInst(extra.rhs); + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); if (sema.typeOf(lhs).zigTypeTag(zcu) == .bool and sema.typeOf(rhs).zigTypeTag(zcu) == .bool) { const msg = msg: { const msg = try sema.errMsg(lhs_src, "expected error set type, found 'bool'", .{}); @@ -8408,8 +7870,8 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr }; return sema.failWithOwnedErrorMsg(block, msg); } - const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs); - const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs); + const lhs_ty = try sema.analyzeAsType(block, lhs_src, .type, lhs); + const rhs_ty = try sema.analyzeAsType(block, rhs_src, .type, rhs); if (lhs_ty.zigTypeTag(zcu) != .error_set) return sema.fail(block, lhs_src, "expected error set type, found '{f}'", .{lhs_ty.fmt(pt)}); if (rhs_ty.zigTypeTag(zcu) != .error_set) @@ -8420,21 +7882,21 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr return .anyerror_type; } - if (ip.isInferredErrorSetType(lhs_ty.toIntern())) { - switch (try sema.resolveInferredErrorSet(block, src, lhs_ty.toIntern())) { - // isAnyError might have changed from a false negative to a true - // positive after resolution. - .anyerror_type => return .anyerror_type, - else => {}, - } + switch (ip.indexToKey(lhs_ty.toIntern())) { + .inferred_error_set_type => |func_index| { + try sema.ensureFuncIesResolved(block, src, func_index); + if (ip.funcIesResolvedUnordered(func_index) == .anyerror_type) return .anyerror_type; + }, + .error_set_type => {}, + else => unreachable, } - if (ip.isInferredErrorSetType(rhs_ty.toIntern())) { - switch (try sema.resolveInferredErrorSet(block, src, rhs_ty.toIntern())) { - // isAnyError might have changed from a false negative to a true - // positive after resolution. - .anyerror_type => return .anyerror_type, - else => {}, - } + switch (ip.indexToKey(rhs_ty.toIntern())) { + .inferred_error_set_type => |func_index| { + try sema.ensureFuncIesResolved(block, src, func_index); + if (ip.funcIesResolvedUnordered(func_index) == .anyerror_type) return .anyerror_type; + }, + .error_set_type => {}, + else => unreachable, } const err_set_ty = try sema.errorSetMerge(lhs_ty, rhs_ty); @@ -8533,23 +7995,22 @@ fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag(zcu)) { .@"enum" => operand, .@"union" => blk: { - try operand_ty.resolveFields(pt); - const tag_ty = operand_ty.unionTagType(zcu) orelse { + if (operand_ty.unionTagType(zcu) == null) { return sema.fail( block, operand_src, "untagged union '{f}' cannot be converted to integer", .{operand_ty.fmt(pt)}, ); - }; + } - break :blk try sema.unionToTag(block, tag_ty, operand, operand_src); + break :blk try sema.unionToTag(block, operand); }, else => { return sema.fail(block, operand_src, "expected enum or tagged union, found '{f}'", .{ @@ -8568,17 +8029,9 @@ fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError }); } - if (try sema.typeHasOnePossibleValue(enum_tag_ty)) |opv| { - return Air.internedToRef((try pt.getCoerced(opv, int_tag_ty)).toIntern()); - } - - if (try sema.resolveValue(enum_tag)) |enum_tag_val| { - if (enum_tag_val.isUndef(zcu)) { - return pt.undefRef(int_tag_ty); - } - - const val = try enum_tag_val.intFromEnum(enum_tag_ty, pt); - return Air.internedToRef(val.toIntern()); + if (sema.resolveValue(enum_tag)) |enum_tag_val| { + if (enum_tag_val.isUndef(zcu)) return pt.undefRef(int_tag_ty); + return .fromValue(enum_tag_val.intFromEnum(zcu)); } try sema.requireRuntimeBlock(block, src, operand_src); @@ -8593,18 +8046,19 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const src = block.nodeOffset(inst_data.src_node); const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@enumFromInt"); - const operand = try sema.resolveInst(extra.rhs); + const operand = sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); if (dest_ty.zigTypeTag(zcu) != .@"enum") { return sema.fail(block, src, "expected enum, found '{f}'", .{dest_ty.fmt(pt)}); } + try sema.ensureLayoutResolved(dest_ty, src, .init); _ = try sema.checkIntType(block, operand_src, operand_ty); - if (try sema.resolveValue(operand)) |int_val| { + if (sema.resolveValue(operand)) |int_val| { if (dest_ty.isNonexhaustiveEnum(zcu)) { const int_tag_ty = dest_ty.intTagType(zcu); - if (try sema.intFitsInType(int_val, int_tag_ty, null)) { + if (int_val.intFitsInType(int_tag_ty, null, zcu)) { return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern()); } return sema.fail(block, src, "int value '{f}' out of range of non-exhaustive enum '{f}'", .{ @@ -8626,19 +8080,15 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_enum }); } - if (try sema.typeHasOnePossibleValue(dest_ty)) |opv| { + if (try dest_ty.onePossibleValue(pt)) |opv| { if (block.wantSafety()) { // The operand is runtime-known but the result is comptime-known. In // this case we still need a safety check. - const expect_int_val = switch (zcu.intern_pool.indexToKey(opv.toIntern())) { - .enum_tag => |enum_tag| enum_tag.int, - else => unreachable, - }; - const expect_int_coerced = try pt.getCoerced(.fromInterned(expect_int_val), operand_ty); - const ok = try block.addBinOp(.cmp_eq, operand, Air.internedToRef(expect_int_coerced.toIntern())); + const expect_int = try pt.getCoerced(opv.intFromEnum(zcu), operand_ty); + const ok = try block.addBinOp(.cmp_eq, operand, .fromValue(expect_int)); try sema.addSafetyCheck(block, src, ok, .invalid_enum_value); } - return Air.internedToRef(opv.toIntern()); + return .fromValue(opv); } try sema.requireRuntimeBlock(block, src, operand_src); @@ -8660,12 +8110,17 @@ fn zirOptionalPayloadPtr( defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; - const optional_ptr = try sema.resolveInst(inst_data.operand); + const optional_ptr = sema.resolveInst(inst_data.operand); const src = block.nodeOffset(inst_data.src_node); + const ptr_ty = sema.typeOf(optional_ptr); + assert(ptr_ty.zigTypeTag(sema.pt.zcu) == .pointer); + try sema.ensureLayoutResolved(ptr_ty.childType(sema.pt.zcu), src, .ptr_access); + return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false); } +/// Asserts that the layout of the pointer child type is already resolved. fn analyzeOptionalPayloadPtr( sema: *Sema, block: *Block, @@ -8680,12 +8135,13 @@ fn analyzeOptionalPayloadPtr( assert(optional_ptr_ty.zigTypeTag(zcu) == .pointer); const opt_type = optional_ptr_ty.childType(zcu); + opt_type.assertHasLayout(zcu); if (opt_type.zigTypeTag(zcu) != .optional) { return sema.failWithExpectedOptionalType(block, src, opt_type); } const child_type = opt_type.optionalChild(zcu); - const child_pointer = try pt.ptrTypeSema(.{ + const child_pointer = try pt.ptrType(.{ .child = child_type.toIntern(), .flags = .{ .is_const = optional_ptr_ty.isConstPtr(zcu), @@ -8698,7 +8154,7 @@ fn analyzeOptionalPayloadPtr( if (sema.isComptimeMutablePtr(ptr_val)) { // Set the optional to non-null at comptime. // If the payload is OPV, we must use that value instead of undef. - const payload_val = try sema.typeHasOnePossibleValue(child_type) orelse try pt.undefValue(child_type); + const payload_val = try child_type.onePossibleValue(pt) orelse try pt.undefValue(child_type); const opt_val = try pt.intern(.{ .opt = .{ .ty = opt_type.toIntern(), .val = payload_val.toIntern(), @@ -8748,33 +8204,27 @@ fn zirOptionalPayload( const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); const result_ty = switch (operand_ty.zigTypeTag(zcu)) { .optional => operand_ty.optionalChild(zcu), - .pointer => t: { - if (operand_ty.ptrSize(zcu) != .c) { - return sema.failWithExpectedOptionalType(block, src, operand_ty); - } - // TODO https://github.com/ziglang/zig/issues/6597 - if (true) break :t operand_ty; - const ptr_info = operand_ty.ptrInfo(zcu); - break :t try pt.ptrTypeSema(.{ - .child = ptr_info.child, - .flags = .{ - .alignment = ptr_info.flags.alignment, - .is_const = ptr_info.flags.is_const, - .is_volatile = ptr_info.flags.is_volatile, - .is_allowzero = ptr_info.flags.is_allowzero, - .address_space = ptr_info.flags.address_space, - }, - }); + // TODO: https://github.com/ziglang/zig/issues/6597 will eliminate this branch so that we only need to handle optionals. + .pointer => switch (operand_ty.ptrSize(zcu)) { + .c => operand_ty, // if `ptr` is a `[*c]T`, then `ptr.?` is also a `[*c]T` + .one, .many, .slice => return sema.failWithExpectedOptionalType(block, src, operand_ty), }, else => return sema.failWithExpectedOptionalType(block, src, operand_ty), }; - if (try sema.resolveDefinedValue(block, src, operand)) |val| { - if (val.optionalValue(zcu)) |payload| return Air.internedToRef(payload.toIntern()); + ct: { + if (try sema.resolveDefinedValue(block, src, operand)) |val| { + if (val.optionalValue(zcu)) |payload| return .fromValue(payload); // comptime-known payload + } else if (try sema.resolveIsNullFromType(block, src, operand_ty)) |is_null| { + if (!is_null) break :ct; // fully runtime-known + } else { + break :ct; // fully runtime-known + } + // Comptime-known to be `null`. if (block.isComptime()) return sema.fail(block, src, "unable to unwrap null", .{}); if (safety_check and block.wantSafety()) { try sema.safetyPanic(block, src, .unwrap_null); @@ -8784,11 +8234,14 @@ fn zirOptionalPayload( return .unreachable_value; } - try sema.requireRuntimeBlock(block, src, null); if (safety_check and block.wantSafety()) { const is_non_null = try block.addUnOp(.is_non_null, operand); try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); } + + // If the payload is OPV, we need the safety check but have a comptime-known result. + if (try result_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); + return block.addTyOp(.optional_payload, result_ty, operand); } @@ -8805,7 +8258,7 @@ fn zirErrUnionPayload( const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const operand_src = src; const err_union_ty = sema.typeOf(operand); if (err_union_ty.zigTypeTag(zcu) != .error_union) { @@ -8844,8 +8297,8 @@ fn analyzeErrUnionPayload( try sema.addSafetyCheckUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err); } - if (try sema.typeHasOnePossibleValue(payload_ty)) |payload_only_value| { - return Air.internedToRef(payload_only_value.toIntern()); + if (try payload_ty.onePossibleValue(pt)) |payload_opv| { + return .fromValue(payload_opv); } return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand); @@ -8861,12 +8314,17 @@ fn zirErrUnionPayloadPtr( defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const src = block.nodeOffset(inst_data.src_node); + const ptr_ty = sema.typeOf(operand); + assert(ptr_ty.zigTypeTag(sema.pt.zcu) == .pointer); + try sema.ensureLayoutResolved(ptr_ty.childType(sema.pt.zcu), src, .ptr_access); + return sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); } +/// Asserts that the layout of the pointer child type is already resolved. fn analyzeErrUnionPayloadPtr( sema: *Sema, block: *Block, @@ -8887,8 +8345,9 @@ fn analyzeErrUnionPayloadPtr( } const err_union_ty = operand_ty.childType(zcu); + err_union_ty.assertHasLayout(zcu); const payload_ty = err_union_ty.errorUnionPayload(zcu); - const operand_pointer_ty = try pt.ptrTypeSema(.{ + const operand_pointer_ty = try pt.ptrType(.{ .child = payload_ty.toIntern(), .flags = .{ .is_const = operand_ty.isConstPtr(zcu), @@ -8901,7 +8360,7 @@ fn analyzeErrUnionPayloadPtr( if (sema.isComptimeMutablePtr(ptr_val)) { // Set the error union to non-error at comptime. // If the payload is OPV, we must use that value instead of undef. - const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty); + const payload_val = try payload_ty.onePossibleValue(pt) orelse try pt.undefValue(payload_ty); const eu_val = try pt.intern(.{ .error_union = .{ .ty = err_union_ty.toIntern(), .val = .{ .payload = payload_val.toIntern() }, @@ -8948,7 +8407,7 @@ fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); return sema.analyzeErrUnionCode(block, src, operand); } @@ -8984,7 +8443,7 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); return sema.analyzeErrUnionCodePtr(block, src, operand); } @@ -9302,11 +8761,12 @@ fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: } } -fn checkParamTypeCommon( +fn checkParamType( sema: *Sema, block: *Block, param_idx: u32, param_ty: Type, + param_is_comptime: bool, param_is_noalias: bool, param_src: LazySrcLoc, cc: std.builtin.CallingConvention, @@ -9321,29 +8781,22 @@ fn checkParamTypeCommon( opaque_str, param_ty.fmt(pt), }); } - if (!param_ty.isGenericPoison() and - !target_util.fnCallConvAllowsZigTypes(cc) and - !try sema.validateExternType(param_ty, .param_ty)) - { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(param_src, "parameter of type '{f}' not allowed in function with calling convention '{s}'", .{ - param_ty.fmt(pt), @tagName(cc), - }); - errdefer msg.destroy(sema.gpa); - - try sema.explainWhyTypeIsNotExtern(msg, param_src, param_ty, .param_ty); - - try sema.addDeclaredHereNote(msg, param_ty); - break :msg msg; - }); + if (!target_util.fnCallConvAllowsZigTypes(cc)) { + if (param_is_comptime) { + return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{t}'", .{cc}); + } + if (param_ty.isGenericPoison()) { + return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{t}'", .{cc}); + } + // The `validateExtern` check happens later, in `validateResolvedFuncType`. } switch (cc) { .x86_64_interrupt, .x86_interrupt => { const err_code_size = target.ptrBitWidth(); switch (param_idx) { - 0 => if (param_ty.zigTypeTag(zcu) != .pointer) return sema.fail(block, param_src, "first parameter of function with '{s}' calling convention must be a pointer type", .{@tagName(cc)}), - 1 => if (param_ty.bitSize(zcu) != err_code_size) return sema.fail(block, param_src, "second parameter of function with '{s}' calling convention must be a {d}-bit integer", .{ @tagName(cc), err_code_size }), - else => return sema.fail(block, param_src, "'{s}' calling convention supports up to 2 parameters, found {d}", .{ @tagName(cc), param_idx + 1 }), + 0 => if (param_ty.zigTypeTag(zcu) != .pointer) return sema.fail(block, param_src, "first parameter of function with '{t}' calling convention must be a pointer type", .{cc}), + 1 => if (param_ty.bitSize(zcu) != err_code_size) return sema.fail(block, param_src, "second parameter of function with '{t}' calling convention must be a {d}-bit integer", .{ cc, err_code_size }), + else => return sema.fail(block, param_src, "'{t}' calling convention supports up to 2 parameters, found {d}", .{ cc, param_idx + 1 }), } }, .arc_interrupt, @@ -9359,7 +8812,7 @@ fn checkParamTypeCommon( .m68k_interrupt, .msp430_interrupt, .avr_signal, - => return sema.fail(block, param_src, "parameters are not allowed with '{s}' calling convention", .{@tagName(cc)}), + => return sema.fail(block, param_src, "parameters are not allowed with '{t}' calling convention", .{cc}), else => {}, } if (param_is_noalias and !param_ty.isGenericPoison() and !param_ty.isPtrAtRuntime(zcu) and !param_ty.isSliceAtRuntime(zcu)) { @@ -9367,7 +8820,7 @@ fn checkParamTypeCommon( } } -fn checkReturnTypeAndCallConvCommon( +fn checkReturnTypeAndCallConv( sema: *Sema, block: *Block, bare_ret_ty: Type, @@ -9381,7 +8834,6 @@ fn checkReturnTypeAndCallConvCommon( ) CompileError!void { const pt = sema.pt; const zcu = pt.zcu; - const gpa = zcu.gpa; if (opt_varargs_src) |varargs_src| { try sema.checkCallConvSupportsVarArgs(block, varargs_src, @"callconv"); } @@ -9395,21 +8847,14 @@ fn checkReturnTypeAndCallConvCommon( opaque_str, ies_ret_ty_prefix, bare_ret_ty.fmt(pt), }); } - if (!bare_ret_ty.isGenericPoison() and - !target_util.fnCallConvAllowsZigTypes(@"callconv") and - (inferred_error_set or !try sema.validateExternType(bare_ret_ty, .ret_ty))) - { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(ret_ty_src, "return type '{s}{f}' not allowed in function with calling convention '{s}'", .{ - ies_ret_ty_prefix, bare_ret_ty.fmt(pt), @tagName(@"callconv"), - }); - errdefer msg.destroy(gpa); - if (!inferred_error_set) { - try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src, bare_ret_ty, .ret_ty); - try sema.addDeclaredHereNote(msg, bare_ret_ty); - } - break :msg msg; - }); + if (!target_util.fnCallConvAllowsZigTypes(@"callconv")) { + if (inferred_error_set) { + return sema.fail(block, ret_ty_src, "return type '!{f}' not allowed in function with calling convention '{t}'", .{ bare_ret_ty.fmt(pt), @"callconv" }); + } + if (bare_ret_ty.isGenericPoison()) { + return sema.fail(block, ret_ty_src, "generic return type not allowed in function with calling convention '{t}'", .{@"callconv"}); + } + // The `validateExtern` check happens later, in `validateResolvedFuncType`. } validate_incoming_stack_align: { const a: u64 = switch (@"callconv") { @@ -9444,7 +8889,7 @@ fn checkReturnTypeAndCallConvCommon( else => false, }; if (!ret_ok) { - return sema.fail(block, ret_ty_src, "function with calling convention '{s}' must return 'void' or 'noreturn'", .{@tagName(@"callconv")}); + return sema.fail(block, ret_ty_src, "function with calling convention '{t}' must return 'void' or 'noreturn'", .{@"callconv"}); } }, .@"inline" => if (is_noinline) { @@ -9465,18 +8910,76 @@ fn checkReturnTypeAndCallConvCommon( } } }; - return sema.fail(block, callconv_src, "calling convention '{s}' only available on architectures {f}", .{ - @tagName(@"callconv"), - ArchListFormatter{ .archs = allowed_archs }, + return sema.fail(block, callconv_src, "calling convention '{t}' only available on architectures {f}", .{ + @"callconv", ArchListFormatter{ .archs = allowed_archs }, }); }, - .bad_backend => |bad_backend| return sema.fail(block, callconv_src, "calling convention '{s}' not supported by compiler backend '{s}'", .{ - @tagName(@"callconv"), - @tagName(bad_backend), + .bad_backend => |bad_backend| return sema.fail(block, callconv_src, "calling convention '{t}' not supported by compiler backend '{t}'", .{ + @"callconv", bad_backend, }), } } +/// To avoid forcing type layout resolution too quickly, some validation of function types cannot be +/// performed when the type is first constructed, and instead must happen when either (a) a function +/// with that type is declared, or (b) a function with that type is called. That validation is +/// handled here. +/// +/// Asserts that all parameter types and return types have their layout fully resolved. +fn validateResolvedFuncType( + sema: *Sema, + block: *Block, + @"callconv": std.builtin.CallingConvention, + param_types: []const InternPool.Index, + ret_ty: Type, + src: LazySrcLoc, + maybe_func_decl_inst: ?InternPool.TrackedInst.Index, +) SemaError!void { + const pt = sema.pt; + const zcu = pt.zcu; + const gpa = zcu.comp.gpa; + if (!target_util.fnCallConvAllowsZigTypes(@"callconv")) { + // Check that all parameter types are extern-compatible. + for (param_types, 0..) |param_ty_ip, param_index| { + const param_ty: Type = .fromInterned(param_ty_ip); + if (!param_ty.validateExtern(.param_ty, zcu)) { + const param_src: LazySrcLoc = if (maybe_func_decl_inst) |inst| .{ + .base_node_inst = inst, + .offset = .{ .fn_proto_param = .{ + .fn_proto_node_offset = .zero, + .param_index = @intCast(param_index), + } }, + } else src; + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(param_src, "parameter of type '{f}' not allowed in function with calling convention '{t}'", .{ + param_ty.fmt(pt), @"callconv", + }); + errdefer msg.destroy(gpa); + try sema.explainWhyTypeIsNotExtern(msg, param_src, param_ty, .param_ty); + try sema.addDeclaredHereNote(msg, param_ty); + break :msg msg; + }); + } + } + // Check that the return type is extern-compatible. + if (!ret_ty.validateExtern(.ret_ty, zcu)) { + const ret_ty_src: LazySrcLoc = if (maybe_func_decl_inst) |inst| .{ + .base_node_inst = inst, + .offset = .{ .node_offset_fn_type_ret_ty = .zero }, + } else src; + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(ret_ty_src, "return type '{f}' not allowed in function with calling convention '{t}'", .{ + ret_ty.fmt(pt), @"callconv", + }); + errdefer msg.destroy(gpa); + try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src, ret_ty, .ret_ty); + try sema.addDeclaredHereNote(msg, ret_ty); + break :msg msg; + }); + } + } +} + fn callConvIsCallable(cc: std.builtin.CallingConvention.Tag) bool { return switch (cc) { .naked, @@ -9569,12 +9072,9 @@ fn funcCommon( const io = comp.io; const ip = &zcu.intern_pool; + const src = block.nodeOffset(src_node_offset); const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = src_node_offset }); const cc_src = block.src(.{ .node_offset_fn_type_cc = src_node_offset }); - const func_src = block.nodeOffset(src_node_offset); - - const ret_ty_requires_comptime = try bare_return_type.comptimeOnlySema(pt); - var is_generic = bare_return_type.isGenericPoison() or ret_ty_requires_comptime; var comptime_bits: u32 = 0; for (block.params.items(.ty), block.params.items(.is_comptime), 0..) |param_ty_ip, param_is_comptime, i| { @@ -9587,49 +9087,21 @@ fn funcCommon( .fn_proto_node_offset = src_node_offset, .param_index = @intCast(i), } }); - const param_ty_comptime = try param_ty.comptimeOnlySema(pt); - const param_ty_generic = param_ty.isGenericPoison(); - if (param_is_comptime or param_ty_comptime or param_ty_generic) { - is_generic = true; - } if (param_is_comptime) { comptime_bits |= @as(u32, 1) << @intCast(i); // TODO: handle cast error } - if (param_is_comptime and !target_util.fnCallConvAllowsZigTypes(cc)) { - return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)}); - } - if (param_ty_generic and !target_util.fnCallConvAllowsZigTypes(cc)) { - return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)}); - } - try sema.checkParamTypeCommon( + try sema.checkParamType( block, @intCast(i), param_ty, + param_is_comptime, is_noalias, param_src, cc, ); - if (param_ty_comptime and !param_is_comptime and has_body and !block.isComptime()) { - const msg = msg: { - const msg = try sema.errMsg(param_src, "parameter of type '{f}' must be declared comptime", .{ - param_ty.fmt(pt), - }); - errdefer msg.destroy(sema.gpa); - - try sema.explainWhyTypeIsComptime(msg, param_src, param_ty); - - try sema.addDeclaredHereNote(msg, param_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - } - - if (var_args and is_generic) { - return sema.fail(block, func_src, "generic function cannot be variadic", .{}); } - try sema.checkReturnTypeAndCallConvCommon( + try sema.checkReturnTypeAndCallConv( block, bare_return_type, ret_ty_src, @@ -9643,48 +9115,28 @@ fn funcCommon( is_noinline, ); - // If the return type is comptime-only but not dependent on parameters then - // all parameter types also need to be comptime. - if (has_body and ret_ty_requires_comptime and !block.isComptime()) comptime_check: { - for (block.params.items(.is_comptime)) |is_comptime| { - if (!is_comptime) break; - } else break :comptime_check; - const ies_ret_ty_prefix: []const u8 = if (inferred_error_set) "!" else ""; - const msg = try sema.errMsg( - ret_ty_src, - "function with comptime-only return type '{s}{f}' requires all parameters to be comptime", - .{ ies_ret_ty_prefix, bare_return_type.fmt(pt) }, + const param_types = block.params.items(.ty); + + if (has_body) { + for (param_types, 0..) |param_ty_ip, param_index| { + const param_ty: Type = .fromInterned(param_ty_ip); + const param_src = block.src(.{ .fn_proto_param = .{ + .fn_proto_node_offset = src_node_offset, + .param_index = @intCast(param_index), + } }); + try sema.ensureLayoutResolved(param_ty, param_src, .parameter); + } + try sema.ensureLayoutResolved(bare_return_type, ret_ty_src, .return_type); + try sema.validateResolvedFuncType( + block, + cc, + param_types, + bare_return_type, + src, + ip.getNav(sema.owner.unwrap().nav_val).srcInst(ip), ); - errdefer msg.destroy(sema.gpa); - try sema.explainWhyTypeIsComptime(msg, ret_ty_src, bare_return_type); - - const tags = sema.code.instructions.items(.tag); - const data = sema.code.instructions.items(.data); - const param_body = sema.code.getParamBody(func_inst); - for ( - block.params.items(.is_comptime), - block.params.items(.name), - param_body[0..block.params.len], - ) |is_comptime, name_nts, param_index| { - if (!is_comptime) { - const param_src = block.tokenOffset(switch (tags[@intFromEnum(param_index)]) { - .param => data[@intFromEnum(param_index)].pl_tok.src_tok, - .param_anytype => data[@intFromEnum(param_index)].str_tok.src_tok, - else => unreachable, - }); - const name = sema.code.nullTerminatedString(name_nts); - if (name.len != 0) { - try sema.errNote(param_src, msg, "param '{s}' is required to be comptime", .{name}); - } else { - try sema.errNote(param_src, msg, "param is required to be comptime", .{}); - } - } - } - return sema.failWithOwnedErrorMsg(block, msg); } - const param_types = block.params.items(.ty); - if (inferred_error_set) { assert(has_body); return .fromIntern(try ip.getFuncDeclIes(gpa, io, pt.tid, .{ @@ -9696,7 +9148,6 @@ fn funcCommon( .bare_return_type = bare_return_type.toIntern(), .cc = cc, .is_var_args = var_args, - .is_generic = is_generic, .is_noinline = is_noinline, .zir_body_inst = try block.trackZir(func_inst), @@ -9714,7 +9165,6 @@ fn funcCommon( .return_type = bare_return_type.toIntern(), .cc = cc, .is_var_args = var_args, - .is_generic = is_generic, .is_noinline = is_noinline, }); @@ -9756,7 +9206,7 @@ fn zirParam( } const param_ty_inst = try sema.resolveInlineBody(block, body, inst); - break :ty try sema.analyzeAsType(block, src, param_ty_inst); + break :ty try sema.analyzeAsType(block, src, .fn_param_types, param_ty_inst); }; try block.params.append(sema.arena, .{ @@ -9812,7 +9262,7 @@ fn analyzeAs( ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; - const operand = try sema.resolveInst(zir_operand); + const operand = sema.resolveInst(zir_operand); const dest_ty = try sema.resolveTypeOrPoison(block, src, zir_dest_type) orelse return operand; switch (dest_ty.zigTypeTag(zcu)) { .@"opaque" => return sema.fail(block, src, "cannot cast to opaque type '{f}'", .{dest_ty.fmt(pt)}), @@ -9838,32 +9288,23 @@ fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 0); - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); const ptr_ty = operand_ty.scalarType(zcu); const is_vector = operand_ty.zigTypeTag(zcu) == .vector; if (!ptr_ty.isPtrAtRuntime(zcu)) { return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ty.fmt(pt)}); } - const pointee_ty = ptr_ty.childType(zcu); - if (try ptr_ty.comptimeOnlySema(pt)) { - const msg = msg: { - const msg = try sema.errMsg(ptr_src, "comptime-only type '{f}' has no pointer address", .{pointee_ty.fmt(pt)}); - errdefer msg.destroy(sema.gpa); - try sema.explainWhyTypeIsComptime(msg, ptr_src, pointee_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } + const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined; const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .usize_type, .len = len }) else .usize; - if (try sema.resolveValue(operand)) |operand_val| ct: { + if (sema.resolveValue(operand)) |operand_val| ct: { if (!is_vector) { if (operand_val.isUndef(zcu)) { return .undef_usize; } - const addr = try operand_val.getUnsignedIntSema(pt) orelse { + const addr = operand_val.getUnsignedInt(zcu) orelse { // Wasn't an integer pointer. This is a runtime operation. break :ct; }; @@ -9879,7 +9320,7 @@ fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! new_elem.* = .undef_usize; continue; } - const addr = try ptr_val.getUnsignedIntSema(pt) orelse { + const addr = ptr_val.getUnsignedInt(zcu) orelse { // A vector element wasn't an integer pointer. This is a runtime operation. break :ct; }; @@ -9917,7 +9358,7 @@ fn zirFieldPtrLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro sema.code.nullTerminatedString(extra.field_name_start), .no_embedded_nulls, ); - const object_ptr = try sema.resolveInst(extra.lhs); + const object_ptr = sema.resolveInst(extra.lhs); return fieldPtrLoad(sema, block, src, object_ptr, field_name, field_name_src); } @@ -9942,7 +9383,7 @@ fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai sema.code.nullTerminatedString(extra.field_name_start), .no_embedded_nulls, ); - const object_ptr = try sema.resolveInst(extra.lhs); + const object_ptr = sema.resolveInst(extra.lhs); return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false); } @@ -9967,7 +9408,7 @@ fn zirStructInitFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi sema.code.nullTerminatedString(extra.field_name_start), .no_embedded_nulls, ); - const object_ptr = try sema.resolveInst(extra.lhs); + const object_ptr = sema.resolveInst(extra.lhs); const struct_ty = sema.typeOf(object_ptr).childType(zcu); switch (struct_ty.zigTypeTag(zcu)) { .@"struct", .@"union" => { @@ -9987,7 +9428,7 @@ fn zirFieldPtrNamedLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil const src = block.nodeOffset(inst_data.src_node); const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1); const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; - const object_ptr = try sema.resolveInst(extra.lhs); + const object_ptr = sema.resolveInst(extra.lhs); const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name }); return fieldPtrLoad(sema, block, src, object_ptr, field_name, field_name_src); } @@ -10000,7 +9441,7 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const src = block.nodeOffset(inst_data.src_node); const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1); const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; - const object_ptr = try sema.resolveInst(extra.lhs); + const object_ptr = sema.resolveInst(extra.lhs); const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name }); return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false); } @@ -10015,7 +9456,7 @@ fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intCast"); - const operand = try sema.resolveInst(extra.rhs); + const operand = sema.resolveInst(extra.rhs); return sema.intCast(block, block.nodeOffset(inst_data.src_node), dest_ty, src, operand, operand_src); } @@ -10044,7 +9485,7 @@ fn intCast( try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src); const is_vector = dest_ty.zigTypeTag(zcu) == .vector; - if ((try sema.typeHasOnePossibleValue(dest_ty))) |opv| { + if (try dest_ty.onePossibleValue(pt)) |opv| { // requirement: intCast(u0, input) iff input == 0 if (block.wantSafety()) { try sema.requireRuntimeBlock(block, src, operand_src); @@ -10090,7 +9531,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@bitCast"); - const operand = try sema.resolveInst(extra.rhs); + const operand = sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); switch (dest_ty.zigTypeTag(zcu)) { .@"anyframe", @@ -10258,7 +9699,7 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatCast"); const dest_scalar_ty = dest_ty.scalarType(zcu); - const operand = try sema.resolveInst(extra.rhs); + const operand = sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); const operand_scalar_ty = operand_ty.scalarType(zcu); @@ -10287,7 +9728,7 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A ), } - if (try sema.resolveValue(operand)) |operand_val| { + if (sema.resolveValue(operand)) |operand_val| { if (!is_vector) { return Air.internedToRef((try operand_val.floatCast(dest_ty, pt)).toIntern()); } @@ -10319,8 +9760,8 @@ fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; const src = block.nodeOffset(inst_data.src_node); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const array = try sema.resolveInst(extra.lhs); - const elem_index = try sema.resolveInst(extra.rhs); + const array = sema.resolveInst(extra.lhs); + const elem_index = sema.resolveInst(extra.rhs); return sema.elemVal(block, src, array, elem_index, src, false); } @@ -10332,8 +9773,8 @@ fn zirElemPtrLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const src = block.nodeOffset(inst_data.src_node); const elem_index_src = block.src(.{ .node_offset_array_access_index = inst_data.src_node }); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const array_ptr = try sema.resolveInst(extra.lhs); - const uncoerced_elem_index = try sema.resolveInst(extra.rhs); + const array_ptr = sema.resolveInst(extra.lhs); + const uncoerced_elem_index = sema.resolveInst(extra.rhs); if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| { const array_ptr_ty = sema.typeOf(array_ptr); if (try sema.pointerDeref(block, src, array_ptr_val, array_ptr_ty)) |array_val| { @@ -10351,7 +9792,7 @@ fn zirElemValImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].elem_val_imm; - const array = try sema.resolveInst(inst_data.operand); + const array = sema.resolveInst(inst_data.operand); const elem_index = try sema.pt.intRef(.usize, inst_data.idx); return sema.elemVal(block, LazySrcLoc.unneeded, array, elem_index, LazySrcLoc.unneeded, false); } @@ -10365,8 +9806,8 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; const src = block.nodeOffset(inst_data.src_node); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const array_ptr = try sema.resolveInst(extra.lhs); - const elem_index = try sema.resolveInst(extra.rhs); + const array_ptr = sema.resolveInst(extra.lhs); + const elem_index = sema.resolveInst(extra.rhs); const indexable_ty = sema.typeOf(array_ptr); if (indexable_ty.zigTypeTag(zcu) != .pointer) { const capture_src = block.src(.{ .for_capture_from_input = inst_data.src_node }); @@ -10382,6 +9823,8 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air }; return sema.failWithOwnedErrorMsg(block, msg); } + try sema.checkIndexable(block, src, indexable_ty); + try sema.ensureLayoutResolved(indexable_ty.childType(zcu), src, .ptr_access); return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false, false); } @@ -10393,8 +9836,8 @@ fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const src = block.nodeOffset(inst_data.src_node); const elem_index_src = block.src(.{ .node_offset_array_access_index = inst_data.src_node }); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const array_ptr = try sema.resolveInst(extra.lhs); - const uncoerced_elem_index = try sema.resolveInst(extra.rhs); + const array_ptr = sema.resolveInst(extra.lhs); + const uncoerced_elem_index = sema.resolveInst(extra.rhs); const elem_index = try sema.coerce(block, .usize, uncoerced_elem_index, elem_index_src); return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false, true); } @@ -10408,7 +9851,7 @@ fn zirArrayInitElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compile const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; const src = block.nodeOffset(inst_data.src_node); const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data; - const array_ptr = try sema.resolveInst(extra.ptr); + const array_ptr = sema.resolveInst(extra.ptr); const elem_index = try pt.intRef(.usize, extra.index); const array_ty = sema.typeOf(array_ptr).childType(zcu); switch (array_ty.zigTypeTag(zcu)) { @@ -10427,8 +9870,8 @@ fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; const src = block.nodeOffset(inst_data.src_node); const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data; - const array_ptr = try sema.resolveInst(extra.lhs); - const start = try sema.resolveInst(extra.start); + const array_ptr = sema.resolveInst(extra.lhs); + const start = sema.resolveInst(extra.start); const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node }); const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node }); @@ -10443,9 +9886,9 @@ fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; const src = block.nodeOffset(inst_data.src_node); const extra = sema.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data; - const array_ptr = try sema.resolveInst(extra.lhs); - const start = try sema.resolveInst(extra.start); - const end = try sema.resolveInst(extra.end); + const array_ptr = sema.resolveInst(extra.lhs); + const start = sema.resolveInst(extra.start); + const end = sema.resolveInst(extra.end); const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node }); const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node }); @@ -10461,10 +9904,10 @@ fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const src = block.nodeOffset(inst_data.src_node); const sentinel_src = block.src(.{ .node_offset_slice_sentinel = inst_data.src_node }); const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data; - const array_ptr = try sema.resolveInst(extra.lhs); - const start = try sema.resolveInst(extra.start); - const end: Air.Inst.Ref = if (extra.end == .none) .none else try sema.resolveInst(extra.end); - const sentinel = try sema.resolveInst(extra.sentinel); + const array_ptr = sema.resolveInst(extra.lhs); + const start = sema.resolveInst(extra.start); + const end: Air.Inst.Ref = if (extra.end == .none) .none else sema.resolveInst(extra.end); + const sentinel = sema.resolveInst(extra.sentinel); const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node }); const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node }); @@ -10479,10 +9922,10 @@ fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; const src = block.nodeOffset(inst_data.src_node); const extra = sema.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data; - const array_ptr = try sema.resolveInst(extra.lhs); - const start = try sema.resolveInst(extra.start); - const len = try sema.resolveInst(extra.len); - const sentinel = if (extra.sentinel == .none) .none else try sema.resolveInst(extra.sentinel); + const array_ptr = sema.resolveInst(extra.lhs); + const start = sema.resolveInst(extra.start); + const len = sema.resolveInst(extra.len); + const sentinel = if (extra.sentinel == .none) .none else sema.resolveInst(extra.sentinel); const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); const start_src = block.src(.{ .node_offset_slice_start = extra.start_src_node_offset }); const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node }); @@ -10510,7 +9953,7 @@ fn zirSliceSentinelTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE // This is like the logic in `analyzeSlice`; since we've evaluated the LHS as an lvalue, we will // have a double pointer if it was already a pointer. - const lhs_ptr_ty = sema.typeOf(try sema.resolveInst(inst_data.operand)); + const lhs_ptr_ty = sema.typeOf(sema.resolveInst(inst_data.operand)); const lhs_ty = switch (lhs_ptr_ty.zigTypeTag(zcu)) { .pointer => lhs_ptr_ty.childType(zcu), else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{lhs_ptr_ty.fmt(pt)}), @@ -10557,9 +10000,9 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp var label: Block.Label = .{ .zir_block = inst, .merges = .{ - .src_locs = .{}, - .results = .{}, - .br_list = .{}, + .src_locs = .empty, + .results = .empty, + .br_list = .empty, .block_inst = block_inst, }, }; @@ -10590,12 +10033,14 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp // Lastly, we analyze the error prong(s) as a regular switch. const raw_switch_operand, const non_err_cond, const non_err_hint = non_err: { - const eu_maybe_ptr = try sema.resolveInst(zir_switch.main_operand); + const eu_maybe_ptr = sema.resolveInst(zir_switch.main_operand); const err_union_ty: Type = err_union_ty: { const raw_operand_ty = sema.typeOf(eu_maybe_ptr); if (!non_err_case.operand_is_ref) break :err_union_ty raw_operand_ty; try sema.checkPtrOperand(block, operand_src, raw_operand_ty); - break :err_union_ty raw_operand_ty.childType(zcu); + const child_ty = raw_operand_ty.childType(zcu); + try sema.ensureLayoutResolved(child_ty, operand_src, .ptr_access); + break :err_union_ty child_ty; }; if (err_union_ty.zigTypeTag(zcu) != .error_union) { return sema.fail(block, operand_src, "expected error union type, found '{f}'", .{ @@ -10711,9 +10156,9 @@ fn zirSwitchBlock( var label: Block.Label = .{ .zir_block = inst, .merges = .{ - .src_locs = .{}, - .results = .{}, - .br_list = .{}, + .src_locs = .empty, + .results = .empty, + .br_list = .empty, .block_inst = block_inst, }, }; @@ -10723,7 +10168,7 @@ fn zirSwitchBlock( defer child_block.instructions.deinit(sema.gpa); defer merges.deinit(sema.gpa); - const raw_operand = try sema.resolveInst(zir_switch.main_operand); + const raw_operand = sema.resolveInst(zir_switch.main_operand); const validated_switch = try sema.validateSwitchBlock(block, raw_operand, operand_is_ref, inst, &zir_switch); const maybe_ref = try sema.analyzeSwitchBlock(block, &child_block, raw_operand, operand_is_ref, merges, inst, &zir_switch, &validated_switch); return maybe_ref orelse { @@ -10764,18 +10209,19 @@ fn analyzeSwitchBlock( .{ raw_operand, .none }; const operand_ty = sema.typeOf(val); - const maybe_operand_opv = try sema.typeHasOnePossibleValue(operand_ty); + operand_ty.assertHasLayout(zcu); + const maybe_operand_opv = try operand_ty.onePossibleValue(pt); const init_cond: Air.Inst.Ref, const item_ty: Type = switch (operand_ty.zigTypeTag(zcu)) { .@"union" => tag: { - const tag_ty = operand_ty.unionTagType(zcu).?; - const tag_val = try sema.unionToTag(block, tag_ty, val, operand_src); - break :tag .{ tag_val, tag_ty }; + const tag_val = try sema.unionToTag(block, val); + break :tag .{ tag_val, sema.typeOf(tag_val) }; }, else => .{ if (maybe_operand_opv) |operand_opv| .fromValue(operand_opv) else val, operand_ty, }, }; + item_ty.assertHasLayout(zcu); if (zir_switch.has_continue and !block.isComptime()) { const operand_alloc: Air.Inst.Ref = if (zir_switch.any_maybe_runtime_capture and @@ -10849,7 +10295,7 @@ fn analyzeSwitchBlock( if (extra.block_inst != switch_inst) return error.ComptimeBreak; // This is a `switch_continue` targeting this block. Change the operand and start over. const new_operand_src = child_block.nodeOffset(extra.operand_src_node.unwrap().?); - const new_operand_uncoerced = try sema.resolveInst(break_inst.data.@"break".operand); + const new_operand_uncoerced = sema.resolveInst(break_inst.data.@"break".operand); const new_operand = try sema.coerce(child_block, raw_operand_ty, new_operand_uncoerced, new_operand_src); try sema.emitBackwardBranch(child_block, src); @@ -10860,7 +10306,7 @@ fn analyzeSwitchBlock( .{ new_operand, .none }; const new_cond_ref = if (union_originally) - try sema.unionToTag(child_block, item_ty, new_val, src) + try sema.unionToTag(child_block, new_val) else new_val; @@ -10881,7 +10327,7 @@ fn analyzeSwitchBlock( unreachable; } - if (try sema.typeHasOnePossibleValue(item_ty)) |item_opv| { + if (try item_ty.onePossibleValue(pt)) |item_opv| { // We simplify conditions with OPV to either a `loop` or a `block` since // we cannot switch on a value which doesn't exist at runtime. assert(operand == .loop); // `simple` should have already been comptime-resolved above! @@ -10912,7 +10358,7 @@ fn analyzeSwitchBlock( assert(case.range_infos.len == 0); for (case.item_infos, item_refs) |item_info, item_ref| { if (item_info.bodyLen()) |body_len| extra_index += body_len; - if (sema.wantSwitchProngBodyAnalysis(block, item_ref, operand_ty, false, true, prong_info.is_comptime_unreach)) { + if (sema.wantSwitchProngBodyAnalysis(item_ref, operand_ty, false, true, prong_info.is_comptime_unreach)) { break :skip_case; } } @@ -10928,7 +10374,7 @@ fn analyzeSwitchBlock( unreachable; // malformed validated switch }; - const analyze_body = sema.wantSwitchProngBodyAnalysis(block, .fromValue(item_opv), operand_ty, union_originally, err_set, false); + const analyze_body = sema.wantSwitchProngBodyAnalysis(.fromValue(item_opv), operand_ty, union_originally, err_set, false); if (!analyze_body) return .unreachable_value; if (!(err_set and @@ -10938,10 +10384,10 @@ fn analyzeSwitchBlock( const payload_inst: Zir.Inst.Index = if (capture != .none) inst: { const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; const payload_ref: Air.Inst.Ref = payload_ref: { - const item_val: InternPool.Index = switch (operand_ty.zigTypeTag(zcu)) { + const item_val: Value = switch (operand_ty.zigTypeTag(zcu)) { .@"union" => item_val: { if (maybe_operand_opv) |operand_opv| { - break :item_val zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val; + break :item_val .fromInterned(zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val); } assert(union_originally); // operand type must be union, otherwise it would be an OPV type here assert(zir_switch.any_maybe_runtime_capture); // there's a payload capture @@ -10978,10 +10424,10 @@ fn analyzeSwitchBlock( validated_switch.else_err_ty, ); }, - else => item_opv.toIntern(), + else => item_opv, }; break :payload_ref switch (capture) { - .by_val => .fromIntern(item_val), + .by_val => .fromValue(item_val), .by_ref => try sema.uavRef(item_val), .none => unreachable, }; @@ -11186,7 +10632,7 @@ fn finishSwitchBr( if (item_ref == .none) is_under_prong = true; if (item_info.bodyLen()) |body_len| extra_index += body_len; - const analyze_body = sema.wantSwitchProngBodyAnalysis(block, item_ref, operand_ty, union_originally, err_set, prong_info.is_comptime_unreach); + const analyze_body = sema.wantSwitchProngBodyAnalysis(item_ref, operand_ty, union_originally, err_set, prong_info.is_comptime_unreach); if (analyze_body) any_analyze_body = true; if (prong_info.is_inline) { @@ -11246,11 +10692,11 @@ fn finishSwitchBr( any_analyze_body = true; // always an integer range, always needs analysis if (prong_info.is_inline) { - var item = sema.resolveConstDefinedValue(block, .unneeded, range_ref[0], undefined) catch unreachable; - const item_last = sema.resolveConstDefinedValue(block, .unneeded, range_ref[1], undefined) catch unreachable; + var item = sema.resolveValue(range_ref[0]).?; + const item_last = sema.resolveValue(range_ref[1]).?; - if (try item.getUnsignedIntSema(pt)) |first_int| { - if (try item_last.getUnsignedIntSema(pt)) |last_int| { + if (item.getUnsignedInt(zcu)) |first_int| { + if (item_last.getUnsignedInt(zcu)) |last_int| { if (std.math.cast(u32, last_int - first_int)) |range_len| { try branch_hints.ensureUnusedCapacity(gpa, range_len); } @@ -11259,7 +10705,6 @@ fn finishSwitchBr( var prev_result_overflowed = false; while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({ - // Previous validation has resolved any possible lazy values. const int_val: Value, const int_ty: Type = switch (operand_ty.zigTypeTag(zcu)) { .int => .{ item, operand_ty }, .@"enum" => b: { @@ -11426,7 +10871,7 @@ fn finishSwitchBr( const item_ref: Air.Inst.Ref = .fromValue(item_val); - const analyze_body = sema.wantSwitchProngBodyAnalysis(block, item_ref, operand_ty, union_originally, err_set, false); + const analyze_body = sema.wantSwitchProngBodyAnalysis(item_ref, operand_ty, union_originally, err_set, false); if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src); emit_bb = true; @@ -11896,72 +11341,69 @@ fn validateSwitchBlock( try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst}); } - const operand_ty: Type, const item_ty: Type = check_operand: { - const operand_ty = operand_ty: { - const raw_operand_ty = sema.typeOf(raw_operand); - if (operand_is_ref) { - try sema.checkPtrType(block, operand_src, raw_operand_ty, false); - break :operand_ty raw_operand_ty.childType(zcu); - } - break :operand_ty raw_operand_ty; - }; - - const item_ty: Type = item_ty: { - switch (operand_ty.zigTypeTag(zcu)) { - .@"enum", - .error_set, - .int, - .comptime_int, - .type, - .enum_literal, - .@"fn", - .bool, - .void, - => break :item_ty operand_ty, + const operand_ty = operand_ty: { + const raw_operand_ty = sema.typeOf(raw_operand); + if (operand_is_ref) { + try sema.checkPtrType(block, operand_src, raw_operand_ty, false); + const child_ty = raw_operand_ty.childType(zcu); + try sema.ensureLayoutResolved(child_ty, operand_src, .ptr_access); + break :operand_ty child_ty; + } + break :operand_ty raw_operand_ty; + }; - .@"union" => { - try operand_ty.resolveFields(pt); - const enum_ty = operand_ty.unionTagType(zcu) orelse { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(operand_src, "switch on union with no attached enum", .{}); - errdefer msg.destroy(sema.gpa); - if (operand_ty.srcLocOrNull(zcu)) |union_src| { - try sema.errNote(union_src, msg, "consider 'union(enum)' here", .{}); - } - break :msg msg; - }); - }; - break :item_ty enum_ty; - }, + const item_ty: Type = item_ty: { + switch (operand_ty.zigTypeTag(zcu)) { + .@"enum", + .error_set, + .int, + .comptime_int, + .type, + .enum_literal, + .@"fn", + .bool, + .void, + => break :item_ty operand_ty, - .pointer => { - if (!operand_ty.isSlice(zcu)) { - break :item_ty operand_ty; - } - }, + .@"union" => { + const enum_ty = operand_ty.unionTagType(zcu) orelse { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(operand_src, "switch on union with no attached enum", .{}); + errdefer msg.destroy(sema.gpa); + if (operand_ty.srcLocOrNull(zcu)) |union_src| { + try sema.errNote(union_src, msg, "consider 'union(enum)' here", .{}); + } + break :msg msg; + }); + }; + break :item_ty enum_ty; + }, - else => {}, - } - return sema.fail(block, operand_src, "switch on type '{f}'", .{operand_ty.fmt(pt)}); - }; + .pointer => { + if (!operand_ty.isSlice(zcu)) { + break :item_ty operand_ty; + } + }, - if (zir_switch.has_continue and !block.isComptime()) { - if (try operand_ty.comptimeOnlySema(pt)) { - // Even if the operand is comptime-known, this `switch` is runtime. - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(operand_src, "operand of switch loop has comptime-only type '{f}'", .{operand_ty.fmt(pt)}); - errdefer msg.destroy(gpa); - try sema.errNote(operand_src, msg, "switch loops are evaluated at runtime outside of comptime scopes", .{}); - try sema.explainWhyTypeIsComptime(msg, operand_src, operand_ty); - break :msg msg; - }); - } - try sema.validateRuntimeValue(block, operand_src, raw_operand); + else => {}, } - - break :check_operand .{ operand_ty, item_ty }; + return sema.fail(block, operand_src, "switch on type '{f}'", .{operand_ty.fmt(pt)}); }; + if (zir_switch.has_continue and !block.isComptime()) { + if (operand_ty.comptimeOnly(zcu)) { + // Even if the operand is comptime-known, this `switch` is runtime. + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(operand_src, "operand of switch loop has comptime-only type '{f}'", .{operand_ty.fmt(pt)}); + errdefer msg.destroy(gpa); + try sema.errNote(operand_src, msg, "switch loops are evaluated at runtime outside of comptime scopes", .{}); + try sema.explainWhyTypeIsComptime(msg, operand_src, operand_ty); + break :msg msg; + }); + } + try sema.validateRuntimeValue(block, operand_src, raw_operand); + } + const has_else = zir_switch.else_case != null; const has_under = zir_switch.has_under; @@ -12305,7 +11747,7 @@ fn resolveSwitchBlock( child_block: *Block, operand: SwitchOperand, raw_operand_ty: Type, - maybe_lazy_cond_val: Value, + cond_val: Value, merges: *Block.Merges, switch_inst: Zir.Inst.Index, zir_switch: *const Zir.UnwrappedSwitchBlock, @@ -12325,9 +11767,6 @@ fn resolveSwitchBlock( const err_set = item_ty.zigTypeTag(zcu) == .error_set; const cond_ref = operand.simple.cond; - // We have to resolve lazy values to ensure that comparisons with switch - // prong items don't produce false negatives. - const cond_val = try sema.resolveLazyValue(maybe_lazy_cond_val); const case_vals = validated_switch.case_vals; var case_val_idx: usize = 0; @@ -12365,7 +11804,7 @@ fn resolveSwitchBlock( }; continue; } - const item_val = sema.resolveConstDefinedValue(child_block, .unneeded, item_ref, undefined) catch unreachable; + const item_val = sema.resolveValue(item_ref).?; if (cond_val.eql(item_val, item_ty, zcu)) { if (err_set) try sema.maybeErrorUnwrapComptime(child_block, prong_body, cond_ref); if (union_originally and operand_ty.unionFieldType(item_val, zcu).?.isNoReturn(zcu)) { @@ -12398,8 +11837,8 @@ fn resolveSwitchBlock( } } for (range_refs) |range_ref| { - const first_val = sema.resolveConstDefinedValue(child_block, .unneeded, range_ref[0], undefined) catch unreachable; - const last_val = sema.resolveConstDefinedValue(child_block, .unneeded, range_ref[1], undefined) catch unreachable; + const first_val = sema.resolveValue(range_ref[0]).?; + const last_val = sema.resolveValue(range_ref[1]).?; if ((try sema.compareAll(cond_val, .gte, first_val, item_ty)) and (try sema.compareAll(cond_val, .lte, last_val, item_ty))) { @@ -12608,7 +12047,6 @@ fn resolveSwitchProng( fn wantSwitchProngBodyAnalysis( sema: *Sema, - block: *Block, item_ref: Air.Inst.Ref, operand_ty: Type, union_originally: bool, @@ -12617,16 +12055,14 @@ fn wantSwitchProngBodyAnalysis( ) bool { const zcu = sema.pt.zcu; if (union_originally) { - const unresolved_item_val = sema.resolveConstDefinedValue(block, .unneeded, item_ref, undefined) catch unreachable; - const item_val = sema.resolveLazyValue(unresolved_item_val) catch unreachable; + const item_val = sema.resolveValue(item_ref).?; const field_ty = operand_ty.unionFieldType(item_val, zcu).?; if (field_ty.isNoReturn(zcu)) return false; } if (err_set and prong_is_comptime_unreach) { - const unresolved_item_val = sema.resolveConstDefinedValue(block, .unneeded, item_ref, undefined) catch unreachable; - const item_val = sema.resolveLazyValue(unresolved_item_val) catch unreachable; + const item_val = sema.resolveValue(item_ref).?; const err_name = item_val.getErrorName(zcu).unwrap().?; - if (!Type.errorSetHasFieldIp(&zcu.intern_pool, operand_ty.toIntern(), err_name)) return false; + if (!operand_ty.errorSetHasField(err_name, zcu)) return false; } return true; } @@ -12772,8 +12208,7 @@ fn analyzeSwitchTagCapture( .item_refs => |refs| if (refs.len == 1) return refs[0], .special => {}, } - const tag_ty = operand_ty.unionTagType(zcu).?; - return sema.unionToTag(case_block, tag_ty, operand_val, tag_capture_src); + return sema.unionToTag(case_block, operand_val); } fn analyzeSwitchPayloadCapture( @@ -12800,14 +12235,14 @@ fn analyzeSwitchPayloadCapture( const switch_node_offset = operand_src.offset.node_offset_switch_operand; if (kind == .inline_ref) { - const item_val = sema.resolveConstDefinedValue(case_block, .unneeded, kind.inline_ref, undefined) catch unreachable; + const item_val = sema.resolveValue(kind.inline_ref).?; if (operand_ty.zigTypeTag(zcu) == .@"union") { const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, zcu).?); const union_obj = zcu.typeToUnion(operand_ty).?; const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); if (capture_by_ref) { const operand_ptr_info = sema.typeOf(operand_ptr).ptrInfo(zcu); - const ptr_field_ty = try pt.ptrTypeSema(.{ + const ptr_field_ty = try pt.ptrType(.{ .child = field_ty.toIntern(), .flags = .{ .is_const = operand_ptr_info.flags.is_const, @@ -12821,10 +12256,11 @@ fn analyzeSwitchPayloadCapture( const tag_and_val = ip.indexToKey(union_val.toIntern()).un; return .fromIntern(tag_and_val.val); } + if (try field_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); return case_block.addStructFieldVal(operand_val, field_index, field_ty); } } else if (capture_by_ref) { - return sema.uavRef(item_val.toIntern()); + return sema.uavRef(item_val); } else { return kind.inline_ref; } @@ -12850,14 +12286,14 @@ fn analyzeSwitchPayloadCapture( const case_vals = kind.item_refs; const union_obj = zcu.typeToUnion(operand_ty).?; - const first_item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable; + const first_item_val = sema.resolveValue(case_vals[0]).?; const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?; const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]); const field_indices = try sema.arena.alloc(u32, case_vals.len); for (case_vals, field_indices) |item, *field_idx| { - const item_val = sema.resolveConstDefinedValue(case_block, .unneeded, item, undefined) catch unreachable; + const item_val = sema.resolveValue(item).?; field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?; } @@ -12906,23 +12342,14 @@ fn analyzeSwitchPayloadCapture( // By-reference captures have some further restrictions which make them easier to emit if (capture_by_ref) { - const operand_ptr_info = sema.typeOf(operand_ptr).ptrInfo(zcu); + const operand_ptr_ty = sema.typeOf(operand_ptr); const capture_ptr_ty = resolve: { // By-ref captures of hetereogeneous types are only allowed if all field // pointer types are peer resolvable to each other. // We need values to run PTR on, so make a bunch of undef constants. const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); - for (field_indices, dummy_captures) |field_idx, *dummy| { - const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); - const field_ptr_ty = try pt.ptrTypeSema(.{ - .child = field_ty.toIntern(), - .flags = .{ - .is_const = operand_ptr_info.flags.is_const, - .is_volatile = operand_ptr_info.flags.is_volatile, - .address_space = operand_ptr_info.flags.address_space, - .alignment = union_obj.fieldAlign(ip, field_idx), - }, - }); + for (field_indices, dummy_captures) |field_index, *dummy| { + const field_ptr_ty = try operand_ptr_ty.fieldPtrType(field_index, pt); dummy.* = try pt.undefRef(field_ptr_ty); } const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); @@ -12963,6 +12390,8 @@ fn analyzeSwitchPayloadCapture( return case_block.addStructFieldPtr(operand_ptr, first_field_index, capture_ptr_ty); } + if (try capture_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); + if (try sema.resolveDefinedValue(case_block, operand_src, operand_val)) |operand_val_val| { if (operand_val_val.isUndef(zcu)) return pt.undefRef(capture_ty); const union_val = ip.indexToKey(operand_val_val.toIntern()).un; @@ -13119,7 +12548,7 @@ fn analyzeSwitchPayloadCapture( try sema.air_instructions.append(sema.gpa, .{ .tag = .get_union_tag, .data = .{ .ty_op = .{ - .ty = .fromIntern(union_obj.enum_tag_ty), + .ty = .fromIntern(union_obj.enum_tag_type), .operand = operand_val, } }, }); @@ -13146,7 +12575,7 @@ fn analyzeSwitchPayloadCapture( const case_vals = kind.item_refs; if (case_vals.len == 1) { - const item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable; + const item_val = sema.resolveValue(case_vals[0]).?; const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?); return sema.bitCast(case_block, item_ty, .fromValue(item_val), operand_src, null); } @@ -13154,7 +12583,7 @@ fn analyzeSwitchPayloadCapture( var names: InferredErrorSet.NameMap = .{}; try names.ensureUnusedCapacity(sema.arena, case_vals.len); for (case_vals) |err| { - const err_val = sema.resolveConstDefinedValue(case_block, .unneeded, err, undefined) catch unreachable; + const err_val = sema.resolveValue(err).?; names.putAssumeCapacityNoClobber(err_val.getErrorName(zcu).unwrap().?, {}); } const error_ty = try pt.errorSetFromUnsortedNames(names.keys()); @@ -13249,7 +12678,7 @@ fn resolveSwitchItem( // We allow prongs with errors which are not part of the error set // being switched on if their prong body is `=> comptime unreachable,`. switch (try sema.coerceInMemoryAllowedErrorSets(block, item_ty, uncoerced_ty, item_src, item_src)) { - .ok => if (try sema.resolveValue(uncoerced)) |uncoerced_val| { + .ok => if (sema.resolveValue(uncoerced)) |uncoerced_val| { break :item_ref try sema.coerceInMemory(uncoerced_val, item_ty); }, .missing_error => if (prong_is_comptime_unreach) { @@ -13261,17 +12690,8 @@ fn resolveSwitchItem( } break :item_ref try sema.coerce(block, item_ty, uncoerced, item_src); }; - const maybe_lazy = try sema.resolveConstDefinedValue(block, item_src, item_ref, .{ .simple = .switch_item }); - - // We have to resolve lazy values here to avoid false negatives when detecting - // duplicate items and comparing items to a comptime-known switch operand. - - const val = try sema.resolveLazyValue(maybe_lazy); - const ref: Air.Inst.Ref = if (val.toIntern() == maybe_lazy.toIntern()) - item_ref - else - .fromValue(val); - return .{ .{ .ref = ref, .val = val }, end }; + const val = try sema.resolveConstDefinedValue(block, item_src, item_ref, .{ .simple = .switch_item }); + return .{ .{ .ref = item_ref, .val = val }, end }; } fn validateSwitchItemOrRange( @@ -13422,7 +12842,7 @@ fn maybeErrorUnwrap( }, .panic => { const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; - const msg_inst = try sema.resolveInst(inst_data.operand); + const msg_inst = sema.resolveInst(inst_data.operand); const panic_fn = try getBuiltin(sema, operand_src, .@"panic.call"); const args: [2]Air.Inst.Ref = .{ msg_inst, .null_value }; @@ -13445,7 +12865,7 @@ fn maybeErrorUnwrapCondbr(sema: *Sema, block: *Block, body: []const Zir.Inst.Ind if (sema.code.instructions.items(.tag)[@intFromEnum(index)] != .is_non_err) return; const err_inst_data = sema.code.instructions.items(.data)[@intFromEnum(index)].un_node; - const err_operand = try sema.resolveInst(err_inst_data.operand); + const err_operand = sema.resolveInst(err_inst_data.operand); const operand_ty = sema.typeOf(err_operand); if (operand_ty.zigTypeTag(zcu) == .error_set) { try sema.maybeErrorUnwrapComptime(block, body, err_operand); @@ -13488,7 +12908,7 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const name_src = block.builtinCallArgSrc(inst_data.src_node, 1); const ty = try sema.resolveType(block, ty_src, extra.lhs); const field_name = try sema.resolveConstStringIntern(block, name_src, extra.rhs, .{ .simple = .field_name }); - try ty.resolveFields(pt); + try sema.ensureLayoutResolved(ty, ty_src, .field_queried); const ip = &zcu.intern_pool; const has_field = hf: { @@ -13510,7 +12930,8 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }, .union_type => { const union_type = ip.loadUnionType(ty.toIntern()); - break :hf union_type.loadTagType(ip).nameIndex(ip, field_name) != null; + const enum_type = ip.loadEnumType(union_type.enum_tag_type); + break :hf enum_type.nameIndex(ip, field_name) != null; }, .enum_type => { break :hf ip.loadEnumType(ty.toIntern()).nameIndex(ip, field_name) != null; @@ -13568,17 +12989,18 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const file = zcu.fileByIndex(file_index); switch (file.getMode()) { .zig => { - try pt.ensureFileAnalyzed(file_index); - const ty = zcu.fileRootType(file_index); - try sema.declareDependency(.{ .interned = ty }); + try pt.ensureFilePopulated(file_index); + const ty: Type = .fromInterned(zcu.fileRootType(file_index)); try sema.addTypeReferenceEntry(operand_src, ty); - return Air.internedToRef(ty); + // No need for `ensureNamespaceUpToDate`, because `Zcu.PerThread.updateFileNamespace` + // already made sure that all root file structs have up-to-date namespaces. + return .fromType(ty); }, .zon => { const res_ty: InternPool.Index = b: { if (extra.res_ty == .none) break :b .none; - const res_ty_inst = try sema.resolveInst(extra.res_ty); - const res_ty = try sema.analyzeAsType(block, operand_src, res_ty_inst); + const res_ty_inst = sema.resolveInst(extra.res_ty); + const res_ty = try sema.analyzeAsType(block, operand_src, .type, res_ty_inst); if (res_ty.isGenericPoison()) break :b .none; break :b res_ty.toIntern(); }; @@ -13665,8 +13087,8 @@ fn zirShl( const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = try sema.resolveInst(extra.lhs); - const rhs = try sema.resolveInst(extra.rhs); + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); @@ -13692,8 +13114,8 @@ fn zirShl( // we already know `scalar_rhs_ty` is valid for `.shl` -- we only need to validate for `.shl_sat`. if (air_tag == .shl_sat) _ = try sema.checkIntType(block, rhs_src, scalar_rhs_ty); - const maybe_lhs_val = try sema.resolveValueResolveLazy(lhs); - const maybe_rhs_val = try sema.resolveValueResolveLazy(rhs); + const maybe_lhs_val = sema.resolveValue(lhs); + const maybe_rhs_val = sema.resolveValue(rhs); const runtime_src = rs: { if (maybe_rhs_val) |rhs_val| { @@ -13713,11 +13135,11 @@ fn zirShl( const bits = scalar_ty.intInfo(zcu).bits; switch (rhs_ty.zigTypeTag(zcu)) { .int, .comptime_int => { - switch (try rhs_val.orderAgainstZeroSema(pt)) { + switch (Value.order(rhs_val, .zero_comptime_int, zcu)) { .gt => { if (air_tag != .shl_sat) { var rhs_space: Value.BigIntSpace = undefined; - const rhs_bigint = try rhs_val.toBigIntSema(&rhs_space, pt); + const rhs_bigint = rhs_val.toBigInt(&rhs_space, zcu); if (rhs_bigint.orderAgainstScalar(bits) != .lt) { return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null); } @@ -13736,11 +13158,11 @@ fn zirShl( .shl, .shl_exact => return sema.failWithUseOfUndef(block, rhs_src, elem_idx), else => unreachable, }; - switch (try rhs_elem.orderAgainstZeroSema(pt)) { + switch (Value.order(rhs_elem, .zero_comptime_int, zcu)) { .gt => { if (air_tag != .shl_sat) { var rhs_elem_space: Value.BigIntSpace = undefined; - const rhs_elem_bigint = try rhs_elem.toBigIntSema(&rhs_elem_space, pt); + const rhs_elem_bigint = rhs_elem.toBigInt(&rhs_elem_space, zcu); if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) { return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx); } @@ -13769,7 +13191,7 @@ fn zirShl( .shl, .shl_exact => try sema.checkAllScalarsDefined(block, lhs_src, lhs_val), else => unreachable, } - if (try lhs_val.compareAllWithZeroSema(.eq, pt)) return lhs; + if (lhs_val.compareAllWithZero(.eq, zcu)) return lhs; } } break :rs rhs_src; @@ -13785,13 +13207,13 @@ fn zirShl( const rt_rhs_scalar_ty = try pt.smallestUnsignedInt(bit_count); if (!rhs_ty.isVector(zcu)) break :rt_rhs try pt.intValue( rt_rhs_scalar_ty, - @min(try rhs_val.getUnsignedIntSema(pt) orelse bit_count, bit_count), + @min(rhs_val.getUnsignedInt(zcu) orelse bit_count, bit_count), ); const rhs_len = rhs_ty.vectorLen(zcu); const rhs_elems = try sema.arena.alloc(InternPool.Index, rhs_len); for (rhs_elems, 0..) |*rhs_elem, i| rhs_elem.* = (try pt.intValue( rt_rhs_scalar_ty, - @min(try (try rhs_val.elemValue(pt, i)).getUnsignedIntSema(pt) orelse bit_count, bit_count), + @min((try rhs_val.elemValue(pt, i)).getUnsignedInt(zcu) orelse bit_count, bit_count), )).toIntern(); break :rt_rhs try pt.aggregateValue(try pt.vectorType(.{ .len = rhs_len, @@ -13855,8 +13277,8 @@ fn zirShr( const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = try sema.resolveInst(extra.lhs); - const rhs = try sema.resolveInst(extra.rhs); + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); @@ -13875,8 +13297,8 @@ fn zirShr( try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); const scalar_ty = lhs_ty.scalarType(zcu); - const maybe_lhs_val = try sema.resolveValueResolveLazy(lhs); - const maybe_rhs_val = try sema.resolveValueResolveLazy(rhs); + const maybe_lhs_val = sema.resolveValue(lhs); + const maybe_rhs_val = sema.resolveValue(rhs); const runtime_src = rs: { if (maybe_rhs_val) |rhs_val| { @@ -13893,10 +13315,10 @@ fn zirShr( const bits = scalar_ty.intInfo(zcu).bits; switch (rhs_ty.zigTypeTag(zcu)) { .int, .comptime_int => { - switch (try rhs_val.orderAgainstZeroSema(pt)) { + switch (Value.order(rhs_val, .zero_comptime_int, zcu)) { .gt => { var rhs_space: Value.BigIntSpace = undefined; - const rhs_bigint = try rhs_val.toBigIntSema(&rhs_space, pt); + const rhs_bigint = rhs_val.toBigInt(&rhs_space, zcu); if (rhs_bigint.orderAgainstScalar(bits) != .lt) { return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null); } @@ -13912,10 +13334,10 @@ fn zirShr( if (rhs_elem.isUndef(zcu)) { return sema.failWithUseOfUndef(block, rhs_src, elem_idx); } - switch (try rhs_elem.orderAgainstZeroSema(pt)) { + switch (Value.order(rhs_elem, .zero_comptime_int, zcu)) { .gt => { var rhs_elem_space: Value.BigIntSpace = undefined; - const rhs_elem_bigint = try rhs_elem.toBigIntSema(&rhs_elem_space, pt); + const rhs_elem_bigint = rhs_elem.toBigInt(&rhs_elem_space, zcu); if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) { return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx); } @@ -13936,7 +13358,7 @@ fn zirShr( } if (maybe_lhs_val) |lhs_val| { try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); - if (try lhs_val.compareAllWithZeroSema(.eq, pt)) return lhs; + if (lhs_val.compareAllWithZero(.eq, zcu)) return lhs; } } break :rs rhs_src; @@ -13988,8 +13410,8 @@ fn zirBitwise( const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = try sema.resolveInst(extra.lhs); - const rhs = try sema.resolveInst(extra.rhs); + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); @@ -14011,8 +13433,8 @@ fn zirBitwise( const runtime_src = runtime: { // TODO: ask the linker what kind of relocations are available, and // in some cases emit a Value that means "this decl's address AND'd with this operand". - if (try sema.resolveValueResolveLazy(casted_lhs)) |lhs_val| { - if (try sema.resolveValueResolveLazy(casted_rhs)) |rhs_val| { + if (sema.resolveValue(casted_lhs)) |lhs_val| { + if (sema.resolveValue(casted_rhs)) |rhs_val| { const result_val = switch (air_tag) { // zig fmt: off .bit_and => try arith.bitwiseBin(sema, resolved_type, lhs_val, rhs_val, .@"and"), @@ -14040,7 +13462,7 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); const src = block.nodeOffset(inst_data.src_node); - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); const scalar_ty = operand_ty.scalarType(zcu); const scalar_tag = scalar_ty.zigTypeTag(zcu); @@ -14058,7 +13480,7 @@ fn analyzeBitNot( src: LazySrcLoc, ) CompileError!Air.Inst.Ref { const operand_ty = sema.typeOf(operand); - if (try sema.resolveValue(operand)) |operand_val| { + if (sema.resolveValue(operand)) |operand_val| { const result_val = try arith.bitwiseNot(sema, operand_ty, operand_val); return Air.internedToRef(result_val.toIntern()); } @@ -14106,13 +13528,13 @@ fn analyzeTupleCat( var i: u32 = 0; while (i < lhs_len) : (i += 1) { types[i] = lhs_ty.fieldType(i, zcu).toIntern(); - const default_val = lhs_ty.structFieldDefaultValue(i, zcu); - values[i] = default_val.toIntern(); const operand_src = block.src(.{ .array_cat_lhs = .{ .array_cat_offset = src_node, .elem_index = i, } }); - if (default_val.toIntern() == .unreachable_value) { + if (lhs_ty.structFieldDefaultValue(i, zcu)) |default_val| { + values[i] = default_val.toIntern(); + } else { runtime_src = operand_src; values[i] = .none; } @@ -14120,13 +13542,13 @@ fn analyzeTupleCat( i = 0; while (i < rhs_len) : (i += 1) { types[i + lhs_len] = rhs_ty.fieldType(i, zcu).toIntern(); - const default_val = rhs_ty.structFieldDefaultValue(i, zcu); - values[i + lhs_len] = default_val.toIntern(); const operand_src = block.src(.{ .array_cat_rhs = .{ .array_cat_offset = src_node, .elem_index = i, } }); - if (default_val.toIntern() == .unreachable_value) { + if (rhs_ty.structFieldDefaultValue(i, zcu)) |default_val| { + values[i + lhs_len] = default_val.toIntern(); + } else { runtime_src = operand_src; values[i + lhs_len] = .none; } @@ -14168,8 +13590,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = try sema.resolveInst(extra.lhs); - const rhs = try sema.resolveInst(extra.rhs); + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); const src = block.nodeOffset(inst_data.src_node); @@ -14263,12 +13685,12 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }; const runtime_src = if (switch (lhs_ty.zigTypeTag(zcu)) { - .array, .@"struct" => try sema.resolveValue(lhs), + .array, .@"struct" => sema.resolveValue(lhs), .pointer => try sema.resolveDefinedValue(block, lhs_src, lhs), else => unreachable, }) |lhs_val| rs: { if (switch (rhs_ty.zigTypeTag(zcu)) { - .array, .@"struct" => try sema.resolveValue(rhs), + .array, .@"struct" => sema.resolveValue(rhs), .pointer => try sema.resolveDefinedValue(block, rhs_src, rhs), else => unreachable, }) |rhs_val| { @@ -14290,32 +13712,30 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai var elem_i: u32 = 0; while (elem_i < lhs_len) : (elem_i += 1) { const lhs_elem_i = elem_i; - const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i, zcu) else Value.@"unreachable"; - const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try lhs_sub_val.elemValue(pt, lhs_elem_i) else elem_default_val; - const elem_val_inst = Air.internedToRef(elem_val.toIntern()); + const elem_default_val: ?Value = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i, zcu) else null; + const elem_val = elem_default_val orelse try lhs_sub_val.elemValue(pt, lhs_elem_i); const operand_src = block.src(.{ .array_cat_lhs = .{ .array_cat_offset = inst_data.src_node, .elem_index = elem_i, } }); - const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, operand_src); - const coerced_elem_val = try sema.resolveConstValue(block, operand_src, coerced_elem_val_inst, undefined); + const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, .fromValue(elem_val), operand_src); + const coerced_elem_val = sema.resolveValue(coerced_elem_val_inst).?; element_vals[elem_i] = coerced_elem_val.toIntern(); } while (elem_i < result_len) : (elem_i += 1) { const rhs_elem_i = elem_i - lhs_len; - const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i, zcu) else Value.@"unreachable"; - const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try rhs_sub_val.elemValue(pt, rhs_elem_i) else elem_default_val; - const elem_val_inst = Air.internedToRef(elem_val.toIntern()); + const elem_default_val: ?Value = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i, zcu) else null; + const elem_val = elem_default_val orelse try rhs_sub_val.elemValue(pt, rhs_elem_i); const operand_src = block.src(.{ .array_cat_rhs = .{ .array_cat_offset = inst_data.src_node, .elem_index = @intCast(rhs_elem_i), } }); - const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, operand_src); - const coerced_elem_val = try sema.resolveConstValue(block, operand_src, coerced_elem_val_inst, undefined); + const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, .fromValue(elem_val), operand_src); + const coerced_elem_val = sema.resolveValue(coerced_elem_val_inst).?; element_vals[elem_i] = coerced_elem_val.toIntern(); } return sema.addConstantMaybeRef( - (try pt.aggregateValue(result_ty, element_vals)).toIntern(), + try pt.aggregateValue(result_ty, element_vals), ptr_addrspace != null, ); } else break :rs rhs_src; @@ -14324,18 +13744,18 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai try sema.requireRuntimeBlock(block, src, runtime_src); if (ptr_addrspace) |ptr_as| { - const constant_alloc_ty = try pt.ptrTypeSema(.{ + const constant_alloc_ty = try pt.ptrType(.{ .child = result_ty.toIntern(), .flags = .{ .address_space = ptr_as, .is_const = true, }, }); - const alloc_ty = try pt.ptrTypeSema(.{ + const alloc_ty = try pt.ptrType(.{ .child = result_ty.toIntern(), .flags = .{ .address_space = ptr_as }, }); - const elem_ptr_ty = try pt.ptrTypeSema(.{ + const elem_ptr_ty = try pt.ptrType(.{ .child = resolved_elem_ty.toIntern(), .flags = .{ .address_space = ptr_as }, }); @@ -14347,7 +13767,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (lhs_ty.zigTypeTag(zcu) == .pointer and rhs_ty.zigTypeTag(zcu) == .pointer) { - const slice_ty = try pt.ptrTypeSema(.{ + const slice_ty = try pt.ptrType(.{ .child = resolved_elem_ty.toIntern(), .flags = .{ .size = .slice, @@ -14359,45 +13779,44 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const many_alloc = try block.addBitCast(many_ty, mutable_alloc); // lhs_dest_slice = dest[0..lhs.len] - const slice_ty_ref = Air.internedToRef(slice_ty.toIntern()); - const lhs_len_ref = try pt.intRef(.usize, lhs_len); - const lhs_dest_slice = try block.addInst(.{ - .tag = .slice, - .data = .{ .ty_pl = .{ - .ty = slice_ty_ref, - .payload = try sema.addExtra(Air.Bin{ - .lhs = many_alloc, - .rhs = lhs_len_ref, - }), - } }, - }); - - _ = try block.addBinOp(.memcpy, lhs_dest_slice, lhs); + if (lhs_len > 0) { + const lhs_dest_slice = try block.addInst(.{ + .tag = .slice, + .data = .{ .ty_pl = .{ + .ty = .fromType(slice_ty), + .payload = try sema.addExtra(Air.Bin{ + .lhs = many_alloc, + .rhs = try pt.intRef(.usize, lhs_len), + }), + } }, + }); + _ = try block.addBinOp(.memcpy, lhs_dest_slice, lhs); + } // rhs_dest_slice = dest[lhs.len..][0..rhs.len] - const rhs_len_ref = try pt.intRef(.usize, rhs_len); - const rhs_dest_offset = try block.addInst(.{ - .tag = .ptr_add, - .data = .{ .ty_pl = .{ - .ty = Air.internedToRef(many_ty.toIntern()), - .payload = try sema.addExtra(Air.Bin{ - .lhs = many_alloc, - .rhs = lhs_len_ref, - }), - } }, - }); - const rhs_dest_slice = try block.addInst(.{ - .tag = .slice, - .data = .{ .ty_pl = .{ - .ty = slice_ty_ref, - .payload = try sema.addExtra(Air.Bin{ - .lhs = rhs_dest_offset, - .rhs = rhs_len_ref, - }), - } }, - }); - - _ = try block.addBinOp(.memcpy, rhs_dest_slice, rhs); + if (rhs_len > 0) { + const rhs_dest_offset = try block.addInst(.{ + .tag = .ptr_add, + .data = .{ .ty_pl = .{ + .ty = Air.internedToRef(many_ty.toIntern()), + .payload = try sema.addExtra(Air.Bin{ + .lhs = many_alloc, + .rhs = try pt.intRef(.usize, lhs_len), + }), + } }, + }); + const rhs_dest_slice = try block.addInst(.{ + .tag = .slice, + .data = .{ .ty_pl = .{ + .ty = .fromType(slice_ty), + .payload = try sema.addExtra(Air.Bin{ + .lhs = rhs_dest_offset, + .rhs = try pt.intRef(.usize, rhs_len), + }), + } }, + }); + _ = try block.addBinOp(.memcpy, rhs_dest_slice, rhs); + } if (res_sent_val) |sent_val| { const elem_index = try pt.intRef(.usize, result_len); @@ -14486,7 +13905,7 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Ins .none => null, else => Value.fromInterned(ptr_info.sentinel), }, - .len = try val.sliceLen(pt), + .len = val.sliceLen(zcu), }; }, .one => { @@ -14500,8 +13919,20 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Ins .@"struct" => { if (operand_ty.isTuple(zcu) and peer_ty.isIndexable(zcu)) { assert(!peer_ty.isTuple(zcu)); + const peer_elem_ty = switch (peer_ty.zigTypeTag(zcu)) { + .pointer => switch (peer_ty.ptrSize(zcu)) { + .one => switch (peer_ty.childType(zcu).zigTypeTag(zcu)) { + .array, .vector => peer_ty.childType(zcu).childType(zcu), + .@"struct" => return null, + else => unreachable, + }, + .many, .c, .slice => peer_ty.childType(zcu), + }, + .vector, .array => peer_ty.childType(zcu), + else => unreachable, + }; return .{ - .elem_type = peer_ty.elemType2(zcu), + .elem_type = peer_elem_ty, .sentinel = null, .len = operand_ty.arrayLen(zcu), }; @@ -14543,12 +13974,13 @@ fn analyzeTupleMul( var runtime_src: ?LazySrcLoc = null; for (0..tuple_len) |i| { types[i] = operand_ty.fieldType(i, zcu).toIntern(); - values[i] = operand_ty.structFieldDefaultValue(i, zcu).toIntern(); const operand_src = block.src(.{ .array_cat_lhs = .{ .array_cat_offset = src_node, .elem_index = @intCast(i), } }); - if (values[i] == .unreachable_value) { + if (operand_ty.structFieldDefaultValue(i, zcu)) |default_val| { + values[i] = default_val.toIntern(); + } else { runtime_src = operand_src; values[i] = .none; // TODO don't treat unreachable_value as special } @@ -14593,7 +14025,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; const extra = sema.code.extraData(Zir.Inst.ArrayMul, inst_data.payload_index).data; - const uncoerced_lhs = try sema.resolveInst(extra.lhs); + const uncoerced_lhs = sema.resolveInst(extra.lhs); const uncoerced_lhs_ty = sema.typeOf(uncoerced_lhs); const src: LazySrcLoc = block.nodeOffset(inst_data.src_node); const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); @@ -14672,7 +14104,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const ptr_addrspace = if (lhs_ty.zigTypeTag(zcu) == .pointer) lhs_ty.ptrAddressSpace(zcu) else null; const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); - if (try sema.resolveValue(lhs)) |lhs_val| ct: { + if (sema.resolveValue(lhs)) |lhs_val| ct: { const lhs_sub_val = if (lhs_ty.isSinglePointer(zcu)) try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty) orelse break :ct else if (lhs_ty.isSlice(zcu)) @@ -14700,7 +14132,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } break :v try pt.aggregateValue(result_ty, element_vals); }; - return sema.addConstantMaybeRef(val.toIntern(), ptr_addrspace != null); + return sema.addConstantMaybeRef(val, ptr_addrspace != null); } try sema.requireRuntimeBlock(block, src, lhs_src); @@ -14714,7 +14146,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } if (ptr_addrspace) |ptr_as| { - const alloc_ty = try pt.ptrTypeSema(.{ + const alloc_ty = try pt.ptrType(.{ .child = result_ty.toIntern(), .flags = .{ .address_space = ptr_as, @@ -14722,7 +14154,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }, }); const alloc = try block.addTy(.alloc, alloc_ty); - const elem_ptr_ty = try pt.ptrTypeSema(.{ + const elem_ptr_ty = try pt.ptrType(.{ .child = lhs_info.elem_type.toIntern(), .flags = .{ .address_space = ptr_as }, }); @@ -14761,7 +14193,7 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const lhs_src = src; const rhs_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); - const rhs = try sema.resolveInst(inst_data.operand); + const rhs = sema.resolveInst(inst_data.operand); const rhs_ty = sema.typeOf(rhs); const rhs_scalar_ty = rhs_ty.scalarType(zcu); @@ -14774,7 +14206,7 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. if (rhs_scalar_ty.isAnyFloat()) { // We handle float negation here to ensure negative zero is represented in the bits. - if (try sema.resolveValue(rhs)) |rhs_val| { + if (sema.resolveValue(rhs)) |rhs_val| { const result = try arith.negateFloat(sema, rhs_ty, rhs_val); return Air.internedToRef(result.toIntern()); } @@ -14794,7 +14226,7 @@ fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const lhs_src = src; const rhs_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); - const rhs = try sema.resolveInst(inst_data.operand); + const rhs = sema.resolveInst(inst_data.operand); const rhs_ty = sema.typeOf(rhs); const rhs_scalar_ty = rhs_ty.scalarType(zcu); @@ -14822,8 +14254,8 @@ fn zirArithmetic( const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = try sema.resolveInst(extra.lhs); - const rhs = try sema.resolveInst(extra.rhs); + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, src, lhs_src, rhs_src, safety); } @@ -14836,8 +14268,8 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = try sema.resolveInst(extra.lhs); - const rhs = try sema.resolveInst(extra.rhs); + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); @@ -14859,8 +14291,8 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div); - const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); - const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); + const maybe_lhs_val = sema.resolveValue(casted_lhs); + const maybe_rhs_val = sema.resolveValue(casted_rhs); if ((lhs_ty.zigTypeTag(zcu) == .comptime_float and rhs_ty.zigTypeTag(zcu) == .comptime_int) or (lhs_ty.zigTypeTag(zcu) == .comptime_int and rhs_ty.zigTypeTag(zcu) == .comptime_float)) @@ -14945,8 +14377,8 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = try sema.resolveInst(extra.lhs); - const rhs = try sema.resolveInst(extra.rhs); + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); @@ -14968,8 +14400,8 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_exact); - const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); - const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); + const maybe_lhs_val = sema.resolveValue(casted_lhs); + const maybe_rhs_val = sema.resolveValue(casted_rhs); // Because `@divExact` can trigger Illegal Behavior, undefined operands trigger Illegal Behavior. @@ -15041,8 +14473,8 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = try sema.resolveInst(extra.lhs); - const rhs = try sema.resolveInst(extra.rhs); + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); @@ -15064,8 +14496,8 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_floor); - const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); - const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); + const maybe_lhs_val = sema.resolveValue(casted_lhs); + const maybe_rhs_val = sema.resolveValue(casted_rhs); const allow_div_zero = !is_int and resolved_type.toIntern() != .comptime_float_type and @@ -15106,8 +14538,8 @@ fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = try sema.resolveInst(extra.lhs); - const rhs = try sema.resolveInst(extra.rhs); + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); @@ -15129,8 +14561,8 @@ fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_trunc); - const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); - const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); + const maybe_lhs_val = sema.resolveValue(casted_lhs); + const maybe_rhs_val = sema.resolveValue(casted_rhs); const allow_div_zero = !is_int and resolved_type.toIntern() != .comptime_float_type and @@ -15317,8 +14749,8 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = try sema.resolveInst(extra.lhs); - const rhs = try sema.resolveInst(extra.rhs); + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); @@ -15341,8 +14773,8 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod_rem); - const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); - const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); + const maybe_lhs_val = sema.resolveValue(casted_lhs); + const maybe_rhs_val = sema.resolveValue(casted_rhs); const lhs_maybe_negative = a: { if (lhs_scalar_ty.isUnsignedInt(zcu)) break :a false; @@ -15418,8 +14850,8 @@ fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = try sema.resolveInst(extra.lhs); - const rhs = try sema.resolveInst(extra.rhs); + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); @@ -15440,8 +14872,8 @@ fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod); - const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); - const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); + const maybe_lhs_val = sema.resolveValue(casted_lhs); + const maybe_rhs_val = sema.resolveValue(casted_rhs); const allow_div_zero = !is_int and resolved_type.toIntern() != .comptime_float_type and @@ -15482,8 +14914,8 @@ fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = try sema.resolveInst(extra.lhs); - const rhs = try sema.resolveInst(extra.rhs); + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); @@ -15504,8 +14936,8 @@ fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .rem); - const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); - const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); + const maybe_lhs_val = sema.resolveValue(casted_lhs); + const maybe_rhs_val = sema.resolveValue(casted_rhs); const allow_div_zero = !is_int and resolved_type.toIntern() != .comptime_float_type and @@ -15553,8 +14985,8 @@ fn zirOverflowArithmetic( const lhs_src = block.builtinCallArgSrc(extra.node, 0); const rhs_src = block.builtinCallArgSrc(extra.node, 1); - const uncasted_lhs = try sema.resolveInst(extra.lhs); - const uncasted_rhs = try sema.resolveInst(extra.rhs); + const uncasted_lhs = sema.resolveInst(extra.lhs); + const uncasted_rhs = sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(uncasted_lhs); const rhs_ty = sema.typeOf(uncasted_rhs); @@ -15584,8 +15016,8 @@ fn zirOverflowArithmetic( return sema.fail(block, src, "expected vector of integers or integer tag type, found '{f}'", .{dest_ty.fmt(pt)}); } - const maybe_lhs_val = try sema.resolveValue(lhs); - const maybe_rhs_val = try sema.resolveValue(rhs); + const maybe_lhs_val = sema.resolveValue(lhs); + const maybe_rhs_val = sema.resolveValue(rhs); const tuple_ty = try pt.overflowArithmeticTupleType(dest_ty); const overflow_ty: Type = .fromInterned(ip.indexToKey(tuple_ty.toIntern()).tuple_type.types.get(ip)[1]); @@ -15601,12 +15033,12 @@ fn zirOverflowArithmetic( // to the result, even if it is undefined.. // Otherwise, if either of the argument is undefined, undefined is returned. if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef(zcu) and (try lhs_val.compareAllWithZeroSema(.eq, pt))) { + if (!lhs_val.isUndef(zcu) and lhs_val.compareAllWithZero(.eq, zcu)) { break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs }; } } if (maybe_rhs_val) |rhs_val| { - if (!rhs_val.isUndef(zcu) and (try rhs_val.compareAllWithZeroSema(.eq, pt))) { + if (!rhs_val.isUndef(zcu) and rhs_val.compareAllWithZero(.eq, zcu)) { break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; } } @@ -15627,7 +15059,7 @@ fn zirOverflowArithmetic( if (maybe_rhs_val) |rhs_val| { if (rhs_val.isUndef(zcu)) { break :result .{ .overflow_bit = .undef, .wrapped = .undef }; - } else if (try rhs_val.compareAllWithZeroSema(.eq, pt)) { + } else if (rhs_val.compareAllWithZero(.eq, zcu)) { break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; } else if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef(zcu)) { @@ -15642,12 +15074,12 @@ fn zirOverflowArithmetic( .mul_with_overflow => { // If either of the arguments is zero, the result is zero and no overflow occured. if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef(zcu) and try lhs_val.compareAllWithZeroSema(.eq, pt)) { + if (!lhs_val.isUndef(zcu) and lhs_val.compareAllWithZero(.eq, zcu)) { break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; } } if (maybe_rhs_val) |rhs_val| { - if (!rhs_val.isUndef(zcu) and try rhs_val.compareAllWithZeroSema(.eq, pt)) { + if (!rhs_val.isUndef(zcu) and rhs_val.compareAllWithZero(.eq, zcu)) { break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs }; } } @@ -15694,10 +15126,10 @@ fn zirOverflowArithmetic( const bits = scalar_ty.intInfo(zcu).bits; switch (rhs_ty.zigTypeTag(zcu)) { .int, .comptime_int => { - switch (try rhs_val.orderAgainstZeroSema(pt)) { + switch (Value.order(rhs_val, .zero_comptime_int, zcu)) { .gt => { var rhs_space: Value.BigIntSpace = undefined; - const rhs_bigint = try rhs_val.toBigIntSema(&rhs_space, pt); + const rhs_bigint = rhs_val.toBigInt(&rhs_space, zcu); if (rhs_bigint.orderAgainstScalar(bits) != .lt) { return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null); } @@ -15711,10 +15143,10 @@ fn zirOverflowArithmetic( for (0..rhs_ty.vectorLen(zcu)) |elem_idx| { const rhs_elem = try rhs_val.elemValue(pt, elem_idx); if (rhs_elem.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, elem_idx); - switch (try rhs_elem.orderAgainstZeroSema(pt)) { + switch (Value.order(rhs_elem, .zero_comptime_int, zcu)) { .gt => { var rhs_elem_space: Value.BigIntSpace = undefined; - const rhs_elem_bigint = try rhs_elem.toBigIntSema(&rhs_elem_space, pt); + const rhs_elem_bigint = rhs_elem.toBigInt(&rhs_elem_space, zcu); if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) { return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx); } @@ -15728,7 +15160,7 @@ fn zirOverflowArithmetic( }, else => unreachable, } - if (try rhs_val.compareAllWithZeroSema(.eq, pt)) { + if (rhs_val.compareAllWithZero(.eq, zcu)) { break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; } } else { @@ -15737,7 +15169,7 @@ fn zirOverflowArithmetic( } if (maybe_lhs_val) |lhs_val| { try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); - if (try lhs_val.compareAllWithZeroSema(.eq, pt)) { + if (lhs_val.compareAllWithZero(.eq, zcu)) { break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; } } @@ -15767,7 +15199,7 @@ fn zirOverflowArithmetic( }; if (result.inst != .none) { - if (try sema.resolveValue(result.inst)) |some| { + if (sema.resolveValue(result.inst)) |some| { result.wrapped = some; result.inst = .none; } @@ -15817,22 +15249,45 @@ fn analyzeArithmetic( if (zir_tag != .sub) { return sema.failWithInvalidPtrArithmetic(block, src, "pointer-pointer", "subtraction"); } - if (!lhs_ty.elemType2(zcu).eql(rhs_ty.elemType2(zcu), zcu)) { + + // TODO: these semantics are really weird. Pointer subtraction works in increments + // of the pointer child for indexable pointers (excluding pointers to vectors), + // which makes sense, but we also allow it for arbitrary single-item pointers, which + // leads to the weird result that subtraction of '*T' works completely differently + // depending on whether 'T' is an array. That seems dangerous and confusing, and + // requires the odd logic below. This behavior originally came from a now-removed + // function `Type.elemType2`, which was removed precisely *because* the thing it did + // wasn't really well-defined; for that reason, these semantics were probably + // largely accidental to begin with. We should change the langauge to avoid this + // confusing behavior. For instance, perhaps pointer subtraction should only work on + // indexable pointers. + const lhs_elem_ty = ty: { + const ptr_elem_ty = lhs_ty.childType(zcu); + if (lhs_ty.ptrSize(zcu) == .one and ptr_elem_ty.zigTypeTag(zcu) == .array) break :ty ptr_elem_ty.childType(zcu); + break :ty ptr_elem_ty; + }; + const rhs_elem_ty = ty: { + const ptr_elem_ty = rhs_ty.childType(zcu); + if (rhs_ty.ptrSize(zcu) == .one and ptr_elem_ty.zigTypeTag(zcu) == .array) break :ty ptr_elem_ty.childType(zcu); + break :ty ptr_elem_ty; + }; + if (lhs_elem_ty.toIntern() != rhs_elem_ty.toIntern()) { return sema.fail(block, src, "incompatible pointer arithmetic operands '{f}' and '{f}'", .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt), }); } - const elem_size = lhs_ty.elemType2(zcu).abiSize(zcu); + try sema.ensureLayoutResolved(lhs_elem_ty, src, .ptr_offset); + const elem_size = lhs_elem_ty.abiSize(zcu); if (elem_size == 0) { - return sema.fail(block, src, "pointer arithmetic requires element type '{f}' to have runtime bits", .{ - lhs_ty.elemType2(zcu).fmt(pt), + return sema.fail(block, src, "pointer subtraction requires element type '{f}' to have runtime bits", .{ + lhs_elem_ty.fmt(pt), }); } const runtime_src = runtime_src: { - if (try sema.resolveValue(lhs)) |lhs_value| { - if (try sema.resolveValue(rhs)) |rhs_value| { + if (sema.resolveValue(lhs)) |lhs_value| { + if (sema.resolveValue(rhs)) |rhs_value| { const lhs_ptr = switch (zcu.intern_pool.indexToKey(lhs_value.toIntern())) { .undef => return sema.failWithUseOfUndef(block, lhs_src, null), .ptr => |ptr| ptr, @@ -15875,12 +15330,8 @@ fn analyzeArithmetic( else => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"), }; - if (!try lhs_ty.elemType2(zcu).hasRuntimeBitsSema(pt)) { - return sema.fail(block, src, "pointer arithmetic requires element type '{f}' to have runtime bits", .{ - lhs_ty.elemType2(zcu).fmt(pt), - }); - } - return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src); + try sema.ensureLayoutResolved(lhs_ty.childType(zcu), src, .ptr_offset); + return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, rhs_src); }, } } @@ -15915,8 +15366,8 @@ fn analyzeArithmetic( else => unreachable, }; - const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); - const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); + const maybe_lhs_val = sema.resolveValue(casted_lhs); + const maybe_rhs_val = sema.resolveValue(casted_rhs); if (maybe_lhs_val) |lhs_val| { if (maybe_rhs_val) |rhs_val| { @@ -15972,6 +15423,7 @@ fn analyzeArithmetic( return block.addBinOp(air_tag, casted_lhs, casted_rhs); } +/// Asserts that the layout of the pointer child type is already resolved. fn analyzePtrArithmetic( sema: *Sema, block: *Block, @@ -15979,7 +15431,6 @@ fn analyzePtrArithmetic( ptr: Air.Inst.Ref, uncasted_offset: Air.Inst.Ref, air_tag: Air.Inst.Tag, - ptr_src: LazySrcLoc, offset_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { // TODO if the operand is comptime-known to be negative, or is a negative int, @@ -15987,81 +15438,55 @@ fn analyzePtrArithmetic( const offset = try sema.coerce(block, .usize, uncasted_offset, offset_src); const pt = sema.pt; const zcu = pt.zcu; - const opt_ptr_val = try sema.resolveValue(ptr); - const opt_off_val = try sema.resolveDefinedValue(block, offset_src, offset); const ptr_ty = sema.typeOf(ptr); const ptr_info = ptr_ty.ptrInfo(zcu); assert(ptr_info.flags.size == .many or ptr_info.flags.size == .c); - if ((try sema.typeHasOnePossibleValue(.fromInterned(ptr_info.child))) != null) { - // Offset will be multiplied by zero, so result is the same as the base pointer. - return ptr; + const maybe_index: ?u64 = if (try sema.resolveDefinedValue(block, offset_src, offset)) |val| off: { + break :off val.toUnsignedInt(zcu); + } else null; + + const elem_ty: Type = .fromInterned(ptr_info.child); + elem_ty.assertHasLayout(zcu); + + switch (elem_ty.classify(zcu)) { + .no_possible_value, .one_possible_value => { + // Offset will be multiplied by zero, so result is the same as the base pointer. + return ptr; + }, + else => {}, } - const new_ptr_ty = t: { - // Calculate the new pointer alignment. - // This code is duplicated in `Type.elemPtrType`. - if (ptr_info.flags.alignment == .none) { - // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness. - break :t ptr_ty; - } - // If the addend is not a comptime-known value we can still count on - // it being a multiple of the type size. - const elem_size = try Type.fromInterned(ptr_info.child).abiSizeSema(pt); - const addend = if (opt_off_val) |off_val| a: { - const off_int = try sema.usizeCast(block, offset_src, try off_val.toUnsignedIntSema(pt)); - break :a elem_size * off_int; - } else elem_size; - - // The resulting pointer is aligned to the lcd between the offset (an - // arbitrary number) and the alignment factor (always a power of two, - // non zero). - const new_align: Alignment = @enumFromInt(@min( - @ctz(addend), - @intFromEnum(ptr_info.flags.alignment), - )); - assert(new_align != .none); - - break :t try pt.ptrTypeSema(.{ - .child = ptr_info.child, - .sentinel = ptr_info.sentinel, - .flags = .{ - .size = ptr_info.flags.size, - .alignment = new_align, - .is_const = ptr_info.flags.is_const, - .is_volatile = ptr_info.flags.is_volatile, - .is_allowzero = ptr_info.flags.is_allowzero, - .address_space = ptr_info.flags.address_space, - }, - }); - }; + const elem_ptr_ty = try ptr_ty.elemPtrType(maybe_index, pt); + // `elem_ptr_ty` is a single-item pointer, but we want a many-item or C pointer, and to preserve + // any input sentinel. + const new_ptr_ty = try pt.ptrType(info: { + var info = elem_ptr_ty.ptrInfo(zcu); + info.flags.size = ptr_info.flags.size; + info.sentinel = ptr_info.sentinel; + break :info info; + }); - const runtime_src = rs: { - if (opt_ptr_val) |ptr_val| { - if (opt_off_val) |offset_val| { - if (ptr_val.isUndef(zcu)) return pt.undefRef(new_ptr_ty); - - const offset_int = try sema.usizeCast(block, offset_src, try offset_val.toUnsignedIntSema(pt)); - if (offset_int == 0) return ptr; - if (air_tag == .ptr_sub) { - const elem_size = try Type.fromInterned(ptr_info.child).abiSizeSema(pt); - const new_ptr_val = try sema.ptrSubtract(block, op_src, ptr_val, offset_int * elem_size, new_ptr_ty); - return Air.internedToRef(new_ptr_val.toIntern()); - } else { - const new_ptr_val = try pt.getCoerced(try ptr_val.ptrElem(offset_int, pt), new_ptr_ty); - return Air.internedToRef(new_ptr_val.toIntern()); - } - } else break :rs offset_src; - } else break :rs ptr_src; - }; + ct: { + const ptr_val = sema.resolveValue(ptr) orelse break :ct; + if (ptr_val.isUndef(zcu)) return pt.undefRef(new_ptr_ty); + const index = maybe_index orelse break :ct; + + if (index == 0) return ptr; + if (air_tag == .ptr_sub) { + const elem_size = elem_ty.abiSize(zcu); + return .fromValue(try sema.ptrSubtract(block, op_src, ptr_val, index * elem_size, new_ptr_ty)); + } else { + return .fromValue(try pt.getCoerced(try ptr_val.ptrElem(index, pt), new_ptr_ty)); + } + } - try sema.requireRuntimeBlock(block, op_src, runtime_src); try sema.checkLogicalPtrOperation(block, op_src, ptr_ty); return block.addInst(.{ .tag = air_tag, .data = .{ .ty_pl = .{ - .ty = Air.internedToRef(new_ptr_ty.toIntern()), + .ty = .fromType(new_ptr_ty), .payload = try sema.addExtra(Air.Bin{ .lhs = ptr, .rhs = offset, @@ -16077,7 +15502,7 @@ fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.In const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); const ptr_src = src; // TODO better source location - const ptr = try sema.resolveInst(inst_data.operand); + const ptr = sema.resolveInst(inst_data.operand); return sema.analyzeLoad(block, src, ptr, ptr_src); } @@ -16151,7 +15576,7 @@ fn zirAsm( const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand); expr_ty = Air.internedToRef(out_ty.toIntern()); } else { - const inst = try sema.resolveInst(output.data.operand); + const inst = sema.resolveInst(output.data.operand); if (!sema.checkRuntimeValue(inst)) { const output_name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls); return sema.failWithContainsReferenceToComptimeVar(block, output_src, output_name, "assembly output", .fromInterned(inst.toInterned().?)); @@ -16181,7 +15606,7 @@ fn zirAsm( } }); extra_i = input.end; - const uncasted_arg = try sema.resolveInst(input.data.operand); + const uncasted_arg = sema.resolveInst(input.data.operand); const name = sema.code.nullTerminatedString(input.data.name); if (!sema.checkRuntimeValue(uncasted_arg)) { const input_name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls); @@ -16204,7 +15629,7 @@ fn zirAsm( const clobbers = if (extra.data.clobbers == .none) empty: { const clobbers_ty = try sema.getBuiltinType(src, .@"assembly.Clobbers"); break :empty try sema.structInitEmpty(block, clobbers_ty, src, src); - } else try sema.resolveInst(extra.data.clobbers); // Already coerced by AstGen. + } else sema.resolveInst(extra.data.clobbers); // Already coerced by AstGen. const clobbers_val = try sema.resolveConstDefinedValue(block, src, clobbers, .{ .simple = .clobber }); needed_capacity += asm_source.len / 4 + 1; @@ -16248,6 +15673,7 @@ fn zirAsm( buffer[input.c.len + 1 + input.n.len] = 0; sema.air_extra.items.len += (input.c.len + input.n.len + (2 + 3)) / 4; } + if (try expr_ty.toType().onePossibleValue(pt)) |opv| return .fromValue(opv); return asm_air; } @@ -16269,8 +15695,8 @@ fn zirCmpEq( const src: LazySrcLoc = block.nodeOffset(inst_data.src_node); const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); - const lhs = try sema.resolveInst(extra.lhs); - const rhs = try sema.resolveInst(extra.rhs); + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); @@ -16283,10 +15709,10 @@ fn zirCmpEq( // comparing null with optionals if (lhs_ty_tag == .null and (rhs_ty_tag == .optional or rhs_ty.isCPtr(zcu))) { - return sema.analyzeIsNull(block, rhs, op == .neq); + return sema.analyzeIsNull(block, src, rhs, op == .neq); } if (rhs_ty_tag == .null and (lhs_ty_tag == .optional or lhs_ty.isCPtr(zcu))) { - return sema.analyzeIsNull(block, lhs, op == .neq); + return sema.analyzeIsNull(block, src, lhs, op == .neq); } if (lhs_ty_tag == .null or rhs_ty_tag == .null) { @@ -16303,8 +15729,8 @@ fn zirCmpEq( if (lhs_ty_tag == .error_set and rhs_ty_tag == .error_set) { const runtime_src: LazySrcLoc = src: { - if (try sema.resolveValue(lhs)) |lval| { - if (try sema.resolveValue(rhs)) |rval| { + if (sema.resolveValue(lhs)) |lval| { + if (sema.resolveValue(rhs)) |rval| { if (lval.isUndef(zcu) or rval.isUndef(zcu)) return .undef_bool; const lkey = zcu.intern_pool.indexToKey(lval.toIntern()); const rkey = zcu.intern_pool.indexToKey(rval.toIntern()); @@ -16323,8 +15749,8 @@ fn zirCmpEq( return block.addBinOp(air_tag, lhs, rhs); } if (lhs_ty_tag == .type and rhs_ty_tag == .type) { - const lhs_as_type = try sema.analyzeAsType(block, lhs_src, lhs); - const rhs_as_type = try sema.analyzeAsType(block, rhs_src, rhs); + const lhs_as_type = try sema.analyzeAsType(block, lhs_src, .type, lhs); + const rhs_as_type = try sema.analyzeAsType(block, rhs_src, .type, rhs); return if (lhs_as_type.eql(rhs_as_type, zcu) == (op == .eq)) .bool_true else .bool_false; } return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true); @@ -16343,7 +15769,6 @@ fn analyzeCmpUnionTag( const pt = sema.pt; const zcu = pt.zcu; const union_ty = sema.typeOf(un); - try union_ty.resolveFields(pt); const union_tag_ty = union_ty.unionTagType(zcu) orelse { const msg = msg: { const msg = try sema.errMsg(un_src, "comparison of union and enum literal is only valid for tagged union types", .{}); @@ -16358,10 +15783,10 @@ fn analyzeCmpUnionTag( const coerced_tag = try sema.coerce(block, union_tag_ty, tag, tag_src); const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src); - if (try sema.resolveValue(coerced_tag)) |enum_val| { + if (sema.resolveValue(coerced_tag)) |enum_val| { if (enum_val.isUndef(zcu)) return .undef_bool; const field_ty = union_ty.unionFieldType(enum_val, zcu).?; - if (field_ty.zigTypeTag(zcu) == .noreturn) { + if (field_ty.classify(zcu) == .no_possible_value) { return .bool_false; } } @@ -16384,8 +15809,8 @@ fn zirCmp( const src: LazySrcLoc = block.nodeOffset(inst_data.src_node); const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); - const lhs = try sema.resolveInst(extra.lhs); - const rhs = try sema.resolveInst(extra.rhs); + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, false); } @@ -16468,8 +15893,8 @@ fn cmpSelf( const zcu = pt.zcu; const resolved_type = sema.typeOf(casted_lhs); - const maybe_lhs_val = try sema.resolveValue(casted_lhs); - const maybe_rhs_val = try sema.resolveValue(casted_rhs); + const maybe_lhs_val = sema.resolveValue(casted_lhs); + const maybe_rhs_val = sema.resolveValue(casted_rhs); if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return .undef_bool; if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return .undef_bool; @@ -16534,42 +15959,26 @@ fn runtimeBoolCmp( fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const pt = sema.pt; + const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); const ty = try sema.resolveType(block, operand_src, inst_data.operand); - switch (ty.zigTypeTag(pt.zcu)) { - .@"fn", - .noreturn, - .undefined, - .null, - .@"opaque", - => return sema.fail(block, operand_src, "no size available for type '{f}'", .{ty.fmt(pt)}), + try sema.ensureLayoutResolved(ty, operand_src, .size_of); + switch (ty.classify(zcu)) { + .no_possible_value, + => return sema.fail(block, operand_src, "no size available for uninstantiable type '{f}'", .{ty.fmt(pt)}), - .type, - .enum_literal, - .comptime_float, - .comptime_int, - .void, - => return .zero, + .partially_comptime, + .fully_comptime, + => return sema.fail(block, operand_src, "no size available for comptime-only type '{f}'", .{ty.fmt(pt)}), - .bool, - .int, - .float, - .pointer, - .array, - .@"struct", - .optional, - .error_union, - .error_set, - .@"enum", - .@"union", - .vector, - .frame, - .@"anyframe", - => {}, + .one_possible_value => { + assert(ty.abiSize(zcu) == 0); + return .zero; + }, + + .runtime => return .fromValue(try pt.intValue(.comptime_int, ty.abiSize(zcu))), } - const val = try ty.abiSizeLazy(pt); - return Air.internedToRef(val.toIntern()); } fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -16584,12 +15993,12 @@ fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A .undefined, .null, .@"opaque", - => return sema.fail(block, operand_src, "no size available for type '{f}'", .{operand_ty.fmt(pt)}), - .type, .enum_literal, .comptime_float, .comptime_int, + => return sema.fail(block, operand_src, "no size available for type '{f}'", .{operand_ty.fmt(pt)}), + .void, => return .zero, @@ -16609,8 +16018,8 @@ fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A .@"anyframe", => {}, } - const bit_size = try operand_ty.bitSizeSema(pt); - return pt.intRef(.comptime_int, bit_size); + try sema.ensureLayoutResolved(operand_ty, operand_src, .size_of); + return .fromValue(try pt.intValue(.comptime_int, operand_ty.bitSize(zcu))); } fn zirThis( @@ -16619,34 +16028,7 @@ fn zirThis( extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { _ = extended; - const pt = sema.pt; - const zcu = pt.zcu; - const namespace = pt.zcu.namespacePtr(block.namespace); - - switch (pt.zcu.intern_pool.indexToKey(namespace.owner_type)) { - .opaque_type => { - // Opaque types are never outdated since they don't undergo type resolution, so nothing to do! - return Air.internedToRef(namespace.owner_type); - }, - .struct_type, .union_type => { - const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type); - try sema.declareDependency(.{ .interned = new_ty }); - return Air.internedToRef(new_ty); - }, - .enum_type => { - const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type); - try sema.declareDependency(.{ .interned = new_ty }); - // Since this is an enum, it has to be resolved immediately. - // `ensureTypeUpToDate` has resolved the new type if necessary. - // We just need to check for resolution failures. - const ty_unit: AnalUnit = .wrap(.{ .type = new_ty }); - if (zcu.failed_analysis.contains(ty_unit) or zcu.transitive_failed_analysis.contains(ty_unit)) { - return error.AnalysisFail; - } - return Air.internedToRef(new_ty); - }, - else => unreachable, - } + return .fromIntern(sema.pt.zcu.namespacePtr(block.namespace).owner_type); } fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { @@ -16739,7 +16121,7 @@ fn zirRetAddr( _ = sema; _ = extended; if (block.isComptime()) { - // TODO: we could give a meaningful lazy value here. #14938 + // TODO: we could give a meaningful value here. #14938 return .zero_usize; } else { return block.addNoOp(.ret_addr); @@ -16882,6 +16264,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const type_info_ty = try sema.getBuiltinType(src, .Type); const type_info_tag_ty = type_info_ty.unionTagType(zcu).?; + try sema.ensureLayoutResolved(ty, src, .type_info); + if (ty.typeDeclInst(zcu)) |type_decl_inst| { try sema.declareDependency(.{ .namespace = type_decl_inst }); } @@ -16896,7 +16280,14 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .undefined, .null, .enum_literal, - => |type_info_tag| return unionInitFromEnumTag(sema, block, src, type_info_ty, @intFromEnum(type_info_tag), .void_value), + => |type_info_tag| return .fromValue(try pt.unionValue( + type_info_ty, + Value.uninterpret(type_info_tag, type_info_tag_ty, pt) catch |err| switch (err) { + error.TypeMismatch => @panic("std.builtin is corrupt"), + error.OutOfMemory => |e| return e, + }, + .void, + )), .@"fn" => { const fn_info_ty = try sema.getBuiltinType(src, .@"Type.Fn"); @@ -16904,19 +16295,25 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const func_ty_info = zcu.typeToFunc(ty).?; const param_vals = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len); - for (param_vals, 0..) |*param_val, i| { - const param_ty = func_ty_info.param_types.get(ip)[i]; + var func_is_generic = false; + + for (param_vals, 0..) |*param_val, param_index| { + const param_ty = func_ty_info.param_types.get(ip)[param_index]; const is_generic = param_ty == .generic_poison_type; + const is_noalias, const is_comptime = flags: { + const i = std.math.cast(u5, param_index) orelse break :flags .{ false, false }; + break :flags .{ func_ty_info.paramIsNoalias(i), func_ty_info.paramIsComptime(i) }; + }; + + if (is_generic or is_comptime or Type.fromInterned(param_ty).comptimeOnly(zcu)) { + func_is_generic = true; + } + const param_ty_val = try pt.intern(.{ .opt = .{ .ty = try pt.intern(.{ .opt_type = .type_type }), .val = if (is_generic) .none else param_ty, } }); - const is_noalias = blk: { - const index = std.math.cast(u5, i) orelse break :blk false; - break :blk @as(u1, @truncate(func_ty_info.noalias_bits >> index)) != 0; - }; - const param_fields = .{ // is_generic: bool, Value.makeBool(is_generic).toIntern(), @@ -16934,7 +16331,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .child = param_info_ty.toIntern(), }); const new_decl_val = (try pt.aggregateValue(new_decl_ty, param_vals)).toIntern(); - const slice_ty = (try pt.ptrTypeSema(.{ + const slice_ty = (try pt.ptrType(.{ .child = param_info_ty.toIntern(), .flags = .{ .size = .slice, @@ -16956,18 +16353,23 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } }); }; + const ret_ty_is_generic = generic: { + const ret_ty: Type = .fromInterned(func_ty_info.return_type); + if (ret_ty.toIntern() == .generic_poison_type or + (ret_ty.zigTypeTag(zcu) == .error_union and + ret_ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type)) + { + break :generic true; + } + break :generic false; + }; + if (ret_ty_is_generic or Type.fromInterned(func_ty_info.return_type).comptimeOnly(zcu)) { + func_is_generic = true; + } + const ret_ty_opt = try pt.intern(.{ .opt = .{ .ty = try pt.intern(.{ .opt_type = .type_type }), - .val = opt_val: { - const ret_ty: Type = .fromInterned(func_ty_info.return_type); - if (ret_ty.toIntern() == .generic_poison_type) break :opt_val .none; - if (ret_ty.zigTypeTag(zcu) == .error_union) { - if (ret_ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type) { - break :opt_val .none; - } - } - break :opt_val ret_ty.toIntern(); - }, + .val = if (ret_ty_is_generic) .none else func_ty_info.return_type, } }); const callconv_ty = try sema.getBuiltinType(src, .CallingConvention); @@ -16980,7 +16382,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // calling_convention: CallingConvention, callconv_val.toIntern(), // is_generic: bool, - Value.makeBool(func_ty_info.is_generic).toIntern(), + Value.makeBool(func_is_generic).toIntern(), // is_var_args: bool, Value.makeBool(func_ty_info.is_var_args).toIntern(), // return_type: ?type, @@ -17015,7 +16417,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const field_vals = .{ // bits: u16, - (try pt.intValue(.u16, ty.bitSize(zcu))).toIntern(), + (try pt.intValue(.u16, ty.floatBits(zcu.getTarget()))).toIntern(), }; return Air.internedToRef((try pt.internUnion(.{ .ty = type_info_ty.toIntern(), @@ -17025,10 +16427,17 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }, .pointer => { const info = ty.ptrInfo(zcu); - const alignment = if (info.flags.alignment.toByteUnits()) |alignment| - try pt.intValue(.comptime_int, alignment) - else - try Type.fromInterned(info.child).lazyAbiAlignment(pt); + const alignment_ty = try pt.optionalType(.usize_type); + const alignment_val: Value = val: { + const bytes = info.flags.alignment.toByteUnits() orelse { + break :val try pt.nullValue(alignment_ty); + }; + const int_val = try pt.intValue(.usize, bytes); + break :val .fromInterned(try pt.intern(.{ .opt = .{ + .ty = alignment_ty.toIntern(), + .val = int_val.toIntern(), + } })); + }; const addrspace_ty = try sema.getBuiltinType(src, .AddressSpace); const pointer_ty = try sema.getBuiltinType(src, .@"Type.Pointer"); @@ -17041,8 +16450,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai Value.makeBool(info.flags.is_const).toIntern(), // is_volatile: bool, Value.makeBool(info.flags.is_volatile).toIntern(), - // alignment: comptime_int, - alignment.toIntern(), + // alignment: ?usize, + alignment_val.toIntern(), // address_space: AddressSpace (try pt.enumValueFieldIndex(addrspace_ty, @intFromEnum(info.flags.address_space))).toIntern(), // child: type, @@ -17159,7 +16568,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }; // Build our ?[]const Error value - const slice_errors_ty = try pt.ptrTypeSema(.{ + const slice_errors_ty = try pt.ptrType(.{ .child = error_field_ty.toIntern(), .flags = .{ .size = .slice, @@ -17215,19 +16624,19 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }))); }, .@"enum" => { - const is_exhaustive = Value.makeBool(ip.loadEnumType(ty.toIntern()).tag_mode != .nonexhaustive); + const enum_obj = ip.loadEnumType(ty.toIntern()); + const is_exhaustive: Value = .makeBool(!enum_obj.nonexhaustive); const enum_field_ty = try sema.getBuiltinType(src, .@"Type.EnumField"); - const enum_field_vals = try sema.arena.alloc(InternPool.Index, ip.loadEnumType(ty.toIntern()).names.len); + const enum_field_vals = try sema.arena.alloc(InternPool.Index, enum_obj.field_names.len); for (enum_field_vals, 0..) |*field_val, tag_index| { - const enum_type = ip.loadEnumType(ty.toIntern()); - const value_val = if (enum_type.values.len > 0) + const value_val = if (enum_obj.field_values.len > 0) try ip.getCoercedInts( gpa, io, pt.tid, - ip.indexToKey(enum_type.values.get(ip)[tag_index]).int, + ip.indexToKey(enum_obj.field_values.get(ip)[tag_index]).int, .comptime_int_type, ) else @@ -17235,7 +16644,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // TODO: write something like getCoercedInts to avoid needing to dupe const name_val = v: { - const tag_name = enum_type.names.get(ip)[tag_index]; + const tag_name = enum_obj.field_names.get(ip)[tag_index]; const tag_name_len = tag_name.length(ip); const new_decl_ty = try pt.arrayType(.{ .len = tag_name_len, @@ -17275,7 +16684,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .child = enum_field_ty.toIntern(), }); const new_decl_val = (try pt.aggregateValue(fields_array_ty, enum_field_vals)).toIntern(); - const slice_ty = (try pt.ptrTypeSema(.{ + const slice_ty = (try pt.ptrType(.{ .child = enum_field_ty.toIntern(), .flags = .{ .size = .slice, @@ -17303,7 +16712,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const field_values = .{ // tag_type: type, - ip.loadEnumType(ty.toIntern()).tag_ty, + ip.loadEnumType(ty.toIntern()).int_tag_type, // fields: []const EnumField, fields_val, // decls: []const Declaration, @@ -17321,17 +16730,16 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const type_union_ty = try sema.getBuiltinType(src, .@"Type.Union"); const union_field_ty = try sema.getBuiltinType(src, .@"Type.UnionField"); - try ty.resolveLayout(pt); // Getting alignment requires type layout - const union_obj = zcu.typeToUnion(ty).?; - const tag_type = union_obj.loadTagType(ip); - const layout = union_obj.flagsUnordered(ip).layout; + const union_obj = ip.loadUnionType(ty.toIntern()); + const enum_obj = ip.loadEnumType(union_obj.enum_tag_type); + const layout = union_obj.layout; - const union_field_vals = try gpa.alloc(InternPool.Index, tag_type.names.len); + const union_field_vals = try gpa.alloc(InternPool.Index, enum_obj.field_names.len); defer gpa.free(union_field_vals); for (union_field_vals, 0..) |*field_val, field_index| { const name_val = v: { - const field_name = tag_type.names.get(ip)[field_index]; + const field_name = enum_obj.field_names.get(ip)[field_index]; const field_name_len = field_name.length(ip); const new_decl_ty = try pt.arrayType(.{ .len = field_name_len, @@ -17356,19 +16764,31 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } }); }; - const alignment = switch (layout) { - .auto, .@"extern" => try ty.fieldAlignmentSema(field_index, pt), - .@"packed" => .none, + const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); + + const alignment_ty = try pt.optionalType(.usize_type); + const alignment_val: Value = val: { + const a: Alignment = switch (layout) { + .auto, .@"extern" => ty.explicitFieldAlignment(field_index, zcu), + .@"packed" => .none, + }; + const bytes = a.toByteUnits() orelse { + break :val try pt.nullValue(alignment_ty); + }; + const int_val = try pt.intValue(.usize, bytes); + break :val .fromInterned(try pt.intern(.{ .opt = .{ + .ty = alignment_ty.toIntern(), + .val = int_val.toIntern(), + } })); }; - const field_ty = union_obj.field_types.get(ip)[field_index]; const union_field_fields = .{ // name: [:0]const u8, name_val, // type: type, - field_ty, - // alignment: comptime_int, - (try pt.intValue(.comptime_int, alignment.toByteUnits() orelse 0)).toIntern(), + field_ty.toIntern(), + // alignment: ?usize, + alignment_val.toIntern(), }; field_val.* = (try pt.aggregateValue(union_field_ty, &union_field_fields)).toIntern(); } @@ -17379,7 +16799,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .child = union_field_ty.toIntern(), }); const new_decl_val = (try pt.aggregateValue(array_fields_ty, union_field_vals)).toIntern(); - const slice_ty = (try pt.ptrTypeSema(.{ + const slice_ty = (try pt.ptrType(.{ .child = union_field_ty.toIntern(), .flags = .{ .size = .slice, @@ -17431,8 +16851,6 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const type_struct_ty = try sema.getBuiltinType(src, .@"Type.Struct"); const struct_field_ty = try sema.getBuiltinType(src, .@"Type.StructField"); - try ty.resolveLayout(pt); // Getting alignment requires type layout - var struct_field_vals: []InternPool.Index = &.{}; defer gpa.free(struct_field_vals); fv: { @@ -17468,11 +16886,10 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } }); }; - try Type.fromInterned(field_ty).resolveLayout(pt); - const is_comptime = field_val != .none; const opt_default_val = if (is_comptime) Value.fromInterned(field_val) else null; const default_val_ptr = try sema.optRefValue(opt_default_val); + const struct_field_fields = .{ // name: [:0]const u8, name_val, @@ -17482,8 +16899,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai default_val_ptr.toIntern(), // is_comptime: bool, Value.makeBool(is_comptime).toIntern(), - // alignment: comptime_int, - (try pt.intValue(.comptime_int, Type.fromInterned(field_ty).abiAlignment(zcu).toByteUnits() orelse 0)).toIntern(), + // alignment: ?usize, + (try pt.nullValue(try pt.optionalType(.usize_type))).toIntern(), }; struct_field_val.* = (try pt.aggregateValue(struct_field_ty, &struct_field_fields)).toIntern(); } @@ -17492,16 +16909,17 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .struct_type => ip.loadStructType(ty.toIntern()), else => unreachable, }; + try sema.ensureStructDefaultsResolved(ty, src); // can't do this sooner, since it's not allowed on tuples struct_field_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len); - try ty.resolveStructFieldInits(pt); - for (struct_field_vals, 0..) |*field_val, field_index| { - const field_name = struct_type.fieldName(ip, field_index); + const field_name = struct_type.field_names.get(ip)[field_index]; const field_name_len = field_name.length(ip); const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); - const field_init = struct_type.fieldInit(ip, field_index); - const field_is_comptime = struct_type.fieldIsComptime(ip, field_index); + const field_default: InternPool.Index = if (struct_type.field_defaults.len > 0) d: { + break :d struct_type.field_defaults.get(ip)[field_index]; + } else .none; + const field_is_comptime = struct_type.field_is_comptime_bits.get(ip, field_index); const name_val = v: { const new_decl_ty = try pt.arrayType(.{ .len = field_name_len, @@ -17526,15 +16944,23 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } }); }; - const opt_default_val = if (field_init == .none) null else Value.fromInterned(field_init); + const opt_default_val: ?Value = if (field_default == .none) null else .fromInterned(field_default); const default_val_ptr = try sema.optRefValue(opt_default_val); - const alignment = switch (struct_type.layout) { - .@"packed" => .none, - else => try field_ty.structFieldAlignmentSema( - struct_type.fieldAlign(ip, field_index), - struct_type.layout, - pt, - ), + + const alignment_ty = try pt.optionalType(.usize_type); + const alignment_val: Value = val: { + const a: Alignment = switch (struct_type.layout) { + .auto, .@"extern" => ty.explicitFieldAlignment(field_index, zcu), + .@"packed" => .none, + }; + const bytes = a.toByteUnits() orelse { + break :val try pt.nullValue(alignment_ty); + }; + const int_val = try pt.intValue(.usize, bytes); + break :val .fromInterned(try pt.intern(.{ .opt = .{ + .ty = alignment_ty.toIntern(), + .val = int_val.toIntern(), + } })); }; const struct_field_fields = .{ @@ -17546,8 +16972,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai default_val_ptr.toIntern(), // is_comptime: bool, Value.makeBool(field_is_comptime).toIntern(), - // alignment: comptime_int, - (try pt.intValue(.comptime_int, alignment.toByteUnits() orelse 0)).toIntern(), + // alignment: ?usize, + alignment_val.toIntern(), }; field_val.* = (try pt.aggregateValue(struct_field_ty, &struct_field_fields)).toIntern(); } @@ -17559,7 +16985,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .child = struct_field_ty.toIntern(), }); const new_decl_val = (try pt.aggregateValue(array_fields_ty, struct_field_vals)).toIntern(); - const slice_ty = (try pt.ptrTypeSema(.{ + const slice_ty = (try pt.ptrType(.{ .child = struct_field_ty.toIntern(), .flags = .{ .size = .slice, @@ -17585,9 +17011,9 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const backing_integer_val = try pt.intern(.{ .opt = .{ .ty = (try pt.optionalType(.type_type)).toIntern(), - .val = if (zcu.typeToPackedStruct(ty)) |packed_struct| val: { - assert(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)).isInt(zcu)); - break :val packed_struct.backingIntTypeUnordered(ip); + .val = if (zcu.typeToPackedStruct(ty)) |struct_obj| val: { + assert(Type.fromInterned(struct_obj.packed_backing_int_type).isInt(zcu)); + break :val struct_obj.packed_backing_int_type; } else .none, } }); @@ -17616,7 +17042,6 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .@"opaque" => { const type_opaque_ty = try sema.getBuiltinType(src, .@"Type.Opaque"); - try ty.resolveFields(pt); const decls_val = try sema.typeInfoDecls(src, ty.getNamespace(zcu)); const field_values = .{ @@ -17658,7 +17083,7 @@ fn typeInfoDecls( .child = declaration_ty.toIntern(), }); const new_decl_val = (try pt.aggregateValue(array_decl_ty, decl_vals.items)).toIntern(); - const slice_ty = (try pt.ptrTypeSema(.{ + const slice_ty = (try pt.ptrType(.{ .child = declaration_ty.toIntern(), .flags = .{ .size = .slice, @@ -17740,7 +17165,7 @@ fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. _ = block; const zir_datas = sema.code.instructions.items(.data); const inst_data = zir_datas[@intFromEnum(inst)].un_node; - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); return Air.internedToRef(operand_ty.toIntern()); } @@ -17754,7 +17179,7 @@ fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr .parent = block, .sema = sema, .namespace = block.namespace, - .instructions = .{}, + .instructions = .empty, .inlining = block.inlining, .comptime_reason = null, .is_typeof = true, @@ -17772,7 +17197,7 @@ fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); const res_ty = try sema.log2IntType(block, operand_ty, src); return Air.internedToRef(res_ty.toIntern()); @@ -17783,22 +17208,12 @@ fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) Compi const zcu = pt.zcu; switch (operand.zigTypeTag(zcu)) { .comptime_int => return .comptime_int, - .int => { - const bits = operand.bitSize(zcu); - const count = if (bits == 0) - 0 - else blk: { - var count: u16 = 0; - var s = bits - 1; - while (s != 0) : (s >>= 1) { - count += 1; - } - break :blk count; - }; - return pt.intType(.unsigned, count); - }, + .int => return pt.intType(.unsigned, switch (operand.intInfo(zcu).bits) { + 0 => 0, + else => |b| std.math.log2_int_ceil(u16, b), + }), .vector => { - const elem_ty = operand.elemType2(zcu); + const elem_ty = operand.childType(zcu); const log2_elem_ty = try sema.log2IntType(block, elem_ty, src); return pt.vectorType(.{ .len = operand.vectorLen(zcu), @@ -17832,7 +17247,7 @@ fn zirTypeofPeer( .parent = block, .sema = sema, .namespace = block.namespace, - .instructions = .{}, + .instructions = .empty, .inlining = block.inlining, .comptime_reason = null, .is_typeof = true, @@ -17852,7 +17267,7 @@ fn zirTypeofPeer( defer sema.gpa.free(inst_list); for (args, 0..) |arg_ref, i| { - inst_list[i] = try sema.resolveInst(arg_ref); + inst_list[i] = sema.resolveInst(arg_ref); } const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node }); @@ -17865,7 +17280,7 @@ fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); - const uncasted_operand = try sema.resolveInst(inst_data.operand); + const uncasted_operand = sema.resolveInst(inst_data.operand); const uncasted_ty = sema.typeOf(uncasted_operand); if (uncasted_ty.isVector(zcu)) { if (uncasted_ty.scalarType(zcu).zigTypeTag(zcu) != .bool) { @@ -17876,7 +17291,7 @@ fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air return analyzeBitNot(sema, block, uncasted_operand, src); } const operand = try sema.coerce(block, .bool, uncasted_operand, operand_src); - if (try sema.resolveValue(operand)) |val| { + if (sema.resolveValue(operand)) |val| { return if (val.isUndef(zcu)) .undef_bool else if (val.toBool()) .bool_false else .bool_true; } try sema.requireRuntimeBlock(block, src, null); @@ -17900,7 +17315,7 @@ fn zirBoolBr( const inst_data = datas[@intFromEnum(inst)].pl_node; const extra = sema.code.extraData(Zir.Inst.BoolBr, inst_data.payload_index); - const uncoerced_lhs = try sema.resolveInst(extra.data.lhs); + const uncoerced_lhs = sema.resolveInst(extra.data.lhs); const body = sema.code.bodySlice(extra.end, extra.data.body_len); const lhs_src = parent_block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); const rhs_src = parent_block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); @@ -18063,9 +17478,9 @@ fn zirIsNonNull( const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); try sema.checkNullableType(block, src, sema.typeOf(operand)); - return sema.analyzeIsNull(block, operand, true); + return sema.analyzeIsNull(block, src, operand, true); } fn zirIsNonNullPtr( @@ -18080,17 +17495,23 @@ fn zirIsNonNullPtr( const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); - const ptr = try sema.resolveInst(inst_data.operand); + const ptr = sema.resolveInst(inst_data.operand); const ptr_ty = sema.typeOf(ptr); - try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2(zcu)); - if (try sema.resolveValue(ptr)) |ptr_val| { - if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |loaded_val| { - return sema.analyzeIsNull(block, Air.internedToRef(loaded_val.toIntern()), true); - } + assert(ptr_ty.zigTypeTag(zcu) == .pointer); + const nullable_ty = ptr_ty.childType(zcu); + + try sema.checkNullableType(block, src, nullable_ty); + + if (try sema.resolveIsNullFromType(block, src, nullable_ty)) |is_null| { + return .fromValue(.makeBool(!is_null)); } - if (ptr_ty.childType(zcu).isNullFromType(zcu)) |is_null| { - return if (is_null) .bool_false else .bool_true; + + if (sema.resolveValue(ptr)) |ptr_val| { + if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |nullable_val| { + return sema.analyzeIsNull(block, src, .fromValue(nullable_val), true); + } } + return block.addUnOp(.is_non_null_ptr, ptr); } @@ -18111,7 +17532,7 @@ fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); try sema.checkErrorType(block, src, sema.typeOf(operand)); return sema.analyzeIsNonErr(block, src, operand); } @@ -18124,8 +17545,11 @@ fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); - const ptr = try sema.resolveInst(inst_data.operand); - try sema.checkErrorType(block, src, sema.typeOf(ptr).elemType2(zcu)); + const ptr = sema.resolveInst(inst_data.operand); + const ptr_ty = sema.typeOf(ptr); + assert(ptr_ty.zigTypeTag(zcu) == .pointer); + const error_ty = ptr_ty.childType(zcu); + try sema.checkErrorType(block, src, error_ty); const loaded = try sema.analyzeLoad(block, src, ptr, src); return sema.analyzeIsNonErr(block, src, loaded); } @@ -18136,7 +17560,7 @@ fn zirRetIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); return sema.analyzeIsNonErr(block, src, operand); } @@ -18157,7 +17581,7 @@ fn zirCondbr( const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len); const else_body = sema.code.bodySlice(extra.end + then_body.len, extra.data.else_body_len); - const uncasted_cond = try sema.resolveInst(extra.data.condition); + const uncasted_cond = sema.resolveInst(extra.data.condition); const cond = try sema.coerce(parent_block, .bool, uncasted_cond, cond_src); if (try sema.resolveDefinedValue(parent_block, cond_src, cond)) |cond_val| { @@ -18193,7 +17617,7 @@ fn zirCondbr( if (sema.code.instructions.items(.tag)[@intFromEnum(index)] != .is_non_err) break :blk null; const err_inst_data = sema.code.instructions.items(.data)[@intFromEnum(index)].un_node; - const err_operand = try sema.resolveInst(err_inst_data.operand); + const err_operand = sema.resolveInst(err_inst_data.operand); const operand_ty = sema.typeOf(err_operand); assert(operand_ty.zigTypeTag(zcu) == .error_union); const result_ty = operand_ty.errorUnionSet(zcu); @@ -18241,7 +17665,7 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError! const operand_src = parent_block.src(.{ .node_offset_try_operand = inst_data.src_node }); const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); const body = sema.code.bodySlice(extra.end, extra.data.body_len); - const err_union = try sema.resolveInst(extra.data.operand); + const err_union = sema.resolveInst(extra.data.operand); const err_union_ty = sema.typeOf(err_union); const pt = sema.pt; const zcu = pt.zcu; @@ -18294,6 +17718,11 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError! } }, }); sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); + + // The payload type might still be OPV, in which case `try_inst` is just there for the runtime + // control flow and we should return a comptime-known result. + if (try err_union_ty.errorUnionPayload(zcu).onePossibleValue(pt)) |opv| return .fromValue(opv); + return try_inst; } @@ -18303,7 +17732,7 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr const operand_src = parent_block.src(.{ .node_offset_try_operand = inst_data.src_node }); const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); const body = sema.code.bodySlice(extra.end, extra.data.body_len); - const operand = try sema.resolveInst(extra.data.operand); + const operand = sema.resolveInst(extra.data.operand); const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src); const err_union_ty = sema.typeOf(err_union); const pt = sema.pt; @@ -18347,7 +17776,7 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr const operand_ty = sema.typeOf(operand); const ptr_info = operand_ty.ptrInfo(zcu); - const res_ty = try pt.ptrTypeSema(.{ + const res_ty = try pt.ptrType(.{ .child = err_union_ty.errorUnionPayload(zcu).toIntern(), .flags = .{ .is_const = ptr_info.flags.is_const, @@ -18396,9 +17825,9 @@ fn ensurePostHoc(sema: *Sema, block: *Block, dest_block: Zir.Inst.Index) !*Label .label = .{ .zir_block = dest_block, .merges = .{ - .src_locs = .{}, - .results = .{}, - .br_list = .{}, + .src_locs = .empty, + .results = .empty, + .br_list = .empty, .block_inst = new_block_inst, }, }, @@ -18406,7 +17835,7 @@ fn ensurePostHoc(sema: *Sema, block: *Block, dest_block: Zir.Inst.Index) !*Label .parent = block, .sema = sema, .namespace = block.namespace, - .instructions = .{}, + .instructions = .empty, .label = &labeled_block.label, .inlining = block.inlining, .comptime_reason = block.comptime_reason, @@ -18424,7 +17853,7 @@ fn ensurePostHoc(sema: *Sema, block: *Block, dest_block: Zir.Inst.Index) !*Label fn addRuntimeBreak(sema: *Sema, child_block: *Block, block_inst: Zir.Inst.Index, break_operand: Zir.Inst.Ref) !void { const labeled_block = try sema.ensurePostHoc(child_block, block_inst); - const operand = try sema.resolveInst(break_operand); + const operand = sema.resolveInst(break_operand); const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand); try labeled_block.label.merges.results.append(sema.gpa, operand); @@ -18510,9 +17939,9 @@ fn zirRetImplicit( return; } - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = .zero }); - const base_tag = sema.fn_ret_ty.baseZigTypeTag(zcu); + const base_tag = sema.fn_ret_ty.optEuBaseType(zcu).zigTypeTag(zcu); if (base_tag == .noreturn) { const msg = msg: { const msg = try sema.errMsg(ret_ty_src, "function declared '{f}' implicitly returns", .{ @@ -18543,7 +17972,7 @@ fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!voi defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const src = block.nodeOffset(inst_data.src_node); return sema.analyzeRet(block, operand, src, block.src(.{ .node_offset_return_operand = inst_data.src_node })); @@ -18555,7 +17984,7 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!voi const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); - const ret_ptr = try sema.resolveInst(inst_data.operand); + const ret_ptr = sema.resolveInst(inst_data.operand); if (block.isComptime() or block.inlining != null or sema.func_is_naked) { const operand = try sema.analyzeLoad(block, src, ret_ptr, src); @@ -18652,7 +18081,7 @@ fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE if (block.isComptime() or block.is_typeof) return; const save_index = inst_data.operand == .none or b: { - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); break :b operand_ty.isError(zcu); }; @@ -18701,7 +18130,7 @@ fn restoreErrRetIndex(sema: *Sema, start_block: *Block, src: LazySrcLoc, target_ return; // No need to restore }; - const operand = try sema.resolveInstAllowNone(operand_zir); + const operand = sema.resolveInstAllowNone(operand_zir); if (start_block.isComptime() or start_block.is_typeof) { const is_non_error = if (operand != .none) blk: { @@ -18809,8 +18238,6 @@ fn analyzeRet( return sema.failWithOwnedErrorMsg(block, msg); } - try sema.fn_ret_ty.resolveLayout(pt); - try sema.validateRuntimeValue(block, operand_src, operand); const air_tag: Air.Inst.Tag = if (block.wantSafety()) .ret_safe else .ret; @@ -18853,8 +18280,8 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const hostsize_src = block.src(.{ .node_offset_ptr_hostsize = extra.data.src_node }); const elem_ty = blk: { - const air_inst = try sema.resolveInst(extra.data.elem_type); - const ty = sema.analyzeAsType(block, elem_ty_src, air_inst) catch |err| { + const air_inst = sema.resolveInst(extra.data.elem_type); + const ty = sema.analyzeAsType(block, elem_ty_src, .type, air_inst) catch |err| { if (err == error.AnalysisFail and sema.err != null and sema.typeOf(air_inst).isSinglePointer(zcu)) { try sema.errNote(elem_ty_src, sema.err.?, "use '.*' to dereference pointer", .{}); } @@ -18874,7 +18301,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const sentinel = if (inst_data.flags.has_sentinel) blk: { const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); extra_i += 1; - const coerced = try sema.coerce(block, elem_ty, try sema.resolveInst(ref), sentinel_src); + const coerced = try sema.coerce(block, elem_ty, sema.resolveInst(ref), sentinel_src); const val = try sema.resolveConstDefinedValue(block, sentinel_src, coerced, .{ .simple = .pointer_sentinel }); try checkSentinelType(sema, block, sentinel_src, elem_ty); if (val.canMutateComptimeVarState(zcu)) { @@ -18887,18 +18314,9 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const abi_align: Alignment = if (inst_data.flags.has_align) blk: { const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); extra_i += 1; - const coerced = try sema.coerce(block, align_ty, try sema.resolveInst(ref), align_src); + const coerced = try sema.coerce(block, align_ty, sema.resolveInst(ref), align_src); const val = try sema.resolveConstDefinedValue(block, align_src, coerced, .{ .simple = .@"align" }); - // Check if this happens to be the lazy alignment of our element type, in - // which case we can make this 0 without resolving it. - switch (zcu.intern_pool.indexToKey(val.toIntern())) { - .int => |int| switch (int.storage) { - .lazy_align => |lazy_ty| if (lazy_ty == elem_ty.toIntern()) break :blk .none, - else => {}, - }, - else => {}, - } - const align_bytes = (try val.getUnsignedIntSema(pt)).?; + const align_bytes = val.toUnsignedInt(zcu); break :blk try sema.validateAlign(block, align_src, align_bytes); } else .none; @@ -18928,7 +18346,8 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air elem_ty.fmt(pt), bit_offset, bit_offset - host_size * 8, host_size, }); } - const elem_bit_size = try elem_ty.bitSizeSema(pt); + try sema.ensureLayoutResolved(elem_ty, elem_ty_src, .bit_ptr_child); + const elem_bit_size = elem_ty.bitSize(zcu); if (elem_bit_size > host_size * 8 - bit_offset) { return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {d} ends {d} bits after the end of a {d} byte host integer", .{ elem_ty.fmt(pt), bit_offset, elem_bit_size - (host_size * 8 - bit_offset), host_size, @@ -18942,31 +18361,18 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air } } else if (inst_data.size != .one and elem_ty.zigTypeTag(zcu) == .@"opaque") { return sema.fail(block, elem_ty_src, "indexable pointer to opaque type '{f}' not allowed", .{elem_ty.fmt(pt)}); - } else if (inst_data.size == .c) { - if (!try sema.validateExternType(elem_ty, .other)) { - const msg = msg: { - const msg = try sema.errMsg(elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{f}'", .{elem_ty.fmt(pt)}); - errdefer msg.destroy(sema.gpa); - - try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src, elem_ty, .other); - - try sema.addDeclaredHereNote(msg, elem_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } } - if (host_size != 0 and !try sema.validatePackedType(elem_ty)) { - return sema.failWithOwnedErrorMsg(block, msg: { + if (host_size != 0) { + if (elem_ty.unpackable(zcu)) |reason| return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(elem_ty_src, "bit-pointer cannot refer to value of type '{f}'", .{elem_ty.fmt(pt)}); errdefer msg.destroy(sema.gpa); - try sema.explainWhyTypeIsNotPacked(msg, elem_ty_src, elem_ty); + try sema.explainWhyTypeIsUnpackable(msg, elem_ty_src, reason); break :msg msg; }); } - const ty = try pt.ptrTypeSema(.{ + const ty = try pt.ptrType(.{ .child = elem_ty.toIntern(), .sentinel = sentinel, .flags = .{ @@ -18996,6 +18402,8 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const pt = sema.pt; const zcu = pt.zcu; + try sema.ensureLayoutResolved(obj_ty, ty_src, .init); + switch (obj_ty.zigTypeTag(zcu)) { .@"struct" => return sema.structInitEmpty(block, obj_ty, src, src), .array, .vector => return sema.arrayInitEmpty(block, src, obj_ty), @@ -19058,6 +18466,9 @@ fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is .child = ptr_ty.childType(zcu).toIntern(), }); } else ty_operand; + + try sema.ensureLayoutResolved(init_ty, src, .init); + const obj_ty = init_ty.optEuBaseType(zcu); const empty_ref = switch (obj_ty.zigTypeTag(zcu)) { @@ -19069,13 +18480,13 @@ fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is const init_ref = try sema.coerce(block, init_ty, empty_ref, src); if (is_byref) { - const init_val = (try sema.resolveValue(init_ref)).?; - return sema.uavRef(init_val.toIntern()); + return sema.uavRef(sema.resolveValue(init_ref).?); } else { return init_ref; } } +/// Asserts that the layout of `struct_ty` is already resolved. fn structInitEmpty( sema: *Sema, block: *Block, @@ -19087,7 +18498,7 @@ fn structInitEmpty( const zcu = pt.zcu; const gpa = sema.gpa; // This logic must be synchronized with that in `zirStructInit`. - try struct_ty.resolveFields(pt); + struct_ty.assertHasLayout(zcu); // The init values to use for the struct instance. const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount(zcu)); @@ -19118,63 +18529,36 @@ fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) Com fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const pt = sema.pt; + const zcu = pt.zcu; + const ip = &zcu.intern_pool; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); const field_src = block.builtinCallArgSrc(inst_data.src_node, 1); - const init_src = block.builtinCallArgSrc(inst_data.src_node, 2); + const payload_src = block.builtinCallArgSrc(inst_data.src_node, 2); const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data; const union_ty = try sema.resolveType(block, ty_src, extra.union_type); if (union_ty.zigTypeTag(pt.zcu) != .@"union") { return sema.fail(block, ty_src, "expected union type, found '{f}'", .{union_ty.fmt(pt)}); } + union_ty.assertHasLayout(zcu); // from a previous `field_type_ref` instruction const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .union_field_names }); - const init = try sema.resolveInst(extra.init); - return sema.unionInit(block, init, init_src, union_ty, ty_src, field_name, field_src); -} - -fn unionInit( - sema: *Sema, - block: *Block, - uncasted_init: Air.Inst.Ref, - init_src: LazySrcLoc, - union_ty: Type, - union_ty_src: LazySrcLoc, - field_name: InternPool.NullTerminatedString, - field_src: LazySrcLoc, -) CompileError!Air.Inst.Ref { - const pt = sema.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src); const field_ty: Type = .fromInterned(zcu.typeToUnion(union_ty).?.field_types.get(ip)[field_index]); - const init = try sema.coerce(block, field_ty, uncasted_init, init_src); - _ = union_ty_src; - return unionInitFromEnumTag(sema, block, init_src, union_ty, field_index, init); -} -fn unionInitFromEnumTag( - sema: *Sema, - block: *Block, - init_src: LazySrcLoc, - union_ty: Type, - field_index: u32, - init: Air.Inst.Ref, -) !Air.Inst.Ref { - const pt = sema.pt; - const zcu = pt.zcu; + const payload = try sema.coerce(block, field_ty, sema.resolveInst(extra.init), payload_src); + + if (union_ty.containerLayout(zcu) == .@"packed") { + return sema.bitCast(block, union_ty, payload, block.nodeOffset(inst_data.src_node), payload_src); + } - if (try sema.resolveValue(init)) |init_val| { + if (sema.resolveValue(payload)) |payload_val| { const tag_ty = union_ty.unionTagTypeHypothetical(zcu); const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index); - return Air.internedToRef((try pt.internUnion(.{ - .ty = union_ty.toIntern(), - .tag = tag_val.toIntern(), - .val = init_val.toIntern(), - }))); + return .fromValue(try pt.unionValue(union_ty, tag_val, payload_val)); } - try sema.requireRuntimeBlock(block, init_src, null); - return block.addUnionInit(union_ty, field_index, init); + try sema.requireRuntimeBlock(block, payload_src, null); + return block.addUnionInit(union_ty, field_index, payload); } fn zirStructInit( @@ -19202,8 +18586,8 @@ fn zirStructInit( // The type wasn't actually known, so treat this as an anon struct init. return sema.structInitAnon(block, src, inst, .typed_init, extra.data, extra.end, is_ref); }; + try sema.ensureLayoutResolved(result_ty, src, .init); const resolved_ty = result_ty.optEuBaseType(zcu); - try resolved_ty.resolveLayout(pt); if (resolved_ty.zigTypeTag(zcu) == .@"struct") { // This logic must be synchronized with that in `zirStructInitEmpty`. @@ -19226,7 +18610,6 @@ fn zirStructInit( var field_i: u32 = 0; var extra_index = extra.end; - const is_packed = resolved_ty.containerLayout(zcu) == .@"packed"; while (field_i < extra.data.fields_len) : (field_i += 1) { const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index); extra_index = item.end; @@ -19248,19 +18631,16 @@ fn zirStructInit( assert(field_inits[field_index] == .none); field_assign_idxs[field_index] = field_i; found_fields[field_index] = item.data.field_type; - const uncoerced_init = try sema.resolveInst(item.data.init); + const uncoerced_init = sema.resolveInst(item.data.init); const field_ty = resolved_ty.fieldType(field_index, zcu); field_inits[field_index] = try sema.coerce(block, field_ty, uncoerced_init, field_src); - if (!is_packed) { - try resolved_ty.resolveStructFieldInits(pt); - if (try resolved_ty.structFieldValueComptime(pt, field_index)) |default_value| { - const init_val = (try sema.resolveValue(field_inits[field_index])) orelse { - return sema.failWithNeededComptime(block, field_src, .{ .simple = .stored_to_comptime_field }); - }; - - if (!init_val.eql(default_value, resolved_ty.fieldType(field_index, zcu), zcu)) { - return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index); - } + if (resolved_ty.structFieldIsComptime(field_index, zcu)) { + const default_value = (try resolved_ty.structFieldValueComptime(pt, field_index)).?; + const init_val = sema.resolveValue(field_inits[field_index]) orelse { + return sema.failWithNeededComptime(block, field_src, .{ .simple = .stored_to_comptime_field }); + }; + if (!init_val.eql(default_value, resolved_ty.fieldType(field_index, zcu), zcu)) { + return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index); } } } @@ -19288,9 +18668,9 @@ fn zirStructInit( const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index); const field_ty: Type = .fromInterned(zcu.typeToUnion(resolved_ty).?.field_types.get(ip)[field_index]); - if (field_ty.zigTypeTag(zcu) == .noreturn) { + if (field_ty.classify(zcu) == .no_possible_value) { return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "cannot initialize 'noreturn' field of union", .{}); + const msg = try sema.errMsg(src, "cannot initialize union field with uninstantiable type '{f}'", .{field_ty.fmt(pt)}); errdefer msg.destroy(sema.gpa); try sema.addFieldErrNote(resolved_ty, field_index, msg, "field '{f}' declared here", .{ @@ -19301,21 +18681,31 @@ fn zirStructInit( }); } - const uncoerced_init_inst = try sema.resolveInst(item.data.init); + const uncoerced_init_inst = sema.resolveInst(item.data.init); const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src); - if (try sema.resolveValue(init_inst)) |val| { + if (resolved_ty.containerLayout(zcu) == .@"packed") { + const union_val = try sema.bitCast(block, resolved_ty, init_inst, src, field_src); + const result_val = try sema.coerce(block, result_ty, union_val, src); + if (is_ref) { + return sema.analyzeRef(block, src, result_val, .none); + } else { + return result_val; + } + } + + if (sema.resolveValue(init_inst)) |val| { const struct_val = Value.fromInterned(try pt.internUnion(.{ .ty = resolved_ty.toIntern(), .tag = tag_val.toIntern(), .val = val.toIntern(), })); const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), src); - const final_val = (try sema.resolveValue(final_val_inst)).?; - return sema.addConstantMaybeRef(final_val.toIntern(), is_ref); + const final_val = sema.resolveValue(final_val_inst).?; + return sema.addConstantMaybeRef(final_val, is_ref); } - if (try resolved_ty.comptimeOnlySema(pt)) { + if (resolved_ty.comptimeOnly(zcu)) { return sema.failWithNeededComptime(block, field_src, .{ .comptime_only = .{ .ty = resolved_ty, .msg = .union_init, @@ -19326,7 +18716,7 @@ fn zirStructInit( if (is_ref) { const target = zcu.getTarget(); - const alloc_ty = try pt.ptrTypeSema(.{ + const alloc_ty = try pt.ptrType(.{ .child = result_ty.toIntern(), .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, }); @@ -19334,10 +18724,6 @@ fn zirStructInit( const base_ptr = try sema.optEuBasePtrInit(block, alloc, src); const field_ptr = try sema.unionFieldPtr(block, field_src, base_ptr, field_name, field_src, resolved_ty, true); try sema.storePtr(block, src, field_ptr, init_inst); - if ((try sema.typeHasOnePossibleValue(tag_ty)) == null) { - const new_tag = Air.internedToRef(tag_val.toIntern()); - _ = try block.addBinOp(.set_union_tag, base_ptr, new_tag); - } return sema.makePtrConst(block, alloc); } @@ -19409,20 +18795,29 @@ fn finishStructInit( continue; } - try struct_ty.resolveStructFieldInits(pt); + if (struct_type.field_is_comptime_bits.get(ip, i)) { + field_inits[i] = .fromIntern(struct_type.field_defaults.get(ip)[i]); + continue; + } + + try sema.ensureStructDefaultsResolved(struct_ty, init_src); - const field_init = struct_type.fieldInit(ip, i); - if (field_init == .none) { - const field_name = struct_type.field_names.get(ip)[i]; - const template = "missing struct field: {f}"; - const args = .{field_name.fmt(ip)}; - if (root_msg) |msg| { - try sema.errNote(init_src, msg, template, args); - } else { - root_msg = try sema.errMsg(init_src, template, args); - } + const field_default: InternPool.Index = d: { + if (struct_type.field_defaults.len == 0) break :d .none; + break :d struct_type.field_defaults.get(ip)[i]; + }; + if (field_default != .none) { + field_inits[i] = .fromIntern(field_default); + continue; + } + + const field_name = struct_type.field_names.get(ip)[i]; + const template = "missing struct field: {f}"; + const args = .{field_name.fmt(ip)}; + if (root_msg) |msg| { + try sema.errNote(init_src, msg, template, args); } else { - field_inits[i] = Air.internedToRef(field_init); + root_msg = try sema.errMsg(init_src, template, args); } } }, @@ -19442,18 +18837,38 @@ fn finishStructInit( } } else null; - const runtime_index = opt_runtime_index orelse { - const elems = try sema.arena.alloc(InternPool.Index, field_inits.len); - for (elems, field_inits) |*elem, field_init| { - elem.* = (sema.resolveValue(field_init) catch unreachable).?.toIntern(); - } - const struct_val = try pt.aggregateValue(struct_ty, elems); - const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), init_src); - const final_val = (try sema.resolveValue(final_val_inst)).?; - return sema.addConstantMaybeRef(final_val.toIntern(), is_ref); + const runtime_index = opt_runtime_index orelse switch (struct_ty.containerLayout(zcu)) { + .auto, .@"extern" => { + const elems = try sema.arena.alloc(InternPool.Index, field_inits.len); + for (elems, field_inits) |*elem, field_init| { + elem.* = sema.resolveValue(field_init).?.toIntern(); + } + const struct_val = try pt.aggregateValue(struct_ty, elems); + const final_val_ref = try sema.coerce(block, result_ty, .fromValue(struct_val), init_src); + return sema.addConstantMaybeRef(sema.resolveValue(final_val_ref).?, is_ref); + }, + .@"packed" => { + const buf = try sema.arena.alloc(u8, @intCast((struct_ty.bitSize(zcu) + 7) / 8)); + var bit_offset: u16 = 0; + for (field_inits) |field_init| { + const field_val = sema.resolveValue(field_init).?; + field_val.writeToPackedMemory(pt, buf, bit_offset) catch |err| switch (err) { + error.ReinterpretDeclRef => unreachable, // bitpack fields cannot be pointers + error.OutOfMemory => |e| return e, + }; + bit_offset += @intCast(field_val.typeOf(zcu).bitSize(zcu)); + } + assert(bit_offset == struct_ty.bitSize(zcu)); + const struct_val = Value.readFromPackedMemory(struct_ty, pt, buf, 0, sema.arena) catch |err| switch (err) { + error.IllDefinedMemoryLayout => unreachable, // bitpacks have well-defined layout + error.OutOfMemory => |e| return e, + }; + const final_val_ref = try sema.coerce(block, result_ty, .fromValue(struct_val), init_src); + return sema.addConstantMaybeRef(sema.resolveValue(final_val_ref).?, is_ref); + }, }; - if (try struct_ty.comptimeOnlySema(pt)) { + if (struct_ty.comptimeOnly(zcu)) { return sema.failWithNeededComptime(block, block.src(.{ .init_elem = .{ .init_node_offset = init_src.offset.node_offset.x, .elem_index = @intCast(runtime_index), @@ -19468,9 +18883,8 @@ fn finishStructInit( } if (is_ref) { - try struct_ty.resolveLayout(pt); const target = zcu.getTarget(); - const alloc_ty = try pt.ptrTypeSema(.{ + const alloc_ty = try pt.ptrType(.{ .child = result_ty.toIntern(), .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, }); @@ -19489,7 +18903,6 @@ fn finishStructInit( .init_node_offset = init_src.offset.node_offset.x, .elem_index = @intCast(runtime_index), } })); - try struct_ty.resolveStructFieldInits(pt); const struct_val = try block.addAggregateInit(struct_ty, field_inits); return sema.coerce(block, result_ty, struct_val, init_src); } @@ -19558,7 +18971,7 @@ fn structInitAnon( field_name.* = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls); - const init = try sema.resolveInst(item.data.init); + const init = sema.resolveInst(item.data.init); field_ty.* = sema.typeOf(init).toIntern(); if (Type.fromInterned(field_ty.*).zigTypeTag(zcu) == .@"opaque") { const msg = msg: { @@ -19574,7 +18987,7 @@ fn structInitAnon( }; return sema.failWithOwnedErrorMsg(block, msg); } - if (try sema.resolveValue(init)) |init_val| { + if (sema.resolveValue(init)) |init_val| { field_val.* = init_val.toIntern(); any_values = true; } else { @@ -19585,12 +18998,11 @@ fn structInitAnon( break :rs runtime_index; }; - // We treat anonymous struct types as reified types, because there are similarities: - // * They use a form of structural equivalence, which we can easily model using a custom hash - // * They do not have captures - // * They immediately have their fields resolved - // In general, other code should treat anon struct types and reified struct types identically, - // so there's no point having a separate `InternPool.NamespaceType` field for them. + // We treat anonymous struct types as reified types, because there are similarities: they have + // no captures, and instead use a form of structural equivalence which we can easy represent by + // hashing the field names/types/values. They also perform layout resolution immediately. These + // similarities mean that other code should actually treat anon struct types and reified struct + // types identically anyway, so sharing the representation makes everything simpler. const type_hash: u64 = hash: { var hasher = std.hash.Wyhash.init(0); hasher.update(std.mem.sliceAsBytes(types)); @@ -19599,35 +19011,33 @@ fn structInitAnon( break :hash hasher.final(); }; const tracked_inst = try block.trackZir(inst); - const struct_ty = switch (try ip.getStructType(gpa, io, pt.tid, .{ - .layout = .auto, + const struct_ty: Type = switch (try ip.getReifiedStructType(gpa, io, pt.tid, .{ + .zir_index = tracked_inst, + .type_hash = type_hash, .fields_len = extra_data.fields_len, - .known_non_opv = false, - .requires_comptime = .unknown, + .layout = .auto, .any_comptime_fields = any_values, - .any_default_inits = any_values, - .inits_resolved = true, - .any_aligned_fields = false, - .key = .{ .reified = .{ - .zir_index = tracked_inst, - .type_hash = type_hash, - } }, - }, false)) { + .any_field_defaults = any_values, + .any_field_aligns = false, + .packed_backing_int_type = .none, + })) { + .existing => |ty| .fromInterned(ty), .wip => |wip| ty: { errdefer wip.cancel(ip, pt.tid); - const type_name = try sema.createTypeName(block, .anon, "struct", inst, wip.index); - wip.setName(ip, type_name.name, type_name.nav); - - const struct_type = ip.loadStructType(wip.index); + try sema.setTypeName(block, &wip, .anon, "struct", inst); - for (names, values, 0..) |name, init_val, field_idx| { - assert(struct_type.addFieldName(ip, name) == null); - if (init_val != .none) struct_type.setFieldComptime(ip, field_idx); - } - - @memcpy(struct_type.field_types.get(ip), types); + // Reified structs have field information populated immediately. + @memcpy(wip.field_names.get(ip), names); + @memcpy(wip.field_types.get(ip), types); if (any_values) { - @memcpy(struct_type.field_inits.get(ip), values); + @memcpy(wip.field_values.get(ip), values); + @memset(wip.field_is_comptime_bits.getAll(ip), 0); + for (values, 0..) |val, field_index| { + if (val == .none) continue; + const bit_bag_index = field_index / 32; + const mask = @as(u32, 1) << @intCast(field_index % 32); + wip.field_is_comptime_bits.getAll(ip)[bit_bag_index] |= mask; + } } const new_namespace_index = try pt.createNamespace(.{ @@ -19636,30 +19046,24 @@ fn structInitAnon( .file_scope = block.getFileScopeIndex(zcu), .generation = zcu.generation, }); - try zcu.comp.queueJob(.{ .resolve_type_fully = wip.index }); - codegen_type: { - if (zcu.comp.config.use_llvm) break :codegen_type; - if (block.ownerModule().strip) break :codegen_type; - zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); - try zcu.comp.queueJob(.{ .link_type = wip.index }); - } + errdefer pt.destroyNamespace(new_namespace_index); if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); - break :ty wip.finish(ip, new_namespace_index); + break :ty .fromInterned(wip.finish(ip, new_namespace_index)); }, - .existing => |ty| ty, }; - try sema.declareDependency(.{ .interned = struct_ty }); try sema.addTypeReferenceEntry(src, struct_ty); + // No need for `ensureNamespaceUpToDate` because this type's namespace is always empty. + try sema.ensureLayoutResolved(struct_ty, src, .init); _ = opt_runtime_index orelse { - const struct_val = try pt.aggregateValue(.fromInterned(struct_ty), values); - return sema.addConstantMaybeRef(struct_val.toIntern(), is_ref); + const struct_val = try pt.aggregateValue(struct_ty, values); + return sema.addConstantMaybeRef(struct_val, is_ref); }; if (is_ref) { const target = zcu.getTarget(); - const alloc_ty = try pt.ptrTypeSema(.{ - .child = struct_ty, + const alloc_ty = try pt.ptrType(.{ + .child = struct_ty.toIntern(), .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, }); const alloc = try block.addTy(.alloc, alloc_ty); @@ -19672,12 +19076,12 @@ fn structInitAnon( }; extra_index = item.end; - const field_ptr_ty = try pt.ptrTypeSema(.{ + const field_ptr_ty = try pt.ptrType(.{ .child = field_ty, .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, }); if (values[i] == .none) { - const init = try sema.resolveInst(item.data.init); + const init = sema.resolveInst(item.data.init); const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); _ = try block.addBinOp(.store, field_ptr, init); } @@ -19694,10 +19098,10 @@ fn structInitAnon( .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index), }; extra_index = item.end; - element_refs[i] = try sema.resolveInst(item.data.init); + element_refs[i] = sema.resolveInst(item.data.init); } - return block.addAggregateInit(.fromInterned(struct_ty), element_refs); + return block.addAggregateInit(struct_ty, element_refs); } fn zirArrayInit( @@ -19737,17 +19141,16 @@ fn zirArrayInit( } }); // Less inits than needed. if (i + 2 > args.len) if (is_tuple) { - const default_val = array_ty.structFieldDefaultValue(i, zcu).toIntern(); - if (default_val == .unreachable_value) { + const default_val = array_ty.structFieldDefaultValue(i, zcu) orelse { const template = "missing tuple field with index {d}"; if (root_msg) |msg| { try sema.errNote(src, msg, template, .{i}); } else { root_msg = try sema.errMsg(src, template, .{i}); } - } else { - dest.* = Air.internedToRef(default_val); - } + continue; + }; + dest.* = .fromValue(default_val); continue; } else { dest.* = Air.internedToRef(sentinel_val.?.toIntern()); @@ -19755,15 +19158,13 @@ fn zirArrayInit( }; const arg = args[i + 1]; - const resolved_arg = try sema.resolveInst(arg); + const resolved_arg = sema.resolveInst(arg); const elem_ty = if (is_tuple) array_ty.fieldType(i, zcu) else - array_ty.elemType2(zcu); + array_ty.childType(zcu); dest.* = try sema.coerce(block, elem_ty, resolved_arg, elem_src); if (is_tuple) { - if (array_ty.structFieldIsComptime(i, zcu)) - try array_ty.resolveStructFieldInits(pt); if (try array_ty.structFieldValueComptime(pt, i)) |field_val| { const init_val = try sema.resolveConstValue(block, elem_src, dest.*, .{ .simple = .stored_to_comptime_field }); if (!field_val.eql(init_val, elem_ty, zcu)) { @@ -19788,17 +19189,17 @@ fn zirArrayInit( const elem_vals = try sema.arena.alloc(InternPool.Index, resolved_args.len); for (elem_vals, resolved_args) |*val, arg| { // We checked that all args are comptime above. - val.* = (sema.resolveValue(arg) catch unreachable).?.toIntern(); + val.* = sema.resolveValue(arg).?.toIntern(); } const arr_val = try pt.aggregateValue(array_ty, elem_vals); const result_ref = try sema.coerce(block, result_ty, Air.internedToRef(arr_val.toIntern()), src); - const result_val = (try sema.resolveValue(result_ref)).?; - return sema.addConstantMaybeRef(result_val.toIntern(), is_ref); + const result_val = (sema.resolveValue(result_ref)).?; + return sema.addConstantMaybeRef(result_val, is_ref); }; if (is_ref) { const target = zcu.getTarget(); - const alloc_ty = try pt.ptrTypeSema(.{ + const alloc_ty = try pt.ptrType(.{ .child = result_ty.toIntern(), .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, }); @@ -19807,7 +19208,7 @@ fn zirArrayInit( if (is_tuple) { for (resolved_args, 0..) |arg, i| { - const elem_ptr_ty = try pt.ptrTypeSema(.{ + const elem_ptr_ty = try pt.ptrType(.{ .child = array_ty.fieldType(i, zcu).toIntern(), .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, }); @@ -19820,8 +19221,8 @@ fn zirArrayInit( return sema.makePtrConst(block, alloc); } - const elem_ptr_ty = try pt.ptrTypeSema(.{ - .child = array_ty.elemType2(zcu).toIntern(), + const elem_ptr_ty = try pt.ptrType(.{ + .child = array_ty.childType(zcu).toIntern(), .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, }); const elem_ptr_ty_ref = Air.internedToRef(elem_ptr_ty.toIntern()); @@ -19875,7 +19276,7 @@ fn arrayInitAnon( .init_node_offset = src.offset.node_offset.x, .elem_index = @intCast(i), } }); - const elem = try sema.resolveInst(operand); + const elem = sema.resolveInst(operand); types[i] = sema.typeOf(elem).toIntern(); if (Type.fromInterned(types[i]).zigTypeTag(zcu) == .@"opaque") { const msg = msg: { @@ -19887,7 +19288,7 @@ fn arrayInitAnon( }; return sema.failWithOwnedErrorMsg(block, msg); } - if (try sema.resolveValue(elem)) |val| { + if (sema.resolveValue(elem)) |val| { values[i] = val.toIntern(); any_comptime = true; } else { @@ -19917,7 +19318,7 @@ fn arrayInitAnon( const runtime_src = opt_runtime_src orelse { const tuple_val = try pt.aggregateValue(tuple_ty, values); - return sema.addConstantMaybeRef(tuple_val.toIntern(), is_ref); + return sema.addConstantMaybeRef(tuple_val, is_ref); }; try sema.requireRuntimeBlock(block, src, runtime_src); @@ -19927,25 +19328,25 @@ fn arrayInitAnon( .init_node_offset = src.offset.node_offset.x, .elem_index = @intCast(i), } }); - try sema.validateRuntimeValue(block, operand_src, try sema.resolveInst(operand)); + try sema.validateRuntimeValue(block, operand_src, sema.resolveInst(operand)); } if (is_ref) { const target = sema.pt.zcu.getTarget(); - const alloc_ty = try pt.ptrTypeSema(.{ + const alloc_ty = try pt.ptrType(.{ .child = tuple_ty.toIntern(), .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, }); const alloc = try block.addTy(.alloc, alloc_ty); for (operands, 0..) |operand, i_usize| { const i: u32 = @intCast(i_usize); - const field_ptr_ty = try pt.ptrTypeSema(.{ + const field_ptr_ty = try pt.ptrType(.{ .child = types[i], .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, }); if (values[i] == .none) { const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); - _ = try block.addBinOp(.store, field_ptr, try sema.resolveInst(operand)); + _ = try block.addBinOp(.store, field_ptr, sema.resolveInst(operand)); } } @@ -19954,14 +19355,14 @@ fn arrayInitAnon( const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len); for (operands, 0..) |operand, i| { - element_refs[i] = try sema.resolveInst(operand); + element_refs[i] = sema.resolveInst(operand); } return block.addAggregateInit(tuple_ty, element_refs); } -fn addConstantMaybeRef(sema: *Sema, val: InternPool.Index, is_ref: bool) !Air.Inst.Ref { - return if (is_ref) sema.uavRef(val) else Air.internedToRef(val); +fn addConstantMaybeRef(sema: *Sema, val: Value, is_ref: bool) !Air.Inst.Ref { + return if (is_ref) sema.uavRef(val) else .fromValue(val); } fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -19971,6 +19372,7 @@ fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro const field_src = block.builtinCallArgSrc(inst_data.src_node, 1); const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type); const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .field_name }); + try sema.ensureLayoutResolved(aggregate_ty, ty_src, .field_queried); return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src); } @@ -19990,9 +19392,11 @@ fn zirStructInitFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp const aggregate_ty = wrapped_aggregate_ty.optEuBaseType(zcu); const zir_field_name = sema.code.nullTerminatedString(extra.name_start); const field_name = try ip.getOrPutString(gpa, io, pt.tid, zir_field_name, .no_embedded_nulls); + try sema.ensureLayoutResolved(aggregate_ty, ty_src, .init); return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src); } +/// Asserts that the layout of `aggregate_ty` is resolved. fn fieldType( sema: *Sema, block: *Block, @@ -20004,9 +19408,9 @@ fn fieldType( const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; + aggregate_ty.assertHasLayout(zcu); var cur_ty = aggregate_ty; while (true) { - try cur_ty.resolveFields(pt); switch (cur_ty.zigTypeTag(zcu)) { .@"struct" => switch (ip.indexToKey(cur_ty.toIntern())) { .tuple_type => |tuple| { @@ -20024,10 +19428,11 @@ fn fieldType( }, .@"union" => { const union_obj = zcu.typeToUnion(cur_ty).?; - const field_index = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse + const enum_obj = ip.loadEnumType(union_obj.enum_tag_type); + const field_index = enum_obj.nameIndex(ip, field_name) orelse return sema.failWithBadUnionFieldAccess(block, cur_ty, union_obj, field_src, field_name); const field_ty = union_obj.field_types.get(ip)[field_index]; - return Air.internedToRef(field_ty); + return .fromIntern(field_ty); }, .optional => { // Struct/array init through optional requires the child type to not be a pointer. @@ -20056,7 +19461,6 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { const zcu = pt.zcu; const ip = &zcu.intern_pool; const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace); - try stack_trace_ty.resolveFields(pt); const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty); const opt_ptr_stack_trace_ty = try pt.optionalType(ptr_stack_trace_ty.toIntern()); @@ -20064,7 +19468,14 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { .func => |func| if (ip.funcAnalysisUnordered(func).has_error_trace and block.ownerModule().error_tracing) { return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty); }, - .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {}, + + .@"comptime", + .nav_ty, + .nav_val, + .type_layout, + .struct_defaults, + .memoized_state, + => {}, } return Air.internedToRef(try pt.intern(.{ .opt = .{ .ty = opt_ptr_stack_trace_ty.toIntern(), @@ -20083,15 +19494,16 @@ fn zirFrame( } fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const zcu = sema.pt.zcu; + const pt = sema.pt; + const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); const ty = try sema.resolveType(block, operand_src, inst_data.operand); + try sema.ensureLayoutResolved(ty, operand_src, .align_of); if (ty.isNoReturn(zcu)) { - return sema.fail(block, operand_src, "no align available for type '{f}'", .{ty.fmt(sema.pt)}); + return sema.fail(block, operand_src, "no align available for uninstantiable type '{f}'", .{ty.fmt(sema.pt)}); } - const val = try ty.lazyAbiAlignment(sema.pt); - return Air.internedToRef(val.toIntern()); + return .fromValue(try pt.intValue(.comptime_int, ty.abiAlignment(zcu).toByteUnits().?)); } fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -20099,7 +19511,7 @@ fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); const is_vector = operand_ty.zigTypeTag(zcu) == .vector; const operand_scalar_ty = operand_ty.scalarType(zcu); @@ -20108,7 +19520,7 @@ fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError } const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined; const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .u1_type, .len = len }) else .u1; - if (try sema.resolveValue(operand)) |val| { + if (sema.resolveValue(operand)) |val| { if (!is_vector) { return if (val.isUndef(zcu)) .undef_u1 else if (val.toBool()) .one_u1 else .zero_u1; } @@ -20131,7 +19543,7 @@ fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); - const uncoerced_operand = try sema.resolveInst(inst_data.operand); + const uncoerced_operand = sema.resolveInst(inst_data.operand); const operand = try sema.coerce(block, .anyerror, uncoerced_operand, operand_src); if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { @@ -20152,7 +19564,7 @@ fn zirAbs( const pt = sema.pt; const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); const operand_ty = sema.typeOf(operand); const scalar_ty = operand_ty.scalarType(zcu); @@ -20183,7 +19595,7 @@ fn maybeConstantUnaryMath( const pt = sema.pt; const zcu = pt.zcu; switch (result_ty.zigTypeTag(zcu)) { - .vector => if (try sema.resolveValue(operand)) |val| { + .vector => if (sema.resolveValue(operand)) |val| { const scalar_ty = result_ty.scalarType(zcu); const vec_len = result_ty.vectorLen(zcu); if (val.isUndef(zcu)) @@ -20196,7 +19608,7 @@ fn maybeConstantUnaryMath( } return Air.internedToRef((try pt.aggregateValue(result_ty, elems)).toIntern()); }, - else => if (try sema.resolveValue(operand)) |operand_val| { + else => if (sema.resolveValue(operand)) |operand_val| { if (operand_val.isUndef(zcu)) return try pt.undefRef(result_ty); const result_val = try eval(operand_val, result_ty, sema.arena, pt); @@ -20219,7 +19631,7 @@ fn zirUnaryMath( const pt = sema.pt; const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); const operand_ty = sema.typeOf(operand); const scalar_ty = operand_ty.scalarType(zcu); @@ -20244,12 +19656,11 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); const src = block.nodeOffset(inst_data.src_node); - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - try operand_ty.resolveLayout(pt); const enum_ty = switch (operand_ty.zigTypeTag(zcu)) { .enum_literal => { const val = (try sema.resolveDefinedValue(block, operand_src, operand)).?; @@ -20332,17 +19743,17 @@ fn zirReifySliceArgTy( // zig fmt: on }; - const operand_ty = try pt.ptrTypeSema(.{ + const operand_ty = try pt.ptrType(.{ .child = in_scalar_ty.toIntern(), .flags = .{ .size = .slice, .is_const = true }, }); - const operand_uncoerced = try sema.resolveInst(extra.operand); + const operand_uncoerced = sema.resolveInst(extra.operand); const operand_coerced = try sema.coerce(block, operand_ty, operand_uncoerced, src); const operand_val = try sema.resolveConstDefinedValue(block, src, operand_coerced, .{ .simple = comptime_reason }); const len_val: Value = .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.len); if (len_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, null); - const len = try len_val.toUnsignedIntSema(pt); + const len = len_val.toUnsignedInt(zcu); return .fromType(try pt.singleConstPtrType(try pt.arrayType(.{ .len = len, @@ -20365,12 +19776,12 @@ fn zirReifyEnumValueSliceTy( const int_tag_ty = try sema.resolveType(block, int_tag_ty_src, extra.lhs); - const operand_uncoerced = try sema.resolveInst(extra.rhs); + const operand_uncoerced = sema.resolveInst(extra.rhs); const operand_coerced = try sema.coerce(block, .slice_const_slice_const_u8, operand_uncoerced, field_names_src); const operand_val = try sema.resolveConstDefinedValue(block, field_names_src, operand_coerced, .{ .simple = .enum_field_names }); const len_val: Value = .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.len); if (len_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, field_names_src, null); - const len = try len_val.toUnsignedIntSema(pt); + const len = len_val.toUnsignedInt(zcu); return .fromType(try pt.singleConstPtrType(try pt.arrayType(.{ .len = len, @@ -20410,7 +19821,7 @@ fn zirReifyTuple( const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const operand_src = block.builtinCallArgSrc(extra.node, 0); - const types_uncoerced = try sema.resolveInst(extra.operand); + const types_uncoerced = sema.resolveInst(extra.operand); const types_coerced = try sema.coerce(block, .slice_const_type, types_uncoerced, operand_src); const types_slice_val = try sema.resolveConstDefinedValue(block, operand_src, types_coerced, .{ .simple = .tuple_field_types }); const types_array_val = try sema.derefSliceAsArray(block, operand_src, types_slice_val, .{ .simple = .tuple_field_types }); @@ -20422,6 +19833,7 @@ fn zirReifyTuple( if (field_ty_val.isUndef(zcu)) { return sema.failWithUseOfUndef(block, operand_src, null); } + try sema.validateTupleFieldType(block, field_ty_val.toType(), operand_src); field_ty.* = field_ty_val.toIntern(); } @@ -20456,12 +19868,12 @@ fn zirReifyPointer( const size_ty = try sema.getBuiltinType(size_src, .@"Type.Pointer.Size"); const attrs_ty = try sema.getBuiltinType(attrs_src, .@"Type.Pointer.Attributes"); - const size_uncoerced = try sema.resolveInst(extra.size); + const size_uncoerced = sema.resolveInst(extra.size); const size_coerced = try sema.coerce(block, size_ty, size_uncoerced, size_src); const size_val = try sema.resolveConstDefinedValue(block, size_src, size_coerced, .{ .simple = .pointer_size }); const size = try sema.interpretBuiltinType(block, size_src, size_val, std.builtin.Type.Pointer.Size); - const attrs_uncoerced = try sema.resolveInst(extra.attrs); + const attrs_uncoerced = sema.resolveInst(extra.attrs); const attrs_coerced = try sema.coerce(block, attrs_ty, attrs_uncoerced, attrs_src); const attrs_val = try sema.resolveConstDefinedValue(block, attrs_src, attrs_coerced, .{ .simple = .pointer_attrs }); const attrs = try sema.interpretBuiltinType(block, attrs_src, attrs_val, std.builtin.Type.Pointer.Attributes); @@ -20489,18 +19901,8 @@ fn zirReifyPointer( else => {}, } - if (size == .c and !try sema.validateExternType(elem_ty, .other)) { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "C pointers cannot point to non-C-ABI-compatible type '{f}'", .{elem_ty.fmt(pt)}); - errdefer msg.destroy(gpa); - try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src, elem_ty, .other); - try sema.addDeclaredHereNote(msg, elem_ty); - break :msg msg; - }); - } - const sentinel_ty = try pt.optionalType(elem_ty.toIntern()); - const sentinel_uncoerced = try sema.resolveInst(extra.sentinel); + const sentinel_uncoerced = sema.resolveInst(extra.sentinel); const sentinel_coerced = try sema.coerce(block, sentinel_ty, sentinel_uncoerced, sentinel_src); const sentinel_val = try sema.resolveConstDefinedValue(block, sentinel_src, sentinel_coerced, .{ .simple = .pointer_sentinel }); const opt_sentinel = sentinel_val.optionalValue(zcu); @@ -20516,7 +19918,7 @@ fn zirReifyPointer( } } - return .fromType(try pt.ptrTypeSema(.{ + return .fromType(try pt.ptrType(.{ .child = elem_ty.toIntern(), .sentinel = if (opt_sentinel) |s| s.toIntern() else .none, .flags = .{ @@ -20554,7 +19956,7 @@ fn zirReifyFn( const single_param_attrs_ty = try sema.getBuiltinType(param_attrs_src, .@"Type.Fn.Param.Attributes"); const fn_attrs_ty = try sema.getBuiltinType(fn_attrs_src, .@"Type.Fn.Attributes"); - const param_types_uncoerced = try sema.resolveInst(extra.param_types); + const param_types_uncoerced = sema.resolveInst(extra.param_types); const param_types_coerced = try sema.coerce(block, .slice_const_type, param_types_uncoerced, param_types_src); const param_types_slice = try sema.resolveConstDefinedValue(block, param_types_src, param_types_coerced, .{ .simple = .fn_param_types }); const param_types_arr = try sema.derefSliceAsArray(block, param_types_src, param_types_slice, .{ .simple = .fn_param_types }); @@ -20565,14 +19967,14 @@ fn zirReifyFn( .len = params_len, .child = single_param_attrs_ty.toIntern(), })); - const param_attrs_uncoerced = try sema.resolveInst(extra.param_attrs); + const param_attrs_uncoerced = sema.resolveInst(extra.param_attrs); const param_attrs_coerced = try sema.coerce(block, param_attrs_ty, param_attrs_uncoerced, param_attrs_src); const param_attrs_slice = try sema.resolveConstDefinedValue(block, param_attrs_src, param_attrs_coerced, .{ .simple = .fn_param_attrs }); const param_attrs_arr = try sema.derefSliceAsArray(block, param_attrs_src, param_attrs_slice, .{ .simple = .fn_param_attrs }); const ret_ty = try sema.resolveType(block, ret_ty_src, extra.ret_ty); - const fn_attrs_uncoerced = try sema.resolveInst(extra.fn_attrs); + const fn_attrs_uncoerced = sema.resolveInst(extra.fn_attrs); const fn_attrs_coerced = try sema.coerce(block, fn_attrs_ty, fn_attrs_uncoerced, fn_attrs_src); const fn_attrs_val = try sema.resolveConstDefinedValue(block, fn_attrs_src, fn_attrs_coerced, .{ .simple = .fn_attrs }); const fn_attrs = try sema.interpretBuiltinType(block, fn_attrs_src, fn_attrs_val, std.builtin.Type.Fn.Attributes); @@ -20587,17 +19989,15 @@ fn zirReifyFn( try param_attrs_arr.elemValue(pt, param_idx), std.builtin.Type.Fn.Param.Attributes, ); - try sema.checkParamTypeCommon( + try sema.checkParamType( block, @intCast(param_idx), param_ty, + false, param_attrs.@"noalias", param_types_src, fn_attrs.@"callconv", ); - if (try param_ty.comptimeOnlySema(pt)) { - return sema.fail(block, param_attrs_src, "cannot reify function type with comptime-only parameter type '{f}'", .{param_ty.fmt(pt)}); - } if (param_attrs.@"noalias") { if (param_idx > 31) { return sema.fail(block, param_attrs_src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}); @@ -20611,7 +20011,7 @@ fn zirReifyFn( try sema.checkCallConvSupportsVarArgs(block, fn_attrs_src, fn_attrs.@"callconv"); } - try sema.checkReturnTypeAndCallConvCommon( + try sema.checkReturnTypeAndCallConv( block, ret_ty, ret_ty_src, @@ -20621,9 +20021,6 @@ fn zirReifyFn( false, false, ); - if (try ret_ty.comptimeOnlySema(pt)) { - return sema.fail(block, param_attrs_src, "cannot reify function type with comptime-only return type '{f}'", .{ret_ty.fmt(pt)}); - } return .fromIntern(try ip.getFuncType(gpa, io, pt.tid, .{ .param_types = param_types_ip, @@ -20632,7 +20029,6 @@ fn zirReifyFn( .return_type = ret_ty.toIntern(), .cc = fn_attrs.@"callconv", .is_var_args = fn_attrs.varargs, - .is_generic = false, .is_noinline = false, })); } @@ -20653,6 +20049,7 @@ fn zirReifyStruct( const name_strategy: Zir.Inst.NameStrategy = @enumFromInt(extended.small); const extra = sema.code.extraData(Zir.Inst.ReifyStruct, extended.operand).data; const tracked_inst = try block.trackZir(inst); + const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = .nodeOffset(.zero), @@ -20697,16 +20094,16 @@ fn zirReifyStruct( const container_layout_ty = try sema.getBuiltinType(layout_src, .@"Type.ContainerLayout"); const single_field_attrs_ty = try sema.getBuiltinType(field_attrs_src, .@"Type.StructField.Attributes"); - const layout_uncoerced = try sema.resolveInst(extra.layout); + const layout_uncoerced = sema.resolveInst(extra.layout); const layout_coerced = try sema.coerce(block, container_layout_ty, layout_uncoerced, layout_src); const layout_val = try sema.resolveConstDefinedValue(block, layout_src, layout_coerced, .{ .simple = .struct_layout }); const layout = try sema.interpretBuiltinType(block, layout_src, layout_val, std.builtin.Type.ContainerLayout); - const backing_int_ty_uncoerced = try sema.resolveInst(extra.backing_ty); + const backing_int_ty_uncoerced = sema.resolveInst(extra.backing_ty); const backing_int_ty_coerced = try sema.coerce(block, .optional_type, backing_int_ty_uncoerced, backing_ty_src); - const backing_int_ty_val = try sema.resolveConstDefinedValue(block, backing_ty_src, backing_int_ty_coerced, .{ .simple = .type }); + const backing_int_ty_val = try sema.resolveConstDefinedValue(block, backing_ty_src, backing_int_ty_coerced, .{ .simple = .packed_struct_backing_int_type }); - const field_names_uncoerced = try sema.resolveInst(extra.field_names); + const field_names_uncoerced = sema.resolveInst(extra.field_names); const field_names_coerced = try sema.coerce(block, .slice_const_slice_const_u8, field_names_uncoerced, field_names_src); const field_names_slice = try sema.resolveConstDefinedValue(block, field_names_src, field_names_coerced, .{ .simple = .struct_field_names }); const field_names_arr = try sema.derefSliceAsArray(block, field_names_src, field_names_slice, .{ .simple = .struct_field_names }); @@ -20722,12 +20119,12 @@ fn zirReifyStruct( .child = single_field_attrs_ty.toIntern(), })); - const field_types_uncoerced = try sema.resolveInst(extra.field_types); + const field_types_uncoerced = sema.resolveInst(extra.field_types); const field_types_coerced = try sema.coerce(block, field_types_ty, field_types_uncoerced, field_types_src); const field_types_slice = try sema.resolveConstDefinedValue(block, field_types_src, field_types_coerced, .{ .simple = .struct_field_types }); const field_types_arr = try sema.derefSliceAsArray(block, field_types_src, field_types_slice, .{ .simple = .struct_field_types }); - const field_attrs_uncoerced = try sema.resolveInst(extra.field_attrs); + const field_attrs_uncoerced = sema.resolveInst(extra.field_attrs); const field_attrs_coerced = try sema.coerce(block, field_attrs_ty, field_attrs_uncoerced, field_attrs_src); const field_attrs_slice = try sema.resolveConstDefinedValue(block, field_attrs_src, field_attrs_coerced, .{ .simple = .struct_field_attrs }); const field_attrs_arr = try sema.derefSliceAsArray(block, field_attrs_src, field_attrs_slice, .{ .simple = .struct_field_attrs }); @@ -20744,19 +20141,30 @@ fn zirReifyStruct( return sema.failWithUseOfUndef(block, backing_ty_src, null); } - // The validation work here is non-trivial, and it's possible the type already exists. - // So in this first pass, let's just construct a hash to optimize for this case. If the - // inputs turn out to be invalid, we can cancel the WIP type later. + // Most validation of this type happens during type resolution. We basically need to do the work + // which AstGen would normally do. An exception is checking for duplicate field names, which is + // handled by type resolution---it just simplifies some logic a little. + + // As well as validation, we're going to gather some information about the fields, and construct + // a hash representing the inputs for deduplication purposes. var any_comptime_fields = false; - var any_default_inits = false; - var any_aligned_fields = false; + var any_field_defaults = false; + var any_field_aligns = false; - // For deduplication purposes, we must create a hash including all details of this type. // TODO: use a longer hash! var hasher = std.hash.Wyhash.init(0); std.hash.autoHash(&hasher, layout); std.hash.autoHash(&hasher, backing_int_ty_val); + + const backing_int_ty: ?Type = if (backing_int_ty_val.optionalValue(zcu)) |backing| ty: { + switch (layout) { + .auto, .@"extern" => return sema.fail(block, backing_ty_src, "non-packed struct does not support backing integer type", .{}), + .@"packed" => {}, + } + break :ty backing.toType(); + } else null; + // The field *type* array has already been deduplicated for us thanks to the InternPool! std.hash.autoHash(&hasher, field_types_arr); // However, for field names and attributes, we need to actually iterate the individual fields, @@ -20791,207 +20199,119 @@ fn zirReifyStruct( field_attrs_src, .{ .simple = .struct_field_default_value }, ); - // Resolve the value so that lazy values do not create distinct types. - break :d (try sema.resolveLazyValue(deref_val)).toIntern(); + if (deref_val.canMutateComptimeVarState(zcu)) { + return sema.failWithContainsReferenceToComptimeVar(block, field_attrs_src, field_name, "field default value", deref_val); + } + any_field_defaults = true; + break :d deref_val.toIntern(); }; + if (field_attr_comptime.toBool()) { + if (field_default == .none) { + return sema.fail(block, field_attrs_src, "comptime field without default initialization value", .{}); + } + if (layout != .auto) { + return sema.fail(block, field_attrs_src, "{t} struct fields cannot be marked comptime", .{layout}); + } + any_comptime_fields = true; + } + + if (field_attr_align.optionalValue(zcu)) |align_val| { + if (layout == .@"packed") { + return sema.fail(block, field_attrs_src, "packed struct fields cannot be aligned", .{}); + } + // Trigger a compile error if the alignment is invalid. + _ = try sema.validateAlign(block, field_attrs_src, align_val.toUnsignedInt(zcu)); + any_field_aligns = true; + } + std.hash.autoHash(&hasher, .{ field_name, field_attr_comptime, field_attr_align, field_default, }); - - if (field_attr_comptime.toBool()) any_comptime_fields = true; - if (field_attr_align.optionalValue(zcu)) |_| any_aligned_fields = true; - if (field_default != .none) any_default_inits = true; - } - - // Some basic validation to avoid a bogus `getStructType` call... - const backing_int_ty: ?Type = if (backing_int_ty_val.optionalValue(zcu)) |backing| ty: { - switch (layout) { - .auto, .@"extern" => return sema.fail(block, backing_ty_src, "non-packed struct does not support backing integer type", .{}), - .@"packed" => {}, - } - break :ty backing.toType(); - } else null; - if (any_aligned_fields and layout == .@"packed") { - return sema.fail(block, field_attrs_src, "packed struct fields cannot be aligned", .{}); - } - if (any_comptime_fields and layout != .auto) { - return sema.fail(block, field_attrs_src, "{t} struct fields cannot be marked comptime", .{layout}); } - const wip_ty = switch (try ip.getStructType(gpa, io, pt.tid, .{ - .layout = layout, + switch (try ip.getReifiedStructType(gpa, io, pt.tid, .{ + .zir_index = tracked_inst, + .type_hash = hasher.final(), .fields_len = @intCast(fields_len), - .known_non_opv = false, - .requires_comptime = .unknown, + .layout = layout, .any_comptime_fields = any_comptime_fields, - .any_default_inits = any_default_inits, - .any_aligned_fields = any_aligned_fields, - .inits_resolved = true, - .key = .{ .reified = .{ - .zir_index = tracked_inst, - .type_hash = hasher.final(), - } }, - }, false)) { - .wip => |wip| wip, + .any_field_defaults = any_field_defaults, + .any_field_aligns = any_field_aligns, + .packed_backing_int_type = if (backing_int_ty) |ty| ty.toIntern() else .none, + })) { .existing => |ty| { - try sema.declareDependency(.{ .interned = ty }); - try sema.addTypeReferenceEntry(src, ty); - return Air.internedToRef(ty); + try sema.addTypeReferenceEntry(src, .fromInterned(ty)); + // No need for `ensureNamespaceUpToDate` because this type's namespace is always empty. + return .fromIntern(ty); }, - }; - errdefer wip_ty.cancel(ip, pt.tid); - - const type_name = try sema.createTypeName( - block, - name_strategy, - "struct", - inst, - wip_ty.index, - ); - wip_ty.setName(ip, type_name.name, type_name.nav); - - const wip_struct_type = ip.loadStructType(wip_ty.index); - - for (0..fields_len) |field_idx| { - const field_name_val = try field_names_arr.elemValue(pt, field_idx); - const field_attrs_val = try field_attrs_arr.elemValue(pt, field_idx); - - const field_ty = (try field_types_arr.elemValue(pt, field_idx)).toType(); - - // Don't pass a reason; first loop acts as a check that this is valid. - const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, undefined); - if (wip_struct_type.addFieldName(ip, field_name)) |prev_index| { - _ = prev_index; // TODO: better source location - return sema.fail(block, field_names_src, "duplicate struct field name {f}", .{field_name.fmt(ip)}); - } - - const field_attr_comptime = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( - std.builtin.Type.StructField.Attributes, - "comptime", - ).?); - const field_attr_align = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( - std.builtin.Type.StructField.Attributes, - "align", - ).?); - const field_attr_default_value_ptr = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( - std.builtin.Type.StructField.Attributes, - "default_value_ptr", - ).?); - - if (field_attr_align.optionalValue(zcu)) |field_align_val| { - assert(layout != .@"packed"); - const bytes = try field_align_val.toUnsignedIntSema(pt); - const a = try sema.validateAlign(block, field_attrs_src, bytes); - wip_struct_type.field_aligns.get(ip)[field_idx] = a; - } else if (any_aligned_fields) { - assert(layout != .@"packed"); - wip_struct_type.field_aligns.get(ip)[field_idx] = .none; - } + .wip => |wip| { + errdefer wip.cancel(ip, pt.tid); + try sema.setTypeName(block, &wip, name_strategy, "struct", inst); + for (0..fields_len) |field_idx| { + const field_name_val = try field_names_arr.elemValue(pt, field_idx); + const field_attrs_val = try field_attrs_arr.elemValue(pt, field_idx); + + // No source location or reason; first loop checked this is valid. + const field_name = try sema.sliceToIpString(block, .unneeded, field_name_val, undefined); + wip.field_names.get(ip)[field_idx] = field_name; + + const field_ty = (try field_types_arr.elemValue(pt, field_idx)).toType(); + wip.field_types.get(ip)[field_idx] = field_ty.toIntern(); + + const field_attr_comptime = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( + std.builtin.Type.StructField.Attributes, + "comptime", + ).?); + const field_attr_align = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( + std.builtin.Type.StructField.Attributes, + "align", + ).?); + const field_attr_default_value_ptr = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( + std.builtin.Type.StructField.Attributes, + "default_value_ptr", + ).?); + + if (field_attr_comptime.toBool()) { + const bit_bag_index = field_idx / 32; + const mask = @as(u32, 1) << @intCast(field_idx % 32); + wip.field_is_comptime_bits.getAll(ip)[bit_bag_index] |= mask; + } - const field_default: InternPool.Index = d: { - const ptr_val = field_attr_default_value_ptr.optionalValue(zcu) orelse break :d .none; - assert(any_default_inits); - const ptr_ty = try pt.singleConstPtrType(field_ty); - // The first loop checked that this is comptime-dereferencable. - const deref_val = (try sema.pointerDeref(block, field_attrs_src, ptr_val, ptr_ty)).?; - // ...but we've not checked this yet! - if (deref_val.canMutateComptimeVarState(zcu)) { - return sema.failWithContainsReferenceToComptimeVar(block, field_attrs_src, field_name, "field default value", deref_val); - } - break :d (try sema.resolveLazyValue(deref_val)).toIntern(); - }; + if (field_attr_default_value_ptr.optionalValue(zcu)) |ptr_val| { + const ptr_ty = try pt.singleConstPtrType(field_ty); + // No source location; first loop checked this is valid. + const deref_val = (try sema.pointerDeref(block, .unneeded, ptr_val, ptr_ty)).?; + wip.field_values.get(ip)[field_idx] = deref_val.toIntern(); + } else if (any_field_defaults) { + wip.field_values.get(ip)[field_idx] = .none; + } - if (field_attr_comptime.toBool()) { - assert(layout == .auto); - if (field_default == .none) { - return sema.fail(block, field_attrs_src, "comptime field without default initialization value", .{}); + if (field_attr_align.optionalValue(zcu)) |field_align_val| { + const bytes = field_align_val.toUnsignedInt(zcu); + // No source location; first loop checked this is valid. + const a = try sema.validateAlign(block, .unneeded, bytes); + wip.field_aligns.get(ip)[field_idx] = a; + } else if (any_field_aligns) { + wip.field_aligns.get(ip)[field_idx] = .none; + } } - wip_struct_type.setFieldComptime(ip, field_idx); - } - - wip_struct_type.field_types.get(ip)[field_idx] = field_ty.toIntern(); - if (field_default != .none) { - wip_struct_type.field_inits.get(ip)[field_idx] = field_default; - } - - switch (field_ty.zigTypeTag(zcu)) { - .@"opaque" => return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(field_types_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); - errdefer msg.destroy(gpa); - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }), - .noreturn => return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(field_types_src, "struct fields cannot be 'noreturn'", .{}); - errdefer msg.destroy(gpa); - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }), - else => {}, - } - - switch (layout) { - .auto => {}, - .@"extern" => if (!try sema.validateExternType(field_ty, .struct_field)) { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(field_types_src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); - errdefer msg.destroy(gpa); - try sema.explainWhyTypeIsNotExtern(msg, field_types_src, field_ty, .struct_field); - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }); - }, - .@"packed" => if (!try sema.validatePackedType(field_ty)) { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(field_types_src, "packed structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); - errdefer msg.destroy(gpa); - try sema.explainWhyTypeIsNotPacked(msg, field_types_src, field_ty); - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }); - }, - } - } - - if (layout == .@"packed") { - var fields_bit_sum: u64 = 0; - for (0..wip_struct_type.field_types.len) |field_idx| { - const field_ty: Type = .fromInterned(wip_struct_type.field_types.get(ip)[field_idx]); - try field_ty.resolveLayout(pt); - fields_bit_sum += field_ty.bitSize(zcu); - } - if (backing_int_ty) |ty| { - try sema.checkBackingIntType(block, src, ty, fields_bit_sum); - wip_struct_type.setBackingIntType(ip, io, ty.toIntern()); - } else { - const ty = try pt.intType(.unsigned, @intCast(fields_bit_sum)); - wip_struct_type.setBackingIntType(ip, io, ty.toIntern()); - } - } - - const new_namespace_index = try pt.createNamespace(.{ - .parent = block.namespace.toOptional(), - .owner_type = wip_ty.index, - .file_scope = block.getFileScopeIndex(zcu), - .generation = zcu.generation, - }); - try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); - codegen_type: { - if (zcu.comp.config.use_llvm) break :codegen_type; - if (block.ownerModule().strip) break :codegen_type; - // This job depends on any resolve_type_fully jobs queued up before it. - zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); - try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); + const new_namespace_index = try pt.createNamespace(.{ + .parent = block.namespace.toOptional(), + .owner_type = wip.index, + .file_scope = block.getFileScopeIndex(zcu), + .generation = zcu.generation, + }); + errdefer pt.destroyNamespace(new_namespace_index); + if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); + try sema.addTypeReferenceEntry(src, .fromInterned(wip.index)); + return .fromIntern(wip.finish(ip, new_namespace_index)); + }, } - try sema.declareDependency(.{ .interned = wip_ty.index }); - try sema.addTypeReferenceEntry(src, wip_ty.index); - if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); - return .fromIntern(wip_ty.finish(ip, new_namespace_index)); } fn zirReifyUnion( @@ -21054,16 +20374,19 @@ fn zirReifyUnion( const container_layout_ty = try sema.getBuiltinType(layout_src, .@"Type.ContainerLayout"); const single_field_attrs_ty = try sema.getBuiltinType(field_attrs_src, .@"Type.UnionField.Attributes"); - const layout_uncoerced = try sema.resolveInst(extra.layout); + const layout_uncoerced = sema.resolveInst(extra.layout); const layout_coerced = try sema.coerce(block, container_layout_ty, layout_uncoerced, layout_src); const layout_val = try sema.resolveConstDefinedValue(block, layout_src, layout_coerced, .{ .simple = .union_layout }); const layout = try sema.interpretBuiltinType(block, layout_src, layout_val, std.builtin.Type.ContainerLayout); - const arg_ty_uncoerced = try sema.resolveInst(extra.arg_ty); + const arg_ty_uncoerced = sema.resolveInst(extra.arg_ty); const arg_ty_coerced = try sema.coerce(block, .optional_type, arg_ty_uncoerced, arg_ty_src); - const arg_ty_val = try sema.resolveConstDefinedValue(block, arg_ty_src, arg_ty_coerced, .{ .simple = .type }); + const arg_ty_val = try sema.resolveConstDefinedValue(block, arg_ty_src, arg_ty_coerced, switch (layout) { + .@"packed" => .{ .simple = .packed_union_backing_int_type }, + .auto, .@"extern" => .{ .simple = .union_enum_tag_type }, + }); - const field_names_uncoerced = try sema.resolveInst(extra.field_names); + const field_names_uncoerced = sema.resolveInst(extra.field_names); const field_names_coerced = try sema.coerce(block, .slice_const_slice_const_u8, field_names_uncoerced, field_names_src); const field_names_slice = try sema.resolveConstDefinedValue(block, field_names_src, field_names_coerced, .{ .simple = .union_field_names }); const field_names_arr = try sema.derefSliceAsArray(block, field_names_src, field_names_slice, .{ .simple = .union_field_names }); @@ -21079,12 +20402,12 @@ fn zirReifyUnion( .child = single_field_attrs_ty.toIntern(), })); - const field_types_uncoerced = try sema.resolveInst(extra.field_types); + const field_types_uncoerced = sema.resolveInst(extra.field_types); const field_types_coerced = try sema.coerce(block, field_types_ty, field_types_uncoerced, field_types_src); const field_types_slice = try sema.resolveConstDefinedValue(block, field_types_src, field_types_coerced, .{ .simple = .union_field_types }); const field_types_arr = try sema.derefSliceAsArray(block, field_types_src, field_types_slice, .{ .simple = .union_field_types }); - const field_attrs_uncoerced = try sema.resolveInst(extra.field_attrs); + const field_attrs_uncoerced = sema.resolveInst(extra.field_attrs); const field_attrs_coerced = try sema.coerce(block, field_attrs_ty, field_attrs_uncoerced, field_attrs_src); const field_attrs_slice = try sema.resolveConstDefinedValue(block, field_attrs_src, field_attrs_coerced, .{ .simple = .union_field_attrs }); const field_attrs_arr = try sema.derefSliceAsArray(block, field_attrs_src, field_attrs_slice, .{ .simple = .union_field_attrs }); @@ -21101,17 +20424,29 @@ fn zirReifyUnion( return sema.failWithUseOfUndef(block, arg_ty_src, null); } - // The validation work here is non-trivial, and it's possible the type already exists. - // So in this first pass, let's just construct a hash to optimize for this case. If the - // inputs turn out to be invalid, we can cancel the WIP type later. + // Most validation of this type happens during type resolution. We basically need to do the work + // which AstGen would normally do. An exception is checking for duplicate field names, which is + // handled by type resolution---it just simplifies some logic a little. + + // As well as validation, we're going to gather some information about the fields, and construct + // a hash representing the inputs for deduplication purposes. - var any_aligned_fields = false; + var any_field_aligns = false; - // For deduplication purposes, we must create a hash including all details of this type. // TODO: use a longer hash! var hasher = std.hash.Wyhash.init(0); std.hash.autoHash(&hasher, layout); std.hash.autoHash(&hasher, arg_ty_val); + + const explicit_tag_ty: ?Type, const explicit_packed_backing_type: ?Type = ty: { + const arg_ty = arg_ty_val.optionalValue(zcu) orelse break :ty .{ null, null }; + switch (layout) { + .@"extern" => return sema.fail(block, arg_ty_src, "extern union does not support enum tag type", .{}), + .@"packed" => break :ty .{ null, arg_ty.toType() }, + .auto => break :ty .{ arg_ty.toType(), null }, + } + }; + // `field_types_arr` and `field_attrs_arr` are already deduplicated by the InternPool! std.hash.autoHash(&hasher, field_types_arr); std.hash.autoHash(&hasher, field_attrs_arr); @@ -21128,203 +20463,76 @@ fn zirReifyUnion( try field_attrs_arr.elemValue(pt, field_idx), std.builtin.Type.UnionField.Attributes, ); - if (field_attrs.@"align" != null) { - any_aligned_fields = true; - } - } - - // Some basic validation to avoid a bogus `getUnionType` call... - const explicit_tag_ty: ?Type = if (arg_ty_val.optionalValue(zcu)) |arg_ty| ty: { - switch (layout) { - .@"extern", .@"packed" => return sema.fail(block, arg_ty_src, "{t} union does not support enum tag type", .{layout}), - .auto => {}, + if (field_attrs.@"align") |bytes| { + if (layout == .@"packed") { + return sema.fail(block, field_attrs_src, "packed union fields cannot be aligned", .{}); + } + // Trigger a compile error if the alignment is invalid. + _ = try sema.validateAlign(block, field_attrs_src, bytes); + any_field_aligns = true; } - break :ty arg_ty.toType(); - } else null; - if (any_aligned_fields and layout == .@"packed") { - return sema.fail(block, field_attrs_src, "packed union fields cannot be aligned", .{}); } - const wip_ty = switch (try ip.getUnionType(gpa, io, pt.tid, .{ - .flags = .{ - .layout = layout, - .status = .none, - .runtime_tag = rt: { - if (explicit_tag_ty != null) break :rt .tagged; - if (layout == .auto and block.wantSafeTypes()) break :rt .safety; - break :rt .none; - }, - .any_aligned_fields = any_aligned_fields, - .requires_comptime = .unknown, - .assumed_runtime_bits = false, - .assumed_pointer_aligned = false, - .alignment = .none, - }, + switch (try ip.getReifiedUnionType(gpa, io, pt.tid, .{ + .zir_index = tracked_inst, + .type_hash = hasher.final(), .fields_len = @intCast(fields_len), - .enum_tag_ty = .none, // set later because not yet validated - .field_types = &.{}, // set later - .field_aligns = &.{}, // set later - .key = .{ .reified = .{ - .zir_index = tracked_inst, - .type_hash = hasher.final(), - } }, - }, false)) { - .wip => |wip| wip, + .layout = layout, + .any_field_aligns = any_field_aligns, + .tag_usage = tag: { + if (explicit_tag_ty != null) break :tag .tagged; + if (layout == .auto and block.wantSafeTypes()) break :tag .safety; + break :tag .none; + }, + .enum_tag_type = if (explicit_tag_ty) |ty| ty.toIntern() else .none, + .packed_backing_int_type = if (explicit_packed_backing_type) |ty| ty.toIntern() else .none, + })) { .existing => |ty| { - try sema.declareDependency(.{ .interned = ty }); - try sema.addTypeReferenceEntry(src, ty); - return Air.internedToRef(ty); + try sema.addTypeReferenceEntry(src, .fromInterned(ty)); + // No need for `ensureNamespaceUpToDate` because this type's namespace is always empty. + return .fromIntern(ty); }, - }; - errdefer wip_ty.cancel(ip, pt.tid); - - const type_name = try sema.createTypeName( - block, - name_strategy, - "union", - inst, - wip_ty.index, - ); - wip_ty.setName(ip, type_name.name, type_name.nav); - - const loaded_union = ip.loadUnionType(wip_ty.index); - - const enum_tag_ty, const has_explicit_tag = if (explicit_tag_ty) |enum_tag_ty| tag: { - if (enum_tag_ty.zigTypeTag(zcu) != .@"enum") { - return sema.fail(block, arg_ty_src, "tag type must be an enum type", .{}); - } + .wip => |wip| { + errdefer wip.cancel(ip, pt.tid); + try sema.setTypeName(block, &wip, name_strategy, "union", inst); - const tag_ty_fields_len = enum_tag_ty.enumFieldCount(zcu); + for (0..fields_len) |field_idx| { + const field_name_val = try field_names_arr.elemValue(pt, field_idx); + // No source location or reason; first loop checked this is valid. + const field_name = try sema.sliceToIpString(block, .unneeded, field_name_val, undefined); + wip.field_names.get(ip)[field_idx] = field_name; - for (0..fields_len) |field_idx| { - const field_name_val = try field_names_arr.elemValue(pt, field_idx); - // Don't pass a reason; first loop acts as a check that this is valid. - const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, undefined); + const field_ty = (try field_types_arr.elemValue(pt, field_idx)).toType(); + wip.field_types.get(ip)[field_idx] = field_ty.toIntern(); - if (field_idx >= tag_ty_fields_len) { - return sema.fail(block, field_names_src, "no field named '{f}' in enum '{f}'", .{ - field_name.fmt(ip), enum_tag_ty.fmt(pt), - }); + // No source location; first loop checked this is valid. + const field_attrs = try sema.interpretBuiltinType( + block, + .unneeded, + try field_attrs_arr.elemValue(pt, field_idx), + std.builtin.Type.UnionField.Attributes, + ); + if (field_attrs.@"align") |bytes| { + // No source location; first loop checked this is valid. + const a = try sema.validateAlign(block, .unneeded, bytes); + wip.field_aligns.get(ip)[field_idx] = a; + } else if (any_field_aligns) { + wip.field_aligns.get(ip)[field_idx] = .none; + } } - const enum_field_name = enum_tag_ty.enumFieldName(field_idx, zcu); - if (enum_field_name != field_name) { - return sema.fail(block, field_names_src, "union field name '{f}' does not match enum field name '{f}'", .{ - field_name.fmt(ip), enum_field_name.fmt(ip), - }); - } - } - if (tag_ty_fields_len > fields_len) return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(field_names_src, "{d} enum fields missing in union", .{ - tag_ty_fields_len - fields_len, + const new_namespace_index = try pt.createNamespace(.{ + .parent = block.namespace.toOptional(), + .owner_type = wip.index, + .file_scope = block.getFileScopeIndex(zcu), + .generation = zcu.generation, }); - errdefer msg.destroy(gpa); - for (fields_len..tag_ty_fields_len) |enum_field_idx| { - try sema.addFieldErrNote(enum_tag_ty, enum_field_idx, msg, "field '{f}' missing, declared here", .{ - enum_tag_ty.enumFieldName(enum_field_idx, zcu).fmt(ip), - }); - } - try sema.addDeclaredHereNote(msg, enum_tag_ty); - break :msg msg; - }); - break :tag .{ enum_tag_ty.toIntern(), true }; - } else tag: { - // We must track field names and set up the tag type ourselves. - var field_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty; - try field_names.ensureTotalCapacity(sema.arena, fields_len); - - for (0..fields_len) |field_idx| { - const field_name_val = try field_names_arr.elemValue(pt, field_idx); - // Don't pass a reason; first loop acts as a check that this is valid. - const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, undefined); - const gop = field_names.getOrPutAssumeCapacity(field_name); - if (gop.found_existing) { - // TODO: better source location - return sema.fail(block, field_names_src, "duplicate union field {f}", .{field_name.fmt(ip)}); - } - } - const enum_tag_ty = try sema.generateUnionTagTypeSimple(block, field_names.keys(), wip_ty.index, type_name.name); - break :tag .{ enum_tag_ty, false }; - }; - errdefer if (!has_explicit_tag) ip.remove(pt.tid, enum_tag_ty); // remove generated tag type on error - - for (0..fields_len) |field_idx| { - const field_ty = (try field_types_arr.elemValue(pt, field_idx)).toType(); - const field_attrs = try sema.interpretBuiltinType( - block, - field_attrs_src, - try field_attrs_arr.elemValue(pt, field_idx), - std.builtin.Type.UnionField.Attributes, - ); - - if (field_ty.zigTypeTag(zcu) == .@"opaque") { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(field_types_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); - errdefer msg.destroy(gpa); - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }); - } - - switch (layout) { - .auto => {}, - .@"extern" => if (!try sema.validateExternType(field_ty, .union_field)) { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(field_types_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); - errdefer msg.destroy(gpa); - - try sema.explainWhyTypeIsNotExtern(msg, field_types_src, field_ty, .union_field); - - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }); - }, - .@"packed" => if (!try sema.validatePackedType(field_ty)) { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(field_types_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); - errdefer msg.destroy(gpa); - - try sema.explainWhyTypeIsNotPacked(msg, field_types_src, field_ty); - - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }); - }, - } - - loaded_union.field_types.get(ip)[field_idx] = field_ty.toIntern(); - if (field_attrs.@"align") |bytes| { - assert(layout != .@"packed"); - const a = try sema.validateAlign(block, field_attrs_src, bytes); - loaded_union.field_aligns.get(ip)[field_idx] = a; - } else if (any_aligned_fields) { - assert(layout != .@"packed"); - loaded_union.field_aligns.get(ip)[field_idx] = .none; - } - } - - loaded_union.setTagType(ip, io, enum_tag_ty); - loaded_union.setStatus(ip, io, .have_field_types); - - const new_namespace_index = try pt.createNamespace(.{ - .parent = block.namespace.toOptional(), - .owner_type = wip_ty.index, - .file_scope = block.getFileScopeIndex(zcu), - .generation = zcu.generation, - }); - - try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); - codegen_type: { - if (zcu.comp.config.use_llvm) break :codegen_type; - if (block.ownerModule().strip) break :codegen_type; - // This job depends on any resolve_type_fully jobs queued up before it. - zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); - try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); + errdefer pt.destroyNamespace(new_namespace_index); + if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); + try sema.addTypeReferenceEntry(src, .fromInterned(wip.index)); + return .fromIntern(wip.finish(ip, new_namespace_index)); + }, } - try sema.declareDependency(.{ .interned = wip_ty.index }); - try sema.addTypeReferenceEntry(src, wip_ty.index); - if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); - return .fromIntern(wip_ty.finish(ip, new_namespace_index)); } fn zirReifyEnum( @@ -21379,12 +20587,12 @@ fn zirReifyEnum( const enum_mode_ty = try sema.getBuiltinType(mode_src, .@"Type.Enum.Mode"); - const tag_ty = try sema.resolveType(block, tag_ty_src, extra.tag_ty); - if (tag_ty.zigTypeTag(zcu) != .int) { - return sema.fail(block, tag_ty_src, "tag type must be an integer type", .{}); - } + const tag_ty_uncoerced = sema.resolveInst(extra.tag_ty); + const tag_ty_coerced = try sema.coerce(block, .type, tag_ty_uncoerced, tag_ty_src); + const tag_ty_val = try sema.resolveConstDefinedValue(block, tag_ty_src, tag_ty_coerced, .{ .simple = .enum_int_tag_type }); + const tag_ty = tag_ty_val.toType(); - const mode_uncoerced = try sema.resolveInst(extra.mode); + const mode_uncoerced = sema.resolveInst(extra.mode); const mode_coerced = try sema.coerce(block, enum_mode_ty, mode_uncoerced, mode_src); const mode_val = try sema.resolveConstDefinedValue(block, mode_src, mode_coerced, .{ .simple = .type }); const nonexhaustive = switch (try sema.interpretBuiltinType(block, mode_src, mode_val, std.builtin.Type.Enum.Mode)) { @@ -21392,7 +20600,7 @@ fn zirReifyEnum( .nonexhaustive => true, }; - const field_names_uncoerced = try sema.resolveInst(extra.field_names); + const field_names_uncoerced = sema.resolveInst(extra.field_names); const field_names_coerced = try sema.coerce(block, .slice_const_slice_const_u8, field_names_uncoerced, field_names_src); const field_names_slice = try sema.resolveConstDefinedValue(block, field_names_src, field_names_coerced, .{ .simple = .enum_field_names }); const field_names_arr = try sema.derefSliceAsArray(block, field_names_src, field_names_slice, .{ .simple = .enum_field_names }); @@ -21404,7 +20612,7 @@ fn zirReifyEnum( .child = tag_ty.toIntern(), })); - const field_values_uncoerced = try sema.resolveInst(extra.field_values); + const field_values_uncoerced = sema.resolveInst(extra.field_values); const field_values_coerced = try sema.coerce(block, field_values_ty, field_values_uncoerced, field_values_src); const field_values_slice = try sema.resolveConstDefinedValue(block, field_values_src, field_values_coerced, .{ .simple = .enum_field_values }); const field_values_arr = try sema.derefSliceAsArray(block, field_values_src, field_values_slice, .{ .simple = .enum_field_values }); @@ -21415,11 +20623,13 @@ fn zirReifyEnum( } // We don't need to check `field_names_arr`, because `sliceToIpString` will check that for us. - // The validation work here is non-trivial, and it's possible the type already exists. - // So in this first pass, let's just construct a hash to optimize for this case. If the - // inputs turn out to be invalid, we can cancel the WIP type later. + // Most validation of this type happens during type resolution. We basically need to do the work + // which AstGen would normally do. An exception is checking for duplicate field names, which is + // handled by type resolution---it just simplifies some logic a little. + + // As well as validation, we're going to gather some information about the fields, and construct + // a hash representing the inputs for deduplication purposes. - // For deduplication purposes, we must create a hash including all details of this type. // TODO: use a longer hash! var hasher = std.hash.Wyhash.init(0); std.hash.autoHash(&hasher, tag_ty.toIntern()); @@ -21435,87 +20645,46 @@ fn zirReifyEnum( std.hash.autoHash(&hasher, field_name); } - const wip_ty = switch (try ip.getEnumType(gpa, io, pt.tid, .{ - .has_values = true, - .tag_mode = if (nonexhaustive) .nonexhaustive else .explicit, + switch (try ip.getReifiedEnumType(gpa, io, pt.tid, .{ + .zir_index = tracked_inst, + .type_hash = hasher.final(), .fields_len = @intCast(fields_len), - .key = .{ .reified = .{ - .zir_index = tracked_inst, - .type_hash = hasher.final(), - } }, - }, false)) { - .wip => |wip| wip, + .nonexhaustive = nonexhaustive, + .int_tag_type = tag_ty.toIntern(), + })) { .existing => |ty| { - try sema.declareDependency(.{ .interned = ty }); - try sema.addTypeReferenceEntry(src, ty); + try sema.addTypeReferenceEntry(src, .fromInterned(ty)); + // No need for `ensureNamespaceUpToDate` because this type's namespace is always empty. return .fromIntern(ty); }, - }; - var done = false; - errdefer if (!done) wip_ty.cancel(ip, pt.tid); - - const type_name = try sema.createTypeName( - block, - name_strategy, - "enum", - inst, - wip_ty.index, - ); - wip_ty.setName(ip, type_name.name, type_name.nav); - - const new_namespace_index = try pt.createNamespace(.{ - .parent = block.namespace.toOptional(), - .owner_type = wip_ty.index, - .file_scope = block.getFileScopeIndex(zcu), - .generation = zcu.generation, - }); + .wip => |wip| { + errdefer wip.cancel(ip, pt.tid); - try sema.declareDependency(.{ .interned = wip_ty.index }); - try sema.addTypeReferenceEntry(src, wip_ty.index); - if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); - wip_ty.prepare(ip, new_namespace_index); - wip_ty.setTagTy(ip, tag_ty.toIntern()); - done = true; + try sema.setTypeName(block, &wip, name_strategy, "enum", inst); - for (0..fields_len) |field_idx| { - const field_name_val = try field_names_arr.elemValue(pt, field_idx); - // Don't pass a reason; first loop acts as a check that this is valid. - const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, undefined); + // Populate field names and values. Duplicate checking will be handled by type resolution. + for (0..fields_len) |field_index| { + const field_name_val = try field_names_arr.elemValue(pt, field_index); + // No source location or reason; first loop checked this is valid. + const field_name = try sema.sliceToIpString(block, .unneeded, field_name_val, undefined); + wip.field_names.get(ip)[field_index] = field_name; - const field_val = try field_values_arr.elemValue(pt, field_idx); + const field_val = try field_values_arr.elemValue(pt, field_index); + wip.field_values.get(ip)[field_index] = field_val.toIntern(); + } - if (wip_ty.nextField(ip, field_name, field_val.toIntern())) |conflict| { - return sema.failWithOwnedErrorMsg(block, switch (conflict.kind) { - .name => msg: { - const msg = try sema.errMsg(field_names_src, "duplicate enum field '{f}'", .{field_name.fmt(ip)}); - errdefer msg.destroy(gpa); - _ = conflict.prev_field_idx; // TODO: this note is incorrect - try sema.errNote(field_names_src, msg, "other field here", .{}); - break :msg msg; - }, - .value => msg: { - const msg = try sema.errMsg(field_values_src, "enum tag value {f} already taken", .{field_val.fmtValueSema(pt, sema)}); - errdefer msg.destroy(gpa); - _ = conflict.prev_field_idx; // TODO: this note is incorrect - try sema.errNote(field_values_src, msg, "other enum tag value here", .{}); - break :msg msg; - }, + const new_namespace_index = try pt.createNamespace(.{ + .parent = block.namespace.toOptional(), + .owner_type = wip.index, + .file_scope = block.getFileScopeIndex(zcu), + .generation = zcu.generation, }); - } - } - - if (nonexhaustive and fields_len > 1 and std.math.log2_int(u64, fields_len) == tag_ty.bitSize(zcu)) { - return sema.fail(block, src, "non-exhaustive enum specified every value", .{}); - } - - codegen_type: { - if (zcu.comp.config.use_llvm) break :codegen_type; - if (block.ownerModule().strip) break :codegen_type; - // This job depends on any resolve_type_fully jobs queued up before it. - zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); - try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); + errdefer pt.destroyNamespace(new_namespace_index); + if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); + try sema.addTypeReferenceEntry(src, .fromInterned(wip.index)); + return .fromIntern(wip.finish(ip, new_namespace_index)); + }, } - return Air.internedToRef(wip_ty.index); } fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref { @@ -21523,7 +20692,7 @@ fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.In const va_list_ty = try sema.getBuiltinType(src, .VaList); const va_list_ptr = try pt.singleMutPtrType(va_list_ty); - const inst = try sema.resolveInst(zir_ref); + const inst = sema.resolveInst(zir_ref); return sema.coerce(block, va_list_ptr, inst, src); } @@ -21535,8 +20704,8 @@ fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs); const arg_ty = try sema.resolveType(block, ty_src, extra.rhs); - - if (!try sema.validateExternType(arg_ty, .param_ty)) { + try sema.ensureLayoutResolved(arg_ty, ty_src, .parameter); + if (!arg_ty.validateExtern(.param_ty, sema.pt.zcu)) { const msg = msg: { const msg = try sema.errMsg(ty_src, "cannot get '{f}' from variadic argument", .{arg_ty.fmt(sema.pt)}); errdefer msg.destroy(sema.gpa); @@ -21573,7 +20742,8 @@ fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand); try sema.requireRuntimeBlock(block, src, null); - return block.addUnOp(.c_va_end, va_list_ref); + _ = try block.addUnOp(.c_va_end, va_list_ref); + return .void_value; } fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { @@ -21618,7 +20788,7 @@ fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intFromFloat"); - const operand = try sema.resolveInst(extra.rhs); + const operand = sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); @@ -21630,7 +20800,7 @@ fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro _ = try sema.checkIntType(block, src, dest_scalar_ty); try sema.checkFloatType(block, operand_src, operand_scalar_ty); - if (try sema.resolveValue(operand)) |operand_val| { + if (sema.resolveValue(operand)) |operand_val| { const result_val = try sema.intFromFloat(block, operand_src, operand_val, operand_ty, dest_ty, .truncate); return Air.internedToRef(result_val.toIntern()); } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) { @@ -21671,7 +20841,7 @@ fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatFromInt"); - const operand = try sema.resolveInst(extra.rhs); + const operand = sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); @@ -21682,9 +20852,21 @@ fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro try sema.checkFloatType(block, src, dest_scalar_ty); _ = try sema.checkIntType(block, operand_src, operand_scalar_ty); - if (try sema.resolveValue(operand)) |operand_val| { - const result_val = try operand_val.floatFromIntAdvanced(sema.arena, operand_ty, dest_ty, pt, .sema); - return Air.internedToRef(result_val.toIntern()); + if (sema.resolveValue(operand)) |operand_val| { + if (operand_val.isUndef(zcu)) return .fromValue(try pt.undefValue(dest_ty)); + if (dest_ty.zigTypeTag(zcu) != .vector) { + return .fromValue(try pt.floatValue(dest_ty, operand_val.toFloat(f128, zcu))); + } + const dest_elems = try sema.arena.alloc(InternPool.Index, dest_ty.vectorLen(zcu)); + for (dest_elems, 0..) |*out_elem, elem_idx| { + const orig_elem = try operand_val.elemValue(pt, elem_idx); + const casted_elem = if (orig_elem.isUndef(zcu)) + try pt.undefValue(dest_scalar_ty) + else + try pt.floatValue(dest_scalar_ty, orig_elem.toFloat(f128, zcu)); + out_elem.* = casted_elem.toIntern(); + } + return .fromValue(try pt.aggregateValue(dest_ty, dest_elems)); } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_float) { return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_float }); } @@ -21702,7 +20884,7 @@ fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); - const operand_res = try sema.resolveInst(extra.rhs); + const operand_res = sema.resolveInst(extra.rhs); const uncoerced_operand_ty = sema.typeOf(operand_res); const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrFromInt"); @@ -21719,8 +20901,10 @@ fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const ptr_ty = dest_ty.scalarType(zcu); try sema.checkPtrType(block, src, ptr_ty, true); - const elem_ty = ptr_ty.elemType2(zcu); - const ptr_align = try ptr_ty.ptrAlignmentSema(pt); + const elem_ty = ptr_ty.nullablePtrElem(zcu); + + try sema.ensureLayoutResolved(elem_ty, src, .align_check); + const ptr_align = ptr_ty.ptrAlignment(zcu); if (ptr_ty.isSlice(zcu)) { const msg = msg: { @@ -21746,18 +20930,9 @@ fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! } return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern()); } - if (try ptr_ty.comptimeOnlySema(pt)) { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "pointer to comptime-only type '{f}' must be comptime-known, but operand is runtime-known", .{ptr_ty.fmt(pt)}); - errdefer msg.destroy(sema.gpa); - - try sema.explainWhyTypeIsComptime(msg, src, ptr_ty); - break :msg msg; - }); - } try sema.requireRuntimeBlock(block, src, operand_src); try sema.checkLogicalPtrOperation(block, src, ptr_ty); - if (block.wantSafety() and (try elem_ty.hasRuntimeBitsSema(pt) or elem_ty.zigTypeTag(zcu) == .@"fn")) { + if (block.wantSafety()) { if (!ptr_ty.isAllowzeroPtr(zcu)) { const is_non_zero = if (is_vector) all_non_zero: { const zero_usize = Air.internedToRef((try sema.splat(operand_ty, .zero_usize)).toIntern()); @@ -21804,7 +20979,7 @@ fn ptrFromIntVal( } return sema.failWithUseOfUndef(block, operand_src, vec_idx); } - const addr = try operand_val.toUnsignedIntSema(pt); + const addr = operand_val.toUnsignedInt(zcu); if (!ptr_ty.isAllowzeroPtr(zcu) and addr == 0) return sema.fail(block, operand_src, "pointer type '{f}' does not allow address zero", .{ptr_ty.fmt(pt)}); if (addr != 0 and ptr_align != .none) { @@ -21836,7 +21011,7 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData const src = block.nodeOffset(extra.node); const operand_src = block.builtinCallArgSrc(extra.node, 0); const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_opt, "@errorCast"); - const operand = try sema.resolveInst(extra.rhs); + const operand = sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); const dest_tag = dest_ty.zigTypeTag(zcu); @@ -21877,34 +21052,62 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData else => unreachable, }; - const disjoint = disjoint: { - // Try avoiding resolving inferred error sets if we can - if (!dest_err_ty.isAnyError(zcu) and dest_err_ty.errorSetIsEmpty(zcu)) break :disjoint true; - if (!operand_err_ty.isAnyError(zcu) and operand_err_ty.errorSetIsEmpty(zcu)) break :disjoint true; - if (dest_err_ty.isAnyError(zcu)) break :disjoint false; - if (operand_err_ty.isAnyError(zcu)) break :disjoint false; - const dest_err_names = dest_err_ty.errorSetNames(zcu); - for (0..dest_err_names.len) |dest_err_index| { - if (Type.errorSetHasFieldIp(ip, operand_err_ty.toIntern(), dest_err_names.get(ip)[dest_err_index])) - break :disjoint false; - } - - if (!ip.isInferredErrorSetType(dest_err_ty.toIntern()) and - !ip.isInferredErrorSetType(operand_err_ty.toIntern())) - { - break :disjoint true; - } - - _ = try sema.resolveInferredErrorSetTy(block, src, dest_err_ty.toIntern()); - _ = try sema.resolveInferredErrorSetTy(block, operand_src, operand_err_ty.toIntern()); - for (0..dest_err_names.len) |dest_err_index| { - if (Type.errorSetHasFieldIp(ip, operand_err_ty.toIntern(), dest_err_names.get(ip)[dest_err_index])) - break :disjoint false; - } + switch (ip.indexToKey(operand_err_ty.toIntern())) { + .inferred_error_set_type => |func| try sema.ensureFuncIesResolved(block, src, func), + else => {}, + } - break :disjoint true; + const result: enum { + /// The operand and destination error sets are disjoint, i.e. have no errors in common. + disjoint, + /// The destination error set is a superset of the operand error set, so the operation is + /// effectively equivalent to a coercion. + superset, + /// The operand and destination error sets have *some* errors in common, but the destination + /// is not a superset of the operand, so a safety check may be needed. + overlap, + } = if (operand_err_ty.errorSetIsEmpty(zcu)) res: { + break :res .disjoint; + } else check: switch (dest_err_ty.toIntern()) { + .anyerror_type => .superset, + .adhoc_inferred_error_set_type => { + // `@errorCast` to this function's own error set. + try sema.fn_ret_ty_ies.?.addErrorSet(operand_err_ty, ip, sema.arena); + break :check .superset; + }, + else => |err_set_ty| switch (ip.indexToKey(err_set_ty)) { + .inferred_error_set_type => |func_index| { + if (sema.fn_ret_ty_ies) |dst_ies| { + if (dst_ies.func == func_index) { + // `@errorCast` to this function's own error set. + try sema.fn_ret_ty_ies.?.addErrorSet(operand_err_ty, ip, sema.arena); + break :check .superset; + } + } + try sema.ensureFuncIesResolved(block, src, func_index); + continue :check ip.funcIesResolvedUnordered(func_index); + }, + .error_set_type => |dest| { + if (dest.names.len == 0) break :check .disjoint; // dest is 'error{}' + if (operand_err_ty.isAnyError(zcu)) break :check .overlap; // anyerror -> error{...} (non-empty) + var dest_has_all = true; + var dest_has_any = false; + for (operand_err_ty.errorSetNames(zcu).get(ip)) |operand_err_name| { + if (dest.nameIndex(ip, operand_err_name) != null) { + dest_has_any = true; + } else { + dest_has_all = false; + } + } + if (!dest_has_any) break :check .disjoint; + if (dest_has_all) break :check .superset; + break :check .overlap; + }, + else => unreachable, + }, }; - if (disjoint and !(operand_tag == .error_union and dest_tag == .error_union)) { + + if (result == .disjoint and !(operand_tag == .error_union and dest_tag == .error_union)) { return sema.fail(block, src, "error sets '{f}' and '{f}' have no common errors", .{ operand_err_ty.fmt(pt), dest_err_ty.fmt(pt), }); @@ -21912,25 +21115,30 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData // operand must be defined since it can be an invalid error value if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| { - const err_name: InternPool.NullTerminatedString = switch (operand_tag) { - .error_set => ip.indexToKey(operand_val.toIntern()).err.name, - .error_union => switch (ip.indexToKey(operand_val.toIntern()).error_union.val) { + const err_name: InternPool.NullTerminatedString = switch (ip.indexToKey(operand_val.toIntern())) { + .err => |err| err.name, + .error_union => |eu| switch (eu.val) { .err_name => |name| name, .payload => |payload_val| { assert(dest_tag == .error_union); // should be guaranteed from the type checks above - return sema.coerce(block, dest_ty, Air.internedToRef(payload_val), operand_src); + const dest_payload_ty = dest_ty.errorUnionPayload(zcu); + const coerced_payload = try sema.coerce(block, dest_payload_ty, .fromIntern(payload_val), operand_src); + return sema.wrapErrorUnionPayload(block, dest_ty, coerced_payload, operand_src) catch |err| switch (err) { + error.NotCoercible => unreachable, + else => |e| return e, + }; }, }, else => unreachable, }; - if (!dest_err_ty.isAnyError(zcu) and !Type.errorSetHasFieldIp(ip, dest_err_ty.toIntern(), err_name)) { + if (result != .superset and !dest_err_ty.errorSetHasField(err_name, zcu)) { return sema.fail(block, src, "'error.{f}' not a member of error set '{f}'", .{ err_name.fmt(ip), dest_err_ty.fmt(pt), }); } - return Air.internedToRef(try pt.intern(switch (dest_tag) { + return .fromIntern(try pt.intern(switch (dest_tag) { .error_set => .{ .err = .{ .ty = dest_ty.toIntern(), .name = err_name, @@ -21944,21 +21152,17 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData } const err_int_ty = try pt.errorIntType(); - if (block.wantSafety() and !dest_err_ty.isAnyError(zcu) and - dest_err_ty.toIntern() != .adhoc_inferred_error_set_type and - zcu.backendSupportsFeature(.error_set_has_value)) - { + if (block.wantSafety() and result != .superset and zcu.backendSupportsFeature(.error_set_has_value)) { const err_code_inst = switch (operand_tag) { .error_set => operand, .error_union => try block.addTyOp(.unwrap_errunion_err, operand_err_ty, operand), else => unreachable, }; const err_int_inst = try block.addBitCast(err_int_ty, err_code_inst); - if (dest_tag == .error_union) { const zero_err = try pt.intRef(err_int_ty, 0); const is_zero = try block.addBinOp(.cmp_eq, err_int_inst, zero_err); - if (disjoint) { + if (result == .disjoint) { // Error must be zero. try sema.addSafetyCheck(block, src, is_zero, .invalid_error_code); } else { @@ -21987,7 +21191,7 @@ fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDa const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; const src = block.nodeOffset(extra.node); const operand_src = block.src(.{ .node_offset_ptrcast_operand = extra.node }); - const operand = try sema.resolveInst(extra.rhs); + const operand = sema.resolveInst(extra.rhs); const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, flags.needResultTypeBuiltinName()); return sema.ptrCastFull( block, @@ -22006,7 +21210,7 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrCast"); - const operand = try sema.resolveInst(extra.rhs); + const operand = sema.resolveInst(extra.rhs); return sema.ptrCastFull( block, @@ -22043,8 +21247,8 @@ fn ptrCastFull( const src_info = operand_ty.ptrInfo(zcu); const dest_info = dest_ty.ptrInfo(zcu); - try Type.fromInterned(src_info.child).resolveLayout(pt); - try Type.fromInterned(dest_info.child).resolveLayout(pt); + try sema.ensureLayoutResolved(.fromInterned(src_info.child), operand_src, .align_check); + try sema.ensureLayoutResolved(.fromInterned(dest_info.child), src, .align_check); const DestSliceLen = union(enum) { undef, @@ -22072,16 +21276,16 @@ fn ptrCastFull( }; }, .slice => src: { - const operand_val = try sema.resolveValue(operand) orelse break :src .{ .fromInterned(src_info.child), null }; + const operand_val = sema.resolveValue(operand) orelse break :src .{ .fromInterned(src_info.child), null }; if (operand_val.isUndef(zcu)) break :len .undef; const slice_val = switch (operand_ty.zigTypeTag(zcu)) { .optional => operand_val.optionalValue(zcu) orelse break :len .undef, .pointer => operand_val, else => unreachable, }; - const slice_len_resolved = try sema.resolveLazyValue(.fromInterned(zcu.intern_pool.sliceLen(slice_val.toIntern()))); - if (slice_len_resolved.isUndef(zcu)) break :len .undef; - break :src .{ .fromInterned(src_info.child), slice_len_resolved.toUnsignedInt(zcu) }; + const slice_len: Value = .fromInterned(zcu.intern_pool.sliceLen(slice_val.toIntern())); + if (slice_len.isUndef(zcu)) break :len .undef; + break :src .{ .fromInterned(src_info.child), slice_len.toUnsignedInt(zcu) }; }, .many, .c => { return sema.fail(block, src, "cannot infer length of slice from {s}", .{pointerSizeString(src_info.flags.size)}); @@ -22369,7 +21573,7 @@ fn ptrCastFull( ct: { if (flags.addrspace_cast) break :ct; // cannot `@addrSpaceCast` at comptime - const operand_val = try sema.resolveValue(operand) orelse break :ct; + const operand_val = sema.resolveValue(operand) orelse break :ct; if (operand_val.isUndef(zcu)) { if (!dest_ty.ptrAllowsZero(zcu)) { @@ -22395,7 +21599,7 @@ fn ptrCastFull( }; if (dest_align.compare(.gt, src_align)) { - if (try ptr_val.getUnsignedIntSema(pt)) |addr| { + if (ptr_val.getUnsignedInt(zcu)) |addr| { const masked_addr = if (Type.fromInterned(dest_info.child).fnPtrMaskOrNull(zcu)) |mask| addr & mask else @@ -22464,7 +21668,7 @@ fn ptrCastFull( // Now, do an addrspace cast if necessary! if (!flags.addrspace_cast) break :ptr pre_addrspace_cast; - const intermediate_ptr_ty = try pt.ptrTypeSema(info: { + const intermediate_ptr_ty = try pt.ptrType(info: { var info = src_info; info.flags.address_space = dest_info.flags.address_space; break :info info; @@ -22629,7 +21833,7 @@ fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src = block.nodeOffset(extra.node); const operand_src = block.src(.{ .node_offset_ptrcast_operand = extra.node }); - const operand = try sema.resolveInst(extra.operand); + const operand = sema.resolveInst(extra.operand); const operand_ty = sema.typeOf(operand); try sema.checkPtrOperand(block, operand_src, operand_ty); @@ -22638,14 +21842,14 @@ fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst if (flags.volatile_cast) ptr_info.flags.is_volatile = false; const dest_ty = blk: { - const dest_ty = try pt.ptrTypeSema(ptr_info); + const dest_ty = try pt.ptrType(ptr_info); if (operand_ty.zigTypeTag(zcu) == .optional) { break :blk try pt.optionalType(dest_ty.toIntern()); } break :blk dest_ty; }; - if (try sema.resolveValue(operand)) |operand_val| { + if (sema.resolveValue(operand)) |operand_val| { return Air.internedToRef((try pt.getCoerced(operand_val, dest_ty)).toIntern()); } @@ -22664,7 +21868,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@truncate"); const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, src); - const operand = try sema.resolveInst(extra.rhs); + const operand = sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); @@ -22678,48 +21882,24 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return sema.coerce(block, dest_ty, operand, operand_src); } - const dest_info = dest_scalar_ty.intInfo(zcu); + if (try dest_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); - if (try sema.typeHasOnePossibleValue(dest_ty)) |val| { - return Air.internedToRef(val.toIntern()); - } + const dest_info = dest_scalar_ty.intInfo(zcu); if (operand_scalar_ty.zigTypeTag(zcu) != .comptime_int) { const operand_info = operand_ty.intInfo(zcu); - if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { - return Air.internedToRef(val.toIntern()); - } if (operand_info.signedness != dest_info.signedness) { return sema.fail(block, operand_src, "expected {s} integer type, found '{f}'", .{ @tagName(dest_info.signedness), operand_ty.fmt(pt), }); } - switch (std.math.order(dest_info.bits, operand_info.bits)) { - .gt => { - const msg = msg: { - const msg = try sema.errMsg( - src, - "destination type '{f}' has more bits than source type '{f}'", - .{ dest_ty.fmt(pt), operand_ty.fmt(pt) }, - ); - errdefer msg.destroy(sema.gpa); - try sema.errNote(src, msg, "destination type has {d} bits", .{ - dest_info.bits, - }); - try sema.errNote(operand_src, msg, "operand type has {d} bits", .{ - operand_info.bits, - }); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - }, - .eq => return operand, - .lt => {}, + if (dest_info.bits >= operand_info.bits) { + return sema.coerce(block, dest_ty, operand, operand_src); } } - if (try sema.resolveValueResolveLazy(operand)) |val| { + if (sema.resolveValue(operand)) |val| { const result_val = try arith.truncate(sema, val, operand_ty, dest_ty, dest_info.signedness, dest_info.bits); return Air.internedToRef(result_val.toIntern()); } @@ -22740,15 +21920,11 @@ fn zirBitCount( const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); _ = try sema.checkIntOrVector(block, operand, operand_src); const bits = operand_ty.intInfo(zcu).bits; - if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { - return Air.internedToRef(val.toIntern()); - } - const result_scalar_ty = try pt.smallestUnsignedInt(bits); switch (operand_ty.zigTypeTag(zcu)) { .vector => { @@ -22757,7 +21933,7 @@ fn zirBitCount( .len = vec_len, .child = result_scalar_ty.toIntern(), }); - if (try sema.resolveValue(operand)) |val| { + if (sema.resolveValue(operand)) |val| { if (val.isUndef(zcu)) return pt.undefRef(result_ty); const elems = try sema.arena.alloc(InternPool.Index, vec_len); @@ -22774,7 +21950,7 @@ fn zirBitCount( } }, .int => { - if (try sema.resolveValueResolveLazy(operand)) |val| { + if (sema.resolveValue(operand)) |val| { if (val.isUndef(zcu)) return pt.undefRef(result_scalar_ty); return pt.intRef(result_scalar_ty, comptimeOp(val, operand_ty, zcu)); } else { @@ -22791,7 +21967,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src); const bits = scalar_ty.intInfo(zcu).bits; @@ -22803,10 +21979,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .{ scalar_ty.fmt(pt), bits }, ); } - if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { - return .fromValue(val); - } - if (try sema.resolveValue(operand)) |operand_val| { + if (sema.resolveValue(operand)) |operand_val| { return .fromValue(try arith.byteSwap(sema, operand_val, operand_ty)); } return block.addTyOp(.byte_swap, operand_ty, operand); @@ -22815,14 +21988,11 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); - const operand = try sema.resolveInst(inst_data.operand); + const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); _ = try sema.checkIntOrVector(block, operand, operand_src); - if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { - return .fromValue(val); - } - if (try sema.resolveValue(operand)) |operand_val| { + if (sema.resolveValue(operand)) |operand_val| { return .fromValue(try arith.bitReverse(sema, operand_val, operand_ty)); } return block.addTyOp(.bit_reverse, operand_ty, operand); @@ -22849,10 +22019,11 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 const ty = try sema.resolveType(block, ty_src, extra.lhs); const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.rhs, .{ .simple = .field_name }); + try sema.ensureLayoutResolved(ty, ty_src, .field_queried); + const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - try ty.resolveLayout(pt); switch (ty.zigTypeTag(zcu)) { .@"struct" => {}, else => return sema.fail(block, ty_src, "expected struct type, found '{f}'", .{ty.fmt(pt)}), @@ -23090,7 +22261,7 @@ fn checkAtomicPtrOperand( ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; - try elem_ty.resolveLayout(pt); + try sema.ensureLayoutResolved(elem_ty, elem_ty_src, .ptr_access); var diag: Zcu.AtomicPtrAlignmentDiagnostics = .{}; const alignment = zcu.atomicPtrAlignment(elem_ty, &diag) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, @@ -23126,7 +22297,7 @@ fn checkAtomicPtrOperand( const ptr_data = switch (ptr_ty.zigTypeTag(zcu)) { .pointer => ptr_ty.ptrInfo(zcu), else => { - const wanted_ptr_ty = try pt.ptrTypeSema(wanted_ptr_data); + const wanted_ptr_ty = try pt.ptrType(wanted_ptr_data); _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); unreachable; }, @@ -23136,7 +22307,7 @@ fn checkAtomicPtrOperand( wanted_ptr_data.flags.is_allowzero = ptr_data.flags.is_allowzero; wanted_ptr_data.flags.is_volatile = ptr_data.flags.is_volatile; - const wanted_ptr_ty = try pt.ptrTypeSema(wanted_ptr_data); + const wanted_ptr_ty = try pt.ptrType(wanted_ptr_data); const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); return casted_ptr; @@ -23245,8 +22416,8 @@ fn checkSimdBinOp( .len = vec_len, .lhs = lhs, .rhs = rhs, - .lhs_val = try sema.resolveValue(lhs), - .rhs_val = try sema.resolveValue(rhs), + .lhs_val = sema.resolveValue(lhs), + .rhs_val = sema.resolveValue(rhs), .result_ty = result_ty, .scalar_ty = result_ty.scalarType(zcu), }; @@ -23338,7 +22509,7 @@ fn resolveExportOptions( const ip = &zcu.intern_pool; const export_options_ty = try sema.getBuiltinType(src, .ExportOptions); - const air_ref = try sema.resolveInst(zir_ref); + const air_ref = sema.resolveInst(zir_ref); const options = try sema.coerce(block, export_options_ty, air_ref, src); const name_src = block.src(.{ .init_field_name = src.offset.node_offset_builtin_call_arg.builtin_call_node }); @@ -23391,7 +22562,7 @@ fn resolveBuiltinEnum( reason: ComptimeReason, ) CompileError!@field(std.builtin, @tagName(name)) { const ty = try sema.getBuiltinType(src, name); - const air_ref = try sema.resolveInst(zir_ref); + const air_ref = sema.resolveInst(zir_ref); const coerced = try sema.coerce(block, ty, air_ref, src); const val = try sema.resolveConstDefinedValue(block, src, coerced, reason); return sema.interpretBuiltinType(block, src, val, @field(std.builtin, @tagName(name))); @@ -23438,7 +22609,7 @@ fn zirCmpxchg( const success_order_src = block.builtinCallArgSrc(extra.node, 4); const failure_order_src = block.builtinCallArgSrc(extra.node, 5); // zig fmt: on - const expected_value = try sema.resolveInst(extra.expected_value); + const expected_value = sema.resolveInst(extra.expected_value); const elem_ty = sema.typeOf(expected_value); if (elem_ty.zigTypeTag(zcu) == .float) { return sema.fail( @@ -23448,9 +22619,9 @@ fn zirCmpxchg( .{elem_ty.fmt(pt)}, ); } - const uncasted_ptr = try sema.resolveInst(extra.ptr); + const uncasted_ptr = sema.resolveInst(extra.ptr); const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); - const new_value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.new_value), new_value_src); + const new_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.new_value), new_value_src); const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order, .{ .simple = .atomic_order }); const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order, .{ .simple = .atomic_order }); @@ -23470,16 +22641,13 @@ fn zirCmpxchg( const result_ty = try pt.optionalType(elem_ty.toIntern()); // special case zero bit types - if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) { - return Air.internedToRef((try pt.intern(.{ .opt = .{ - .ty = result_ty.toIntern(), - .val = .none, - } }))); + if (elem_ty.classify(zcu) == .one_possible_value) { + return .fromValue(try pt.nullValue(result_ty)); } const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { - if (try sema.resolveValue(expected_value)) |expected_val| { - if (try sema.resolveValue(new_value)) |new_val| { + if (sema.resolveValue(expected_value)) |expected_val| { + if (sema.resolveValue(new_value)) |new_val| { if (expected_val.isUndef(zcu) or new_val.isUndef(zcu)) { // TODO: this should probably cause the memory stored at the pointer // to become undef as well @@ -23531,22 +22699,18 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I else => return sema.fail(block, src, "expected array or vector type, found '{f}'", .{dest_ty.fmt(pt)}), } - const operand = try sema.resolveInst(extra.rhs); + const operand = sema.resolveInst(extra.rhs); const scalar_ty = dest_ty.childType(zcu); const scalar = try sema.coerce(block, scalar_ty, operand, scalar_src); const len = try sema.usizeCast(block, src, dest_ty.arrayLen(zcu)); - if (try sema.typeHasOnePossibleValue(dest_ty)) |val| { - return Air.internedToRef(val.toIntern()); - } - - // We also need this case because `[0:s]T` is not OPV. + // If the length is 0, the result is comptime-known even if the operand isn't. if (len == 0) return .fromValue(try pt.aggregateValue(dest_ty, &.{})); const maybe_sentinel = dest_ty.sentinel(zcu); - if (try sema.resolveValue(scalar)) |scalar_val| { + if (sema.resolveValue(scalar)) |scalar_val| { full: { if (dest_ty.zigTypeTag(zcu) == .vector) break :full; const sentinel = maybe_sentinel orelse break :full; @@ -23581,7 +22745,7 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const op_src = block.builtinCallArgSrc(inst_data.src_node, 0); const operand_src = block.builtinCallArgSrc(inst_data.src_node, 1); const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, .ReduceOp, .{ .simple = .operand_reduce_operation }); - const operand = try sema.resolveInst(extra.rhs); + const operand = sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); const pt = sema.pt; const zcu = pt.zcu; @@ -23615,7 +22779,7 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. return sema.fail(block, operand_src, "@reduce operation requires a vector with nonzero length", .{}); } - if (try sema.resolveValue(operand)) |operand_val| { + if (sema.resolveValue(operand)) |operand_val| { if (operand_val.isUndef(zcu)) return pt.undefRef(scalar_ty); var accum: Value = try operand_val.elemValue(pt, 0); @@ -23651,9 +22815,9 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); try sema.checkVectorElemType(block, elem_ty_src, elem_ty); - const a = try sema.resolveInst(extra.a); - const b = try sema.resolveInst(extra.b); - var mask = try sema.resolveInst(extra.mask); + const a = sema.resolveInst(extra.a); + const b = sema.resolveInst(extra.b); + var mask = sema.resolveInst(extra.mask); var mask_ty = sema.typeOf(mask); const mask_len = switch (sema.typeOf(mask).zigTypeTag(zcu)) { @@ -23733,7 +22897,7 @@ fn analyzeShuffle( continue; } // Safe because mask elements are `i32` and we already checked for undef: - const raw = (try sema.resolveLazyValue(mask_val)).toSignedInt(zcu); + const raw = mask_val.toSignedInt(zcu); if (raw >= 0) { const idx: u32 = @intCast(raw); a_used = true; @@ -23760,8 +22924,8 @@ fn analyzeShuffle( } } - const maybe_a_val = try sema.resolveValue(a_coerced); - const maybe_b_val = try sema.resolveValue(b_coerced); + const maybe_a_val = sema.resolveValue(a_coerced); + const maybe_b_val = sema.resolveValue(b_coerced); const a_rt = a_used and maybe_a_val == null; const b_rt = b_used and maybe_b_val == null; @@ -23849,7 +23013,7 @@ fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); try sema.checkVectorElemType(block, elem_ty_src, elem_ty); - const pred_uncoerced = try sema.resolveInst(extra.pred); + const pred_uncoerced = sema.resolveInst(extra.pred); const pred_ty = sema.typeOf(pred_uncoerced); const vec_len_u64 = switch (pred_ty.zigTypeTag(zcu)) { @@ -23868,12 +23032,12 @@ fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C .len = vec_len, .child = elem_ty.toIntern(), }); - const a = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.a), a_src); - const b = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.b), b_src); + const a = try sema.coerce(block, vec_ty, sema.resolveInst(extra.a), a_src); + const b = try sema.coerce(block, vec_ty, sema.resolveInst(extra.b), b_src); - const maybe_pred = try sema.resolveValue(pred); - const maybe_a = try sema.resolveValue(a); - const maybe_b = try sema.resolveValue(b); + const maybe_pred = sema.resolveValue(pred); + const maybe_a = sema.resolveValue(a); + const maybe_b = sema.resolveValue(b); const runtime_src = if (maybe_pred) |pred_val| rs: { if (pred_val.isUndef(zcu)) return pt.undefRef(vec_ty); @@ -23934,10 +23098,12 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const order_src = block.builtinCallArgSrc(inst_data.src_node, 2); // zig fmt: on const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); - const uncasted_ptr = try sema.resolveInst(extra.ptr); + const uncasted_ptr = sema.resolveInst(extra.ptr); const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true); const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order }); + try sema.ensureLayoutResolved(elem_ty, elem_ty_src, .ptr_access); + switch (order) { .release, .acq_rel => { return sema.fail( @@ -23950,9 +23116,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! else => {}, } - if (try sema.typeHasOnePossibleValue(elem_ty)) |val| { - return Air.internedToRef(val.toIntern()); - } + if (try elem_ty.onePossibleValue(sema.pt)) |opv| return .fromValue(opv); if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| { @@ -23983,9 +23147,9 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const operand_src = block.builtinCallArgSrc(inst_data.src_node, 3); const order_src = block.builtinCallArgSrc(inst_data.src_node, 4); // zig fmt: on - const operand = try sema.resolveInst(extra.operand); + const operand = sema.resolveInst(extra.operand); const elem_ty = sema.typeOf(operand); - const uncasted_ptr = try sema.resolveInst(extra.ptr); + const uncasted_ptr = sema.resolveInst(extra.ptr); const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation); @@ -24009,12 +23173,10 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } // special case zero bit types - if (try sema.typeHasOnePossibleValue(elem_ty)) |val| { - return Air.internedToRef(val.toIntern()); - } + if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { - const maybe_operand_val = try sema.resolveValue(operand); + const maybe_operand_val = sema.resolveValue(operand); const operand_val = maybe_operand_val orelse { try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src); break :rs operand_src; @@ -24065,9 +23227,9 @@ fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const operand_src = block.builtinCallArgSrc(inst_data.src_node, 2); const order_src = block.builtinCallArgSrc(inst_data.src_node, 3); // zig fmt: on - const operand = try sema.resolveInst(extra.operand); + const operand = sema.resolveInst(extra.operand); const elem_ty = sema.typeOf(operand); - const uncasted_ptr = try sema.resolveInst(extra.ptr); + const uncasted_ptr = sema.resolveInst(extra.ptr); const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order }); @@ -24098,14 +23260,14 @@ fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const mulend2_src = block.builtinCallArgSrc(inst_data.src_node, 2); const addend_src = block.builtinCallArgSrc(inst_data.src_node, 3); - const addend = try sema.resolveInst(extra.addend); + const addend = sema.resolveInst(extra.addend); const ty = sema.typeOf(addend); - const mulend1 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend1), mulend1_src); - const mulend2 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend2), mulend2_src); + const mulend1 = try sema.coerce(block, ty, sema.resolveInst(extra.mulend1), mulend1_src); + const mulend2 = try sema.coerce(block, ty, sema.resolveInst(extra.mulend2), mulend2_src); - const maybe_mulend1 = try sema.resolveValue(mulend1); - const maybe_mulend2 = try sema.resolveValue(mulend2); - const maybe_addend = try sema.resolveValue(addend); + const maybe_mulend1 = sema.resolveValue(mulend1); + const maybe_mulend2 = sema.resolveValue(mulend2); + const maybe_addend = sema.resolveValue(addend); const pt = sema.pt; const zcu = pt.zcu; @@ -24167,10 +23329,10 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const call_src = block.nodeOffset(inst_data.src_node); const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data; - const func = try sema.resolveInst(extra.callee); + const func = sema.resolveInst(extra.callee); const modifier_ty = try sema.getBuiltinType(call_src, .CallModifier); - const air_ref = try sema.resolveInst(extra.modifier); + const air_ref = sema.resolveInst(extra.modifier); const modifier_ref = try sema.coerce(block, modifier_ty, air_ref, modifier_src); const modifier_val = try sema.resolveConstDefinedValue(block, modifier_src, modifier_ref, .{ .simple = .call_modifier }); var modifier = try sema.interpretBuiltinType(block, modifier_src, modifier_val, std.builtin.CallModifier); @@ -24208,7 +23370,7 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError }, } - const args = try sema.resolveInst(extra.args); + const args = sema.resolveInst(extra.args); const args_ty = sema.typeOf(args); if (!args_ty.isTuple(zcu)) { @@ -24253,18 +23415,23 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Ins const field_name_src = block.builtinCallArgSrc(extra.src_node, 0); const field_ptr_src = block.builtinCallArgSrc(extra.src_node, 1); - const parent_ptr_ty = try sema.resolveDestType(block, inst_src, extra.parent_ptr_type, .remove_eu, "@fieldParentPtr"); - try sema.checkPtrType(block, inst_src, parent_ptr_ty, true); + const maybe_opt_parent_ptr_ty = try sema.resolveDestType(block, inst_src, extra.parent_ptr_type, .remove_eu, "@fieldParentPtr"); + try sema.checkPtrType(block, inst_src, maybe_opt_parent_ptr_ty, true); + const parent_ptr_ty = switch (maybe_opt_parent_ptr_ty.zigTypeTag(zcu)) { + .optional => maybe_opt_parent_ptr_ty.optionalChild(zcu), + .pointer => maybe_opt_parent_ptr_ty, + else => unreachable, + }; const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu); if (parent_ptr_info.flags.size != .one) { return sema.fail(block, inst_src, "expected single pointer type, found '{f}'", .{parent_ptr_ty.fmt(pt)}); } const parent_ty: Type = .fromInterned(parent_ptr_info.child); + try sema.ensureLayoutResolved(parent_ty, inst_src, .field_used); switch (parent_ty.zigTypeTag(zcu)) { .@"struct", .@"union" => {}, else => return sema.fail(block, inst_src, "expected pointer to struct or union type, found '{f}'", .{parent_ptr_ty.fmt(pt)}), } - try parent_ty.resolveLayout(pt); const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name }); const field_index = switch (parent_ty.zigTypeTag(zcu)) { @@ -24285,144 +23452,77 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Ins return sema.fail(block, field_name_src, "cannot get @fieldParentPtr of a comptime field", .{}); } - const field_ptr = try sema.resolveInst(extra.field_ptr); + const field_ptr = sema.resolveInst(extra.field_ptr); const field_ptr_ty = sema.typeOf(field_ptr); try sema.checkPtrOperand(block, field_ptr_src, field_ptr_ty); - const field_ptr_info = field_ptr_ty.ptrInfo(zcu); - - var actual_parent_ptr_info: InternPool.Key.PtrType = .{ - .child = parent_ty.toIntern(), - .flags = .{ - .alignment = try parent_ptr_ty.ptrAlignmentSema(pt), - .is_const = field_ptr_info.flags.is_const, - .is_volatile = field_ptr_info.flags.is_volatile, - .is_allowzero = field_ptr_info.flags.is_allowzero, - .address_space = field_ptr_info.flags.address_space, - }, - .packed_offset = parent_ptr_info.packed_offset, - }; - const field_ty = parent_ty.fieldType(field_index, zcu); - var actual_field_ptr_info: InternPool.Key.PtrType = .{ - .child = field_ty.toIntern(), - .flags = .{ - .alignment = try field_ptr_ty.ptrAlignmentSema(pt), - .is_const = field_ptr_info.flags.is_const, - .is_volatile = field_ptr_info.flags.is_volatile, - .is_allowzero = field_ptr_info.flags.is_allowzero, - .address_space = field_ptr_info.flags.address_space, - }, - .packed_offset = field_ptr_info.packed_offset, - }; - switch (parent_ty.containerLayout(zcu)) { - .auto => { - actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict( - if (zcu.typeToStruct(parent_ty)) |struct_obj| - try field_ty.structFieldAlignmentSema( - struct_obj.fieldAlign(ip, field_index), - struct_obj.layout, - pt, - ) - else if (zcu.typeToUnion(parent_ty)) |union_obj| - try field_ty.unionFieldAlignmentSema( - union_obj.fieldAlign(ip, field_index), - union_obj.flagsUnordered(ip).layout, - pt, - ) - else - actual_field_ptr_info.flags.alignment, - ); - - actual_parent_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 }; - actual_field_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 }; - }, - .@"extern" => { - const field_offset = parent_ty.structFieldOffset(field_index, zcu); - actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(if (field_offset > 0) - Alignment.fromLog2Units(@ctz(field_offset)) - else - actual_field_ptr_info.flags.alignment); - actual_parent_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 }; - actual_field_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 }; - }, - .@"packed" => { - const byte_offset = std.math.divExact(u32, @abs(@as(i32, actual_parent_ptr_info.packed_offset.bit_offset) + - (if (zcu.typeToStruct(parent_ty)) |struct_obj| zcu.structPackedFieldBitOffset(struct_obj, field_index) else 0) - - actual_field_ptr_info.packed_offset.bit_offset), 8) catch - return sema.fail(block, inst_src, "pointer bit-offset mismatch", .{}); - actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(if (byte_offset > 0) - Alignment.fromLog2Units(@ctz(byte_offset)) - else - actual_field_ptr_info.flags.alignment); - }, - } + const hypothetical_field_ptr_ty = try parent_ptr_ty.fieldPtrType(field_index, pt); + const casted_field_ptr = try sema.ptrCastFull( + block, + flags, + inst_src, + field_ptr, + field_ptr_src, + hypothetical_field_ptr_ty, + "@fieldParentPtr", + ); - const actual_field_ptr_ty = try pt.ptrTypeSema(actual_field_ptr_info); - const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, field_ptr_src); - const actual_parent_ptr_ty = try pt.ptrTypeSema(actual_parent_ptr_info); + const unaligned_parent_ptr_ty = try pt.ptrType(info: { + var info = parent_ptr_info; + info.flags.alignment = hypothetical_field_ptr_ty.ptrAlignment(zcu); + break :info info; + }); - const result = if (try sema.resolveDefinedValue(block, field_ptr_src, casted_field_ptr)) |field_ptr_val| result: { - switch (parent_ty.zigTypeTag(zcu)) { - .@"struct" => switch (parent_ty.containerLayout(zcu)) { - .auto => {}, - .@"extern" => { - const byte_offset = parent_ty.structFieldOffset(field_index, zcu); - const parent_ptr_val = try sema.ptrSubtract(block, field_ptr_src, field_ptr_val, byte_offset, actual_parent_ptr_ty); - break :result Air.internedToRef(parent_ptr_val.toIntern()); - }, - .@"packed" => { - // Logic lifted from type computation above - I'm just assuming it's correct. - // `catch unreachable` since error case handled above. - const byte_offset = std.math.divExact(u32, @abs(@as(i32, actual_parent_ptr_info.packed_offset.bit_offset) + - zcu.structPackedFieldBitOffset(zcu.typeToStruct(parent_ty).?, field_index) - - actual_field_ptr_info.packed_offset.bit_offset), 8) catch unreachable; - const parent_ptr_val = try sema.ptrSubtract(block, field_ptr_src, field_ptr_val, byte_offset, actual_parent_ptr_ty); - break :result Air.internedToRef(parent_ptr_val.toIntern()); - }, - }, - .@"union" => switch (parent_ty.containerLayout(zcu)) { - .auto => {}, - .@"extern", .@"packed" => { - // For an extern or packed union, just coerce the pointer. - const parent_ptr_val = try pt.getCoerced(field_ptr_val, actual_parent_ptr_ty); - break :result Air.internedToRef(parent_ptr_val.toIntern()); - }, - }, + const unaligned_parent_ptr: Air.Inst.Ref = if (try sema.resolveDefinedValue( + block, + field_ptr_src, + casted_field_ptr, + )) |field_ptr_val| switch (parent_ty.containerLayout(zcu)) { + .@"packed" => .fromValue(try pt.getCoerced(field_ptr_val, unaligned_parent_ptr_ty)), + .@"extern" => switch (parent_ty.zigTypeTag(zcu)) { + .@"struct" => .fromValue(try sema.ptrSubtract( + block, + field_ptr_src, + field_ptr_val, + parent_ty.structFieldOffset(field_index, zcu), + unaligned_parent_ptr_ty, + )), + .@"union" => .fromValue(try pt.getCoerced(field_ptr_val, unaligned_parent_ptr_ty)), else => unreachable, - } - - const opt_field: ?InternPool.Key.Ptr.BaseAddr.BaseIndex = opt_field: { - const ptr = switch (ip.indexToKey(field_ptr_val.toIntern())) { - .ptr => |ptr| ptr, - else => break :opt_field null, - }; - if (ptr.byte_offset != 0) break :opt_field null; - break :opt_field switch (ptr.base_addr) { - .field => |field| field, - else => null, + }, + .auto => result: { + const opt_field: ?InternPool.Key.Ptr.BaseAddr.BaseIndex = opt_field: { + const ptr = switch (ip.indexToKey(field_ptr_val.toIntern())) { + .ptr => |ptr| ptr, + else => break :opt_field null, + }; + if (ptr.byte_offset != 0) break :opt_field null; + break :opt_field switch (ptr.base_addr) { + .field => |field| field, + else => null, + }; }; - }; - const field = opt_field orelse { - return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{}); - }; + const field = opt_field orelse { + return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{}); + }; - if (Value.fromInterned(field.base).typeOf(zcu).childType(zcu).toIntern() != parent_ty.toIntern()) { - return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{}); - } + if (Value.fromInterned(field.base).typeOf(zcu).childType(zcu).toIntern() != parent_ty.toIntern()) { + return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{}); + } - if (field.index != field_index) { - return sema.fail(block, inst_src, "field '{f}' has index '{d}' but pointer value is index '{d}' of struct '{f}'", .{ - field_name.fmt(ip), field_index, field.index, parent_ty.fmt(pt), - }); - } - break :result try sema.coerce(block, actual_parent_ptr_ty, Air.internedToRef(field.base), inst_src); + if (field.index != field_index) { + return sema.fail(block, inst_src, "field '{f}' has index '{d}' but pointer value is index '{d}' of struct '{f}'", .{ + field_name.fmt(ip), field_index, field.index, parent_ty.fmt(pt), + }); + } + break :result .fromValue(try pt.getCoerced(.fromInterned(field.base), unaligned_parent_ptr_ty)); + }, } else result: { - try sema.requireRuntimeBlock(block, inst_src, field_ptr_src); break :result try block.addInst(.{ .tag = .field_parent_ptr, .data = .{ .ty_pl = .{ - .ty = Air.internedToRef(actual_parent_ptr_ty.toIntern()), + .ty = .fromType(unaligned_parent_ptr_ty), .payload = try block.sema.addExtra(Air.FieldParentPtr{ .field_ptr = casted_field_ptr, .field_index = @intCast(field_index), @@ -24430,14 +23530,61 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Ins } }, }); }; - return sema.ptrCastFull(block, flags, inst_src, result, inst_src, parent_ptr_ty, "@fieldParentPtr"); + + // There's one more error condition: if the hypothetical field pointer type has a lower + // alignment than the parent pointer type, then we need an `@alignCast`. Note that the earlier + // `ptrCastFull` may *also* have "used" the `@alignCast`; that would be a case where the field + // is naturally less aligned than the rest of the struct, *and* the field pointer is itself + // underaligned compared to the field alignment. For example, `struct { a: u32, b: u16 }` with + // a field pointer of type `*align(1) u16`. + switch (hypothetical_field_ptr_ty.ptrAlignment(zcu).order(parent_ptr_ty.ptrAlignment(zcu))) { + .gt => unreachable, // getting a field pointer can never increase alignment + .eq => return sema.coerce(block, maybe_opt_parent_ptr_ty, unaligned_parent_ptr, inst_src), + .lt => if (flags.align_cast) { + // Go through `ptrCastFull` for the safety check. + return sema.ptrCastFull( + block, + flags, + inst_src, + unaligned_parent_ptr, + inst_src, + maybe_opt_parent_ptr_ty, + "@fieldParentPtr", + ); + } else return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(inst_src, "@fieldParentPtr increases pointer alignment", .{}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(inst_src, msg, "parent pointer type '{f}' has alignment '{d}'", .{ + parent_ptr_ty.fmt(pt), + parent_ptr_ty.abiAlignment(zcu), + }); + if (parent_ty.isTuple(zcu)) { + try sema.errNote(field_ptr_src, msg, "tuple field '{d}' limits alignment to '{d}'", .{ + field_index, + field_ptr_ty.ptrAlignment(zcu), + }); + } else { + try sema.errNote(parent_ty.srcLoc(zcu), msg, "{t} field '{f}' limits alignment to '{d}'", .{ + parent_ty.zigTypeTag(zcu), + switch (parent_ty.zigTypeTag(zcu)) { + .@"struct" => parent_ty.structFieldName(field_index, zcu).unwrap().?.fmt(ip), + .@"union" => parent_ty.unionTagTypeHypothetical(zcu).enumFieldName(field_index, zcu).fmt(ip), + else => unreachable, + }, + field_ptr_ty.ptrAlignment(zcu), + }); + } + try sema.errNote(inst_src, msg, "use @alignCast to assert pointer alignment", .{}); + break :msg msg; + }), + } } fn ptrSubtract(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, byte_subtract: u64, new_ty: Type) !Value { const pt = sema.pt; const zcu = pt.zcu; if (byte_subtract == 0) return pt.getCoerced(ptr_val, new_ty); - var ptr = switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) { + const ptr = switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) { .undef => return sema.failWithUseOfUndef(block, src, null), .ptr => |ptr| ptr, else => unreachable, @@ -24450,9 +23597,11 @@ fn ptrSubtract(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, byte break :msg msg; }); } - ptr.byte_offset -= byte_subtract; - ptr.ty = new_ty.toIntern(); - return Value.fromInterned(try pt.intern(.{ .ptr = ptr })); + return Value.fromInterned(try pt.intern(.{ .ptr = .{ + .ty = new_ty.toIntern(), + .base_addr = ptr.base_addr, + .byte_offset = ptr.byte_offset - byte_subtract, + } })); } fn zirMinMax( @@ -24466,8 +23615,8 @@ fn zirMinMax( const src = block.nodeOffset(inst_data.src_node); const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); - const lhs = try sema.resolveInst(extra.lhs); - const rhs = try sema.resolveInst(extra.rhs); + const lhs = sema.resolveInst(extra.lhs); + const rhs = sema.resolveInst(extra.rhs); return sema.analyzeMinMax(block, src, air_tag, &.{ lhs, rhs }, &.{ lhs_src, rhs_src }); } @@ -24487,7 +23636,7 @@ fn zirMinMaxMulti( for (operands, air_refs, operand_srcs, 0..) |zir_ref, *air_ref, *op_src, i| { op_src.* = block.builtinCallArgSrc(src_node, @intCast(i)); - air_ref.* = try sema.resolveInst(zir_ref); + air_ref.* = sema.resolveInst(zir_ref); } return sema.analyzeMinMax(block, src, air_tag, air_refs, operand_srcs); @@ -24590,7 +23739,7 @@ fn analyzeMinMax( const operand_scalar_ty = sema.typeOf(operand).scalarType(zcu); const want_strat: TypeStrat = switch (operand_scalar_ty.zigTypeTag(zcu)) { .comptime_int => s: { - const val = (try sema.resolveValueResolveLazy(operand)).?; + const val = sema.resolveValue(operand).?; if (val.isUndef(zcu)) break :s .none; break :s .{ .int = .{ .all_comptime_int = true, @@ -24609,7 +23758,7 @@ fn analyzeMinMax( // (replaced with just the simple calls to `Type.minInt`/`Type.maxInt`) so that we only // use the input *types* to determine the result type. const min: Value, const max: Value = bounds: { - if (try sema.resolveValueResolveLazy(operand)) |operand_val| { + if (sema.resolveValue(operand)) |operand_val| { if (vector_len) |len| { var min = try operand_val.elemValue(pt, 0); var max = min; @@ -24696,6 +23845,9 @@ fn analyzeMinMax( .child = intermediate_scalar_ty.toIntern(), }) else intermediate_scalar_ty; + // We might have refined all the way down to an OPV type---check now. + if (try result_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); + // This value, if not `null`, will have type `intermediate_ty`. const comptime_part: ?Value = ct: { // Contains the comptime-known scalar result values. @@ -24712,7 +23864,7 @@ fn analyzeMinMax( var opt_runtime_src: ?LazySrcLoc = null; for (operands, operand_srcs) |operand, operand_src| { - const operand_val = try sema.resolveValueResolveLazy(operand) orelse { + const operand_val = sema.resolveValue(operand) orelse { if (opt_runtime_src == null) opt_runtime_src = operand_src; continue; }; @@ -24819,7 +23971,7 @@ fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !A // Already an array pointer. return ptr; } - const new_ty = try pt.ptrTypeSema(.{ + const new_ty = try pt.ptrType(.{ .child = (try pt.arrayType(.{ .len = len, .sentinel = info.sentinel, @@ -24852,8 +24004,8 @@ fn zirMemcpy( const src = block.nodeOffset(inst_data.src_node); const dest_src = block.builtinCallArgSrc(inst_data.src_node, 0); const src_src = block.builtinCallArgSrc(inst_data.src_node, 1); - const dest_ptr = try sema.resolveInst(extra.lhs); - const src_ptr = try sema.resolveInst(extra.rhs); + const dest_ptr = sema.resolveInst(extra.lhs); + const src_ptr = sema.resolveInst(extra.rhs); const dest_ty = sema.typeOf(dest_ptr); const src_ty = sema.typeOf(src_ptr); const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr); @@ -24880,8 +24032,11 @@ fn zirMemcpy( return sema.failWithOwnedErrorMsg(block, msg); } - const dest_elem_ty = dest_ty.indexablePtrElem(zcu); - const src_elem_ty = src_ty.indexablePtrElem(zcu); + const dest_elem_ty = dest_ty.indexableElem(zcu); + const src_elem_ty = src_ty.indexableElem(zcu); + + try sema.ensureLayoutResolved(dest_elem_ty, dest_src, .ptr_access); + try sema.ensureLayoutResolved(src_elem_ty, src_src, .ptr_access); const imc = try sema.coerceInMemoryAllowed( block, @@ -24946,13 +24101,13 @@ fn zirMemcpy( } zero_bit: { - const src_comptime = try src_elem_ty.comptimeOnlySema(pt); - const dest_comptime = try dest_elem_ty.comptimeOnlySema(pt); + const src_comptime = src_elem_ty.comptimeOnly(zcu); + const dest_comptime = dest_elem_ty.comptimeOnly(zcu); assert(src_comptime == dest_comptime); // IMC if (src_comptime) break :zero_bit; - const src_has_bits = try src_elem_ty.hasRuntimeBitsIgnoreComptimeSema(pt); - const dest_has_bits = try dest_elem_ty.hasRuntimeBitsIgnoreComptimeSema(pt); + const src_has_bits = src_elem_ty.hasRuntimeBits(zcu); + const dest_has_bits = dest_elem_ty.hasRuntimeBits(zcu); assert(src_has_bits == dest_has_bits); // IMC if (src_has_bits) break :zero_bit; @@ -24968,7 +24123,7 @@ fn zirMemcpy( const raw_dest_ptr = if (dest_ty.isSlice(zcu)) dest_ptr_val.slicePtr(zcu) else dest_ptr_val; const raw_src_ptr = if (src_ty.isSlice(zcu)) src_ptr_val.slicePtr(zcu) else src_ptr_val; - const len_u64 = try len_val.?.toUnsignedIntSema(pt); + const len_u64 = len_val.?.toUnsignedInt(zcu); if (check_aliasing) { if (Value.doPointersOverlap( @@ -25018,7 +24173,7 @@ fn zirMemcpy( var new_dest_ptr = dest_ptr; var new_src_ptr = src_ptr; if (len_val) |val| { - const len = try val.toUnsignedIntSema(pt); + const len = val.toUnsignedInt(zcu); if (len == 0) { // This AIR instruction guarantees length > 0 if it is comptime-known. return; @@ -25036,7 +24191,7 @@ fn zirMemcpy( } } else if (dest_len == .none and len_val == null) { // Change the dest to a slice, since its type must have the length. - const dest_ptr_ptr = try sema.analyzeRef(block, dest_src, new_dest_ptr); + const dest_ptr_ptr = try sema.analyzeRef(block, dest_src, new_dest_ptr, .none); new_dest_ptr = try sema.analyzeSlice(block, dest_src, dest_ptr_ptr, .zero, src_len, .none, LazySrcLoc.unneeded, dest_src, dest_src, dest_src, false); const new_src_ptr_ty = sema.typeOf(new_src_ptr); if (new_src_ptr_ty.isSlice(zcu)) { @@ -25067,7 +24222,7 @@ fn zirMemcpy( assert(dest_manyptr_ty_key.flags.size == .one); dest_manyptr_ty_key.child = dest_elem_ty.toIntern(); dest_manyptr_ty_key.flags.size = .many; - break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(dest_manyptr_ty_key), new_dest_ptr, dest_src); + break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrType(dest_manyptr_ty_key), new_dest_ptr, dest_src); } else new_dest_ptr; const new_src_ptr_ty = sema.typeOf(new_src_ptr); @@ -25078,13 +24233,13 @@ fn zirMemcpy( assert(src_manyptr_ty_key.flags.size == .one); src_manyptr_ty_key.child = src_elem_ty.toIntern(); src_manyptr_ty_key.flags.size = .many; - break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(src_manyptr_ty_key), new_src_ptr, src_src); + break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrType(src_manyptr_ty_key), new_src_ptr, src_src); } else new_src_ptr; // ok1: dest >= src + len // ok2: src >= dest + len - const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src_src, src); - const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src); + const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src); + const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, src); const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len); const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len); const ok = try block.addBinOp(.bool_or, ok1, ok2); @@ -25113,8 +24268,8 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const src = block.nodeOffset(inst_data.src_node); const dest_src = block.builtinCallArgSrc(inst_data.src_node, 0); const value_src = block.builtinCallArgSrc(inst_data.src_node, 1); - const dest_ptr = try sema.resolveInst(extra.lhs); - const uncoerced_elem = try sema.resolveInst(extra.rhs); + const dest_ptr = sema.resolveInst(extra.lhs); + const uncoerced_elem = sema.resolveInst(extra.rhs); const dest_ptr_ty = sema.typeOf(dest_ptr); try checkMemOperand(sema, block, dest_src, dest_ptr_ty); @@ -25145,10 +24300,17 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src); + const comptime_only_elem = switch (dest_elem_ty.classify(zcu)) { + .no_possible_value => unreachable, // `elem` is a value of this type + .one_possible_value => return, // no work to do + .runtime => false, + .partially_comptime, .fully_comptime => true, + }; + const runtime_src = rs: { const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls), dest_src); const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse break :rs dest_src; - const len_u64 = try len_val.toUnsignedIntSema(pt); + const len_u64 = len_val.toUnsignedInt(zcu); const len = try sema.usizeCast(block, dest_src, len_u64); if (len == 0) { // This AIR instruction guarantees length > 0 if it is comptime-known. @@ -25157,7 +24319,7 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src; if (!sema.isComptimeMutablePtr(ptr_val)) break :rs dest_src; - const elem_val = try sema.resolveValue(elem) orelse break :rs value_src; + const elem_val = sema.resolveValue(elem) orelse break :rs value_src; const array_ty = try pt.arrayType(.{ .child = dest_elem_ty.toIntern(), .len = len_u64, @@ -25174,6 +24336,15 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void return sema.storePtrVal(block, src, array_ptr_val, array_val, array_ty); }; + if (comptime_only_elem) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(src, "cannot store comptime-only element '{f}' at runtime", .{dest_elem_ty.fmt(pt)}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(dest_src, msg, "operation is runtime due to destination pointer", .{}); + break :msg msg; + }); + } + try sema.requireRuntimeBlock(block, src, runtime_src); try sema.validateRuntimeValue(block, dest_src, dest_ptr); try sema.validateRuntimeValue(block, value_src, elem); @@ -25227,7 +24398,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const cc_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); extra_index += 1; const cc_ty = try sema.getBuiltinType(cc_src, .CallingConvention); - const uncoerced_cc = try sema.resolveInst(cc_ref); + const uncoerced_cc = sema.resolveInst(cc_ref); const coerced_cc = try sema.coerce(block, cc_ty, uncoerced_cc, cc_src); const cc_val = try sema.resolveConstDefinedValue(block, cc_src, coerced_cc, .{ .simple = .@"callconv" }); break :blk try sema.analyzeValueAsCallconv(block, cc_src, cc_val); @@ -25344,7 +24515,7 @@ fn zirCDefine( const val_src = block.builtinCallArgSrc(extra.node, 1); const name = try sema.resolveConstString(block, name_src, extra.lhs, .{ .simple = .operand_cDefine_macro_name }); - const rhs = try sema.resolveInst(extra.rhs); + const rhs = sema.resolveInst(extra.rhs); if (sema.typeOf(rhs).zigTypeTag(zcu) != .void) { const value = try sema.resolveConstString(block, val_src, extra.rhs, .{ .simple = .operand_cDefine_macro_value }); try block.c_import_buf.?.print("#define {s} {s}\n", .{ name, value }); @@ -25393,7 +24564,7 @@ fn zirWasmMemoryGrow( } const index: u32 = @intCast(try sema.resolveInt(block, index_src, extra.lhs, .u32, .{ .simple = .wasm_memory_index })); - const delta = try sema.coerce(block, .usize, try sema.resolveInst(extra.rhs), delta_src); + const delta = try sema.coerce(block, .usize, sema.resolveInst(extra.rhs), delta_src); try sema.requireRuntimeBlock(block, builtin_src, null); return block.addInst(.{ @@ -25419,7 +24590,7 @@ fn resolvePrefetchOptions( const ip = &zcu.intern_pool; const options_ty = try sema.getBuiltinType(src, .PrefetchOptions); - const options = try sema.coerce(block, options_ty, try sema.resolveInst(zir_ref), src); + const options = try sema.coerce(block, options_ty, sema.resolveInst(zir_ref), src); const rw_src = block.src(.{ .init_field_rw = src.offset.node_offset_builtin_call_arg.builtin_call_node }); const locality_src = block.src(.{ .init_field_locality = src.offset.node_offset_builtin_call_arg.builtin_call_node }); @@ -25436,7 +24607,7 @@ fn resolvePrefetchOptions( return std.builtin.PrefetchOptions{ .rw = try sema.interpretBuiltinType(block, rw_src, rw_val, std.builtin.PrefetchOptions.Rw), - .locality = @intCast(try locality_val.toUnsignedIntSema(pt)), + .locality = @intCast(locality_val.toUnsignedInt(zcu)), .cache = try sema.interpretBuiltinType(block, cache_src, cache_val, std.builtin.PrefetchOptions.Cache), }; } @@ -25449,7 +24620,7 @@ fn zirPrefetch( const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; const ptr_src = block.builtinCallArgSrc(extra.node, 0); const opts_src = block.builtinCallArgSrc(extra.node, 1); - const ptr = try sema.resolveInst(extra.lhs); + const ptr = sema.resolveInst(extra.lhs); try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr)); const options = try sema.resolvePrefetchOptions(block, opts_src, extra.rhs); @@ -25491,7 +24662,7 @@ fn resolveExternOptions( const io = comp.io; const ip = &zcu.intern_pool; - const options_inst = try sema.resolveInst(zir_ref); + const options_inst = sema.resolveInst(zir_ref); const extern_options_ty = try sema.getBuiltinType(src, .ExternOptions); const options = try sema.coerce(block, extern_options_ty, options_inst, src); @@ -25574,18 +24745,32 @@ fn zirBuiltinExtern( const ty_src = block.builtinCallArgSrc(extra.node, 0); const options_src = block.builtinCallArgSrc(extra.node, 1); - var ty = try sema.resolveType(block, ty_src, extra.lhs); - if (!ty.isPtrAtRuntime(zcu)) { + const ptr_ty = try sema.resolveType(block, ty_src, extra.lhs); + if (!ptr_ty.isPtrAtRuntime(zcu)) { return sema.fail(block, ty_src, "expected (optional) pointer", .{}); } - if (!try sema.validateExternType(ty, .other)) { - const msg = msg: { - const msg = try sema.errMsg(ty_src, "extern symbol cannot have type '{f}'", .{ty.fmt(pt)}); + + const ptr_info = ptr_ty.ptrInfo(zcu); + + const elem_ty: Type = .fromInterned(ptr_info.child); + try sema.ensureLayoutResolved(elem_ty, src, .@"extern"); + + if (!elem_ty.validateExtern(.other, zcu)) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(ty_src, "extern symbol cannot have type '{f}'", .{ptr_ty.fmt(pt)}); errdefer msg.destroy(sema.gpa); - try sema.explainWhyTypeIsNotExtern(msg, ty_src, ty, .other); + try sema.errNote(ty_src, msg, "pointer element type '{f}' is not extern compatible", .{elem_ty.fmt(pt)}); + try sema.explainWhyTypeIsNotExtern(msg, ty_src, elem_ty, .other); break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); + }); + } + if (elem_ty.zigTypeTag(zcu) == .@"fn" and !ptr_info.flags.is_const) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(ty_src, "extern symbol cannot have type '{f}'", .{ptr_ty.fmt(pt)}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(ty_src, msg, "pointer to extern function must be 'const'", .{}); + break :msg msg; + }); } const options = try sema.resolveExternOptions(block, options_src, extra.rhs); @@ -25603,14 +24788,9 @@ fn zirBuiltinExtern( // TODO: error for threadlocal functions, non-const functions, etc - if (options.linkage == .weak and !ty.ptrAllowsZero(zcu)) { - ty = try pt.optionalType(ty.toIntern()); - } - const ptr_info = ty.ptrInfo(zcu); - const extern_val = try pt.getExtern(.{ .name = options.name, - .ty = ptr_info.child, + .ty = elem_ty.toIntern(), .lib_name = options.library_name, .linkage = options.linkage, .visibility = options.visibility, @@ -25626,7 +24806,7 @@ fn zirBuiltinExtern( // So, for now, just use our containing `declaration`. .zir_index = switch (sema.owner.unwrap()) { .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index, - .type => |owner_ty| Type.fromInterned(owner_ty).typeDeclInst(zcu).?, + .type_layout, .struct_defaults => |owner_ty| Type.fromInterned(owner_ty).typeDeclInstAllowGeneratedTag(zcu).?, .memoized_state => unreachable, .nav_ty, .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index, .func => |func| zir_index: { @@ -25641,13 +24821,17 @@ fn zirBuiltinExtern( .source = .builtin, }); + // For a weak symbol where the given type is not nullable, make the pointer optional. + const result_ptr_ty: Type = if (options.linkage == .weak and !ptr_ty.ptrAllowsZero(zcu)) ty: { + break :ty try pt.optionalType(ptr_ty.toIntern()); + } else ptr_ty; + const uncasted_ptr = try sema.analyzeNavRef(block, src, ip.indexToKey(extern_val).@"extern".owner_nav); - // We want to cast to `ty`, but that isn't necessarily an allowed coercion. - if (try sema.resolveValue(uncasted_ptr)) |uncasted_ptr_val| { - const casted_ptr_val = try pt.getCoerced(uncasted_ptr_val, ty); + if (sema.resolveValue(uncasted_ptr)) |uncasted_ptr_val| { + const casted_ptr_val = try pt.getCoerced(uncasted_ptr_val, result_ptr_ty); return Air.internedToRef(casted_ptr_val.toIntern()); } else { - return block.addBitCast(ty, uncasted_ptr); + return block.addBitCast(result_ptr_ty, uncasted_ptr); } } @@ -25732,6 +24916,7 @@ fn zirBuiltinValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD // Values are handled here. .calling_convention_c => { const callconv_ty = try sema.getBuiltinType(src, .CallingConvention); + // Cannot use `Value.uninterpret` because `c` is a *declaration* whose value depends on the target. return try sema.namespaceLookupVal( block, src, @@ -25740,17 +24925,15 @@ fn zirBuiltinValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD ) orelse @panic("std.builtin is corrupt"); }, .calling_convention_inline => { - comptime assert(@typeInfo(std.builtin.CallingConvention.Tag).@"enum".tag_type == u8); const callconv_ty = try sema.getBuiltinType(src, .CallingConvention); - const callconv_tag_ty = callconv_ty.unionTagType(zcu) orelse @panic("std.builtin is corrupt"); - const inline_tag_val = try pt.enumValue( - callconv_tag_ty, - (try pt.intValue( - .u8, - @intFromEnum(std.builtin.CallingConvention.@"inline"), - )).toIntern(), - ); - return sema.coerce(block, callconv_ty, Air.internedToRef(inline_tag_val.toIntern()), src); + return .fromValue(Value.uninterpret( + @as(std.builtin.CallingConvention, .@"inline"), + callconv_ty, + pt, + ) catch |err| switch (err) { + error.TypeMismatch => @panic("std.builtin is corrupt"), + error.OutOfMemory => |e| return e, + }); }, }; return .fromType(try sema.getBuiltinType(src, builtin_type)); @@ -25760,7 +24943,7 @@ fn zirInplaceArithResultTy(sema: *Sema, extended: Zir.Inst.Extended.InstData) Co const pt = sema.pt; const zcu = pt.zcu; - const lhs = try sema.resolveInst(@enumFromInt(extended.operand)); + const lhs = sema.resolveInst(@enumFromInt(extended.operand)); const lhs_ty = sema.typeOf(lhs); const op: Zir.Inst.InplaceOp = @enumFromInt(extended.small); @@ -25785,7 +24968,7 @@ fn zirInplaceArithResultTy(sema: *Sema, extended: Zir.Inst.Extended.InstData) Co fn zirBranchHint(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; - const uncoerced_hint = try sema.resolveInst(extra.operand); + const uncoerced_hint = sema.resolveInst(extra.operand); const operand_src = block.builtinCallArgSrc(extra.node, 0); const hint_ty = try sema.getBuiltinType(operand_src, .BranchHint); @@ -25839,7 +25022,8 @@ fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: } } -/// Emit a compile error if type cannot be used for a runtime variable. +/// Emit a compile error if `var_ty` cannot be used for a runtime variable. +/// Asserts that the layout of `var_ty` is already resolved. pub fn validateVarType( sema: *Sema, block: *Block, @@ -25849,8 +25033,9 @@ pub fn validateVarType( ) CompileError!void { const pt = sema.pt; const zcu = pt.zcu; + var_ty.assertHasLayout(zcu); if (is_extern) { - if (!try sema.validateExternType(var_ty, .other)) { + if (!var_ty.validateExtern(.other, zcu)) { const msg = msg: { const msg = try sema.errMsg(src, "extern variable cannot have type '{f}'", .{var_ty.fmt(pt)}); errdefer msg.destroy(sema.gpa); @@ -25870,7 +25055,7 @@ pub fn validateVarType( } } - if (!try var_ty.comptimeOnlySema(pt)) return; + if (!var_ty.comptimeOnly(zcu)) return; const msg = msg: { const msg = try sema.errMsg(src, "variable of type '{f}' must be const or comptime", .{var_ty.fmt(pt)}); @@ -25886,49 +25071,28 @@ pub fn validateVarType( return sema.failWithOwnedErrorMsg(block, msg); } -const TypeSet = std.AutoHashMapUnmanaged(InternPool.Index, void); - fn explainWhyTypeIsComptime( sema: *Sema, msg: *Zcu.ErrorMsg, - src_loc: LazySrcLoc, - ty: Type, -) CompileError!void { - var type_set = TypeSet{}; - defer type_set.deinit(sema.gpa); - - try ty.resolveFully(sema.pt); - return sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty, &type_set); -} - -fn explainWhyTypeIsComptimeInner( - sema: *Sema, - msg: *Zcu.ErrorMsg, - src_loc: LazySrcLoc, + src: LazySrcLoc, ty: Type, - type_set: *TypeSet, ) CompileError!void { const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; + assert(ty.comptimeOnly(zcu)); switch (ty.zigTypeTag(zcu)) { .bool, .int, .float, .error_set, - .@"enum", .frame, .@"anyframe", .void, - => return, - - .@"fn" => { - try sema.errNote(src_loc, msg, "use '*const {f}' for a function pointer type", .{ty.fmt(pt)}); - }, - - .type => { - try sema.errNote(src_loc, msg, "types are not available at runtime", .{}); - }, + .@"enum", + .@"opaque", + .pointer, + => unreachable, // not comptime-only .comptime_float, .comptime_int, @@ -25936,99 +25100,65 @@ fn explainWhyTypeIsComptimeInner( .noreturn, .undefined, .null, - => return, - - .@"opaque" => { - try sema.errNote(src_loc, msg, "opaque type '{f}' has undefined size", .{ty.fmt(pt)}); - }, - - .array, .vector => { - try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(zcu), type_set); - }, - .pointer => { - const elem_ty = ty.elemType2(zcu); - if (elem_ty.zigTypeTag(zcu) == .@"fn") { - const fn_info = zcu.typeToFunc(elem_ty).?; - if (fn_info.is_generic) { - try sema.errNote(src_loc, msg, "function is generic", .{}); - } - switch (fn_info.cc) { - .@"inline" => try sema.errNote(src_loc, msg, "function has inline calling convention", .{}), - else => {}, - } - if (Type.fromInterned(fn_info.return_type).comptimeOnly(zcu)) { - try sema.errNote(src_loc, msg, "function has a comptime-only return type", .{}); - } - return; - } - try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(zcu), type_set); - }, - - .optional => { - try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.optionalChild(zcu), type_set); - }, - .error_union => { - try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.errorUnionPayload(zcu), type_set); - }, + => return, // no explanation needed - .@"struct" => { - if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return; + .array, .vector => try sema.explainWhyTypeIsComptime(msg, src, ty.childType(zcu)), + .optional => try sema.explainWhyTypeIsComptime(msg, src, ty.optionalChild(zcu)), + .error_union => try sema.explainWhyTypeIsComptime(msg, src, ty.errorUnionPayload(zcu)), - if (zcu.typeToStruct(ty)) |struct_type| { - for (0..struct_type.field_types.len) |i| { - const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); - const field_src: LazySrcLoc = .{ - .base_node_inst = struct_type.zir_index, - .offset = .{ .container_field_type = @intCast(i) }, - }; + .@"fn" => try sema.errNote(src, msg, "use '*const {f}' for a function pointer type", .{ty.fmt(pt)}), + .type => try sema.errNote(src, msg, "types are not available at runtime", .{}), - if (try field_ty.comptimeOnlySema(pt)) { - try sema.errNote(field_src, msg, "struct requires comptime because of this field", .{}); - try sema.explainWhyTypeIsComptimeInner(msg, field_src, field_ty, type_set); - } - } + .@"struct" => if (zcu.typeToStruct(ty)) |struct_type| { + ty.assertHasLayout(zcu); + for (0..struct_type.field_types.len) |i| { + const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); + if (!field_ty.comptimeOnly(zcu)) continue; + const field_src: LazySrcLoc = .{ + .base_node_inst = struct_type.zir_index, + .offset = .{ .container_field_type = @intCast(i) }, + }; + try sema.errNote(field_src, msg, "struct requires comptime because of this field", .{}); + return sema.explainWhyTypeIsComptime(msg, field_src, field_ty); + } + unreachable; + } else { + const tuple = ip.indexToKey(ty.toIntern()).tuple_type; + for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty_ip, field_val_ip| { + if (field_val_ip != .none) continue; + const field_ty: Type = .fromInterned(field_ty_ip); + if (!field_ty.comptimeOnly(zcu)) continue; + try sema.errNote(src, msg, "tuple requires comptime because of field of type '{f}'", .{field_ty.fmt(pt)}); + return sema.explainWhyTypeIsComptime(msg, src, field_ty); } - // TODO tuples + unreachable; }, .@"union" => { - if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return; - - if (zcu.typeToUnion(ty)) |union_obj| { - for (0..union_obj.field_types.len) |i| { - const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[i]); - const field_src: LazySrcLoc = .{ - .base_node_inst = union_obj.zir_index, - .offset = .{ .container_field_type = @intCast(i) }, - }; - - if (try field_ty.comptimeOnlySema(pt)) { - try sema.errNote(field_src, msg, "union requires comptime because of this field", .{}); - try sema.explainWhyTypeIsComptimeInner(msg, field_src, field_ty, type_set); - } - } + const union_obj = zcu.typeToUnion(ty).?; + for (0..union_obj.field_types.len) |i| { + const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[i]); + if (!field_ty.comptimeOnly(zcu)) continue; + const field_src: LazySrcLoc = .{ + .base_node_inst = union_obj.zir_index, + .offset = .{ .container_field_type = @intCast(i) }, + }; + try sema.errNote(field_src, msg, "union requires comptime because of this field", .{}); + return sema.explainWhyTypeIsComptime(msg, field_src, field_ty); } + unreachable; }, } } -const ExternPosition = enum { - ret_ty, - param_ty, - union_field, - struct_field, - element, - other, -}; - -/// Returns true if `ty` is allowed in extern types. -/// Does *NOT* require `ty` to be resolved in any way. -/// Calls `resolveLayout` for packed containers. -fn validateExternType( +/// Keep in sync with `Type.validateExtern`. +pub fn explainWhyTypeIsNotExtern( sema: *Sema, + msg: *Zcu.ErrorMsg, + src_loc: LazySrcLoc, ty: Type, - position: ExternPosition, -) !bool { + position: Type.ExternPosition, +) SemaError!void { const pt = sema.pt; const zcu = pt.zcu; switch (ty.zigTypeTag(zcu)) { @@ -26041,217 +25171,122 @@ fn validateExternType( .error_union, .error_set, .frame, - => return false, - .void => return position == .union_field or position == .ret_ty or position == .struct_field or position == .element, - .noreturn => return position == .ret_ty, - .@"opaque", - .bool, - .float, - .@"anyframe", - => return true, - .pointer => { - if (ty.childType(zcu).zigTypeTag(zcu) == .@"fn") { - return ty.isConstPtr(zcu) and try sema.validateExternType(ty.childType(zcu), .other); - } - return !(ty.isSlice(zcu) or try ty.comptimeOnlySema(pt)); - }, - .int => switch (ty.intInfo(zcu).bits) { - 0, 8, 16, 32, 64, 128 => return true, - else => return false, - }, - .@"fn" => { - if (position != .other) return false; - // For now we want to authorize PTX kernel to use zig objects, even if we end up exposing the ABI. - // The goal is to experiment with more integrated CPU/GPU code. - if (ty.fnCallingConvention(zcu) == .nvptx_kernel) { - return true; - } - return !target_util.fnCallConvAllowsZigTypes(ty.fnCallingConvention(zcu)); - }, - .@"enum" => { - return sema.validateExternType(ty.intTagType(zcu), position); - }, - .@"struct", .@"union" => switch (ty.containerLayout(zcu)) { - .@"extern" => return true, - .@"packed" => { - const bit_size = try ty.bitSizeSema(pt); - switch (bit_size) { - 0, 8, 16, 32, 64, 128 => return true, - else => return false, - } - }, - .auto => return !(try ty.hasRuntimeBitsSema(pt)), - }, - .array => { - if (position == .ret_ty or position == .param_ty) return false; - return sema.validateExternType(ty.elemType2(zcu), .element); - }, - .vector => return sema.validateExternType(ty.elemType2(zcu), .element), - .optional => return ty.isPtrLikeOptional(zcu), - } -} + => return, + + .void => try sema.errNote(src_loc, msg, "'void' is a zero bit type", .{}), + .noreturn => try sema.errNote(src_loc, msg, "'noreturn' is only allowed as a return type", .{}), -fn explainWhyTypeIsNotExtern( - sema: *Sema, - msg: *Zcu.ErrorMsg, - src_loc: LazySrcLoc, - ty: Type, - position: ExternPosition, -) CompileError!void { - const pt = sema.pt; - const zcu = pt.zcu; - switch (ty.zigTypeTag(zcu)) { .@"opaque", .bool, .float, .@"anyframe", - => return, - - .type, - .comptime_float, - .comptime_int, - .enum_literal, - .undefined, - .null, - .error_union, - .error_set, - .frame, - => return, + => unreachable, // these *are* allowed - .pointer => { - if (ty.isSlice(zcu)) { - try sema.errNote(src_loc, msg, "slices have no guaranteed in-memory representation", .{}); + .pointer => if (ty.isSlice(zcu)) { + try sema.errNote(src_loc, msg, "slices have no guaranteed in-memory representation", .{}); + } else { + assert(ty.childType(zcu).zigTypeTag(zcu) == .@"fn"); + if (!ty.isConstPtr(zcu)) { + try sema.errNote(src_loc, msg, "pointer to extern function must be 'const'", .{}); } else { - const pointee_ty = ty.childType(zcu); - if (!ty.isConstPtr(zcu) and pointee_ty.zigTypeTag(zcu) == .@"fn") { - try sema.errNote(src_loc, msg, "pointer to extern function must be 'const'", .{}); - } else if (try ty.comptimeOnlySema(pt)) { - try sema.errNote(src_loc, msg, "pointer to comptime-only type '{f}'", .{pointee_ty.fmt(pt)}); - try sema.explainWhyTypeIsComptime(msg, src_loc, ty); - } - try sema.explainWhyTypeIsNotExtern(msg, src_loc, pointee_ty, .other); + try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.childType(zcu), .other); } }, - .void => try sema.errNote(src_loc, msg, "'void' is a zero bit type; for C 'void' use 'anyopaque'", .{}), - .noreturn => try sema.errNote(src_loc, msg, "'noreturn' is only allowed as a return type", .{}), .int => if (!std.math.isPowerOfTwo(ty.intInfo(zcu).bits)) { try sema.errNote(src_loc, msg, "only integers with 0 or power of two bits are extern compatible", .{}); } else { try sema.errNote(src_loc, msg, "only integers with 0, 8, 16, 32, 64 and 128 bits are extern compatible", .{}); }, - .@"fn" => { - if (position != .other) { - try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{}); - try sema.errNote(src_loc, msg, "use '*const ' to make a function pointer type", .{}); - return; - } - switch (ty.fnCallingConvention(zcu)) { - .auto => try sema.errNote(src_loc, msg, "extern function must specify calling convention", .{}), - .async => try sema.errNote(src_loc, msg, "async function cannot be extern", .{}), - .@"inline" => try sema.errNote(src_loc, msg, "inline function cannot be extern", .{}), - else => return, - } + .@"fn" => if (position != .other) { + try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{}); + try sema.errNote(src_loc, msg, "use '*const ' to make a function pointer type", .{}); + } else switch (ty.fnCallingConvention(zcu)) { + .auto => try sema.errNote(src_loc, msg, "extern function must specify calling convention", .{}), + else => |cc| try sema.errNote(src_loc, msg, "{t} function cannot be extern", .{cc}), }, .@"enum" => { - const tag_ty = ty.intTagType(zcu); - try sema.errNote(src_loc, msg, "enum tag type '{f}' is not extern compatible", .{tag_ty.fmt(pt)}); - try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position); + const enum_obj = zcu.intern_pool.loadEnumType(ty.toIntern()); + switch (enum_obj.int_tag_mode) { + .auto => { + try sema.errNote(ty.srcLoc(zcu), msg, "integer tag type of enum is inferred", .{}); + try sema.errNote(ty.srcLoc(zcu), msg, "consider explicitly specifying the integer tag type", .{}); + }, + .explicit => { + const tag_ty: Type = .fromInterned(enum_obj.int_tag_type); + try sema.errNote(ty.srcLoc(zcu), msg, "enum tag type '{f}' is not extern compatible", .{tag_ty.fmt(pt)}); + try sema.explainWhyTypeIsNotExtern(msg, ty.srcLoc(zcu), tag_ty, position); + }, + } }, - .@"struct" => try sema.errNote(src_loc, msg, "only extern structs and ABI sized packed structs are extern compatible", .{}), - .@"union" => try sema.errNote(src_loc, msg, "only extern unions and ABI sized packed unions are extern compatible", .{}), - .array => { - if (position == .ret_ty) { - return sema.errNote(src_loc, msg, "arrays are not allowed as a return type", .{}); - } else if (position == .param_ty) { - return sema.errNote(src_loc, msg, "arrays are not allowed as a parameter type", .{}); + .@"struct" => { + const struct_obj = zcu.intern_pool.loadStructType(ty.toIntern()); + switch (struct_obj.layout) { + .auto => try sema.errNote(src_loc, msg, "struct with automatic layout has no guaranteed in-memory representation", .{}), + .@"extern" => unreachable, + .@"packed" => switch (struct_obj.packed_backing_mode) { + .auto => try sema.errNote(src_loc, msg, "inferred backing integer of packed struct has unspecified signedness", .{}), + .explicit => { + const backing_int_ty: Type = .fromInterned(struct_obj.packed_backing_int_type); + try sema.errNote(src_loc, msg, "packed struct backing integer type '{f}' is not extern compatible", .{backing_int_ty.fmt(pt)}); + try sema.explainWhyTypeIsNotExtern(msg, src_loc, backing_int_ty, position); + }, + }, } - try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(zcu), .element); }, - .vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(zcu), .element), - .optional => try sema.errNote(src_loc, msg, "only pointer like optionals are extern compatible", .{}), - } -} - -/// Returns true if `ty` is allowed in packed types. -/// Does not require `ty` to be resolved in any way, but may resolve whether it is comptime-only. -fn validatePackedType(sema: *Sema, ty: Type) !bool { - const pt = sema.pt; - const zcu = pt.zcu; - return switch (ty.zigTypeTag(zcu)) { - .type, - .comptime_float, - .comptime_int, - .enum_literal, - .undefined, - .null, - .error_union, - .error_set, - .frame, - .noreturn, - .@"opaque", - .@"anyframe", - .@"fn", - .array, - => false, - .optional => return ty.isPtrLikeOptional(zcu), - .void, - .bool, - .float, - .int, - .vector, - => true, - .@"enum" => switch (zcu.intern_pool.loadEnumType(ty.toIntern()).tag_mode) { - .auto => false, - .explicit, .nonexhaustive => true, + .@"union" => { + const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern()); + switch (union_obj.layout) { + .auto => try sema.errNote(src_loc, msg, "union with automatic layout has no guaranteed in-memory representation", .{}), + .@"extern" => unreachable, + .@"packed" => switch (union_obj.packed_backing_mode) { + .auto => try sema.errNote(src_loc, msg, "inferred backing integer of packed union has unspecified signedness", .{}), + .explicit => { + const backing_int_ty: Type = .fromInterned(union_obj.packed_backing_int_type); + try sema.errNote(src_loc, msg, "packed union backing integer type '{f}' is not extern compatible", .{backing_int_ty.fmt(pt)}); + try sema.explainWhyTypeIsNotExtern(msg, src_loc, backing_int_ty, position); + }, + }, + } }, - .pointer => !ty.isSlice(zcu) and !try ty.comptimeOnlySema(pt), - .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed", - }; + .array => switch (position) { + .ret_ty => try sema.errNote(src_loc, msg, "arrays are not allowed as a return type", .{}), + .param_ty => try sema.errNote(src_loc, msg, "arrays are not allowed as a parameter type", .{}), + else => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.childType(zcu), .element), + }, + .vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.childType(zcu), .element), + .optional => try sema.errNote(src_loc, msg, "non-pointer optionals have no guaranteed in-memory representation", .{}), + } } -fn explainWhyTypeIsNotPacked( +pub fn explainWhyTypeIsUnpackable( sema: *Sema, msg: *Zcu.ErrorMsg, - src_loc: LazySrcLoc, - ty: Type, + src: LazySrcLoc, + reason: Type.UnpackableReason, ) CompileError!void { const pt = sema.pt; const zcu = pt.zcu; - switch (ty.zigTypeTag(zcu)) { - .void, - .bool, - .float, - .int, - .vector, - .@"enum", - => return, - .type, - .comptime_float, - .comptime_int, - .enum_literal, - .undefined, - .null, - .frame, - .noreturn, - .@"opaque", - .error_union, - .error_set, - .@"anyframe", - .optional, - .array, - => try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{}), - .pointer => if (ty.isSlice(zcu)) { - try sema.errNote(src_loc, msg, "slices have no guaranteed in-memory representation", .{}); - } else { - try sema.errNote(src_loc, msg, "comptime-only pointer has no guaranteed in-memory representation", .{}); - try sema.explainWhyTypeIsComptime(msg, src_loc, ty); + switch (reason) { + .comptime_only => try sema.errNote(src, msg, "comptime-only types have no bit-packed representation", .{}), + .pointer => { + try sema.errNote(src, msg, "pointers cannot be directly bitpacked", .{}); + try sema.errNote(src, msg, "consider using 'usize' and '@intFromPtr'", .{}); }, - .@"fn" => { - try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{}); - try sema.errNote(src_loc, msg, "use '*const ' to make a function pointer type", .{}); + .enum_inferred_int_tag => |enum_ty| { + const enum_src = enum_ty.srcLoc(zcu); + try sema.errNote(enum_src, msg, "integer tag type of enum is inferred", .{}); + try sema.errNote(enum_src, msg, "consider explicitly specifying the integer tag type", .{}); + }, + .non_packed_struct => |struct_ty| { + try sema.errNote(src, msg, "non-packed structs do not have a bit-packed representation", .{}); + try sema.addDeclaredHereNote(msg, struct_ty); + }, + .non_packed_union => |union_ty| { + try sema.errNote(src, msg, "non-packed unions do not have a bit-packed representation", .{}); + try sema.addDeclaredHereNote(msg, union_ty); }, - .@"struct" => try sema.errNote(src_loc, msg, "only packed structs layout are allowed in packed types", .{}), - .@"union" => try sema.errNote(src_loc, msg, "only packed unions layout are allowed in packed types", .{}), + .slice => try sema.errNote(src, msg, "slices do not have a bit-packed representation", .{}), + .other => try sema.errNote(src, msg, "type does not have a bit-packed representation", .{}), } } @@ -26277,7 +25312,14 @@ fn getPanicIdFunc(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !In try sema.ensureMemoizedStateResolved(src, .panic); const panic_fn_index = zcu.builtin_decl_values.get(panic_id.toBuiltin()); switch (sema.owner.unwrap()) { - .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {}, + .@"comptime", + .nav_ty, + .nav_val, + .type_layout, + .struct_defaults, + .memoized_state, + => {}, + .func => |owner_func| zcu.intern_pool.funcSetHasErrorTrace(io, owner_func, true), } return panic_fn_index; @@ -26297,7 +25339,7 @@ fn addSafetyCheck( .parent = parent_block, .sema = sema, .namespace = parent_block.namespace, - .instructions = .{}, + .instructions = .empty, .inlining = parent_block.inlining, .comptime_reason = null, .src_base_inst = parent_block.src_base_inst, @@ -26391,7 +25433,7 @@ fn addSafetyCheckUnwrapError( .parent = parent_block, .sema = sema, .namespace = parent_block.namespace, - .instructions = .{}, + .instructions = .empty, .inlining = parent_block.inlining, .comptime_reason = null, .src_base_inst = parent_block.src_base_inst, @@ -26458,21 +25500,39 @@ fn addSafetyCheckSentinelMismatch( const expected_sentinel = Air.internedToRef(expected_sentinel_val.toIntern()); const ptr_ty = sema.typeOf(ptr); - const actual_sentinel = if (ptr_ty.isSlice(zcu)) - try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index) - else blk: { - const elem_ptr_ty = try ptr_ty.elemPtrType(null, pt); - const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty); - break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr); - }; - - const ok = if (sentinel_ty.zigTypeTag(zcu) == .vector) ok: { - const eql = try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq); - break :ok try parent_block.addReduce(eql, .And); - } else ok: { - assert(sentinel_ty.isSelfComparable(zcu, true)); - break :ok try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel); + const ptr_info = ptr_ty.ptrInfo(zcu); + const actual_sentinel: Air.Inst.Ref = switch (ptr_ty.ptrSize(zcu)) { + .slice => try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index), + .one => s: { + const array_ty: Type = .fromInterned(ptr_info.child); + assert(array_ty.zigTypeTag(zcu) == .array); + assert(array_ty.childType(zcu).toIntern() == sentinel_ty.toIntern()); + const many_ptr_ty = try pt.ptrType(.{ + .child = sentinel_ty.toIntern(), + .flags = .{ + .size = .many, + .is_const = ptr_info.flags.is_const, + .is_volatile = ptr_info.flags.is_volatile, + .is_allowzero = ptr_info.flags.is_allowzero, + .alignment = switch (ptr_info.flags.alignment) { + .none => .none, + else => |ptr_align| .minStrict(ptr_align, sentinel_ty.abiAlignment(zcu)), + }, + .address_space = ptr_info.flags.address_space, + }, + }); + const many_ptr = try parent_block.addBitCast(many_ptr_ty, ptr); + break :s try parent_block.addBinOp(.ptr_elem_val, many_ptr, sentinel_index); + }, + .many => unreachable, + .c => unreachable, }; + assert(sema.typeOf(actual_sentinel).toIntern() == sentinel_ty.toIntern()); + assert(sentinel_ty.isSelfComparable(zcu, true)); + const ok: Air.Inst.Ref = if (sentinel_ty.zigTypeTag(zcu) == .vector) ok: { + const elementwise = try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq); + break :ok try parent_block.addReduce(elementwise, .And); + } else try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel); return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.sentinelMismatch", &.{ expected_sentinel, actual_sentinel, @@ -26496,7 +25556,7 @@ fn addSafetyCheckCall( .parent = parent_block, .sema = sema, .namespace = parent_block.namespace, - .instructions = .{}, + .instructions = .empty, .inlining = parent_block.inlining, .comptime_reason = null, .src_base_inst = parent_block.src_base_inst, @@ -26554,8 +25614,10 @@ fn fieldPtrLoad( const pt = sema.pt; const zcu = pt.zcu; const object_ptr_ty = sema.typeOf(object_ptr); + assert(object_ptr_ty.zigTypeTag(zcu) == .pointer); const pointee_ty = object_ptr_ty.childType(zcu); - if (try typeHasOnePossibleValue(sema, pointee_ty)) |opv| { + try sema.ensureLayoutResolved(pointee_ty, src, .ptr_access); + if (try pointee_ty.onePossibleValue(pt)) |opv| { const object: Air.Inst.Ref = .fromValue(opv); return fieldVal(sema, block, src, object, field_name, field_name_src); } @@ -26603,7 +25665,7 @@ fn fieldVal( return Air.internedToRef((try pt.intValue(.usize, inner_ty.arrayLen(zcu))).toIntern()); } else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) { const ptr_info = object_ty.ptrInfo(zcu); - const result_ty = try pt.ptrTypeSema(.{ + const result_ty = try pt.ptrType(.{ .child = Type.fromInterned(ptr_info.child).childType(zcu).toIntern(), .sentinel = if (inner_ty.sentinel(zcu)) |s| s.toIntern() else .none, .flags = .{ @@ -26663,37 +25725,34 @@ fn fieldVal( switch (child_type.zigTypeTag(zcu)) { .error_set => { - switch (ip.indexToKey(child_type.toIntern())) { - .error_set_type => |error_set_type| blk: { - if (error_set_type.nameIndex(ip, field_name) != null) break :blk; + const err_set_ty: Type = err_set: switch (ip.indexToKey(child_type.toIntern())) { + .inferred_error_set_type => |func_index| { + try sema.ensureFuncIesResolved(block, src, func_index); + const resolved_ies = ip.funcIesResolvedUnordered(func_index); + continue :err_set ip.indexToKey(resolved_ies); + }, + .error_set_type => |err_set| if (err_set.nameIndex(ip, field_name) == null) { return sema.fail(block, src, "no error named '{f}' in '{f}'", .{ field_name.fmt(ip), child_type.fmt(pt), }); - }, - .inferred_error_set_type => { - return sema.fail(block, src, "TODO handle inferred error sets here", .{}); - }, + } else child_type, .simple_type => |t| { assert(t == .anyerror); _ = try pt.getErrorValue(field_name); + break :err_set try pt.singleErrorSetType(field_name); }, else => unreachable, - } - - const error_set_type = if (!child_type.isAnyError(zcu)) - child_type - else - try pt.singleErrorSetType(field_name); - return Air.internedToRef((try pt.intern(.{ .err = .{ - .ty = error_set_type.toIntern(), + }; + return .fromIntern(try pt.intern(.{ .err = .{ + .ty = err_set_ty.toIntern(), .name = field_name, - } }))); + } })); }, .@"union" => { if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { return inst; } - try child_type.resolveFields(pt); + try sema.ensureLayoutResolved(child_type, src, .field_used); if (child_type.unionTagType(zcu)) |enum_ty| { if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index_usize| { const field_index: u32 = @intCast(field_index_usize); @@ -26706,6 +25765,7 @@ fn fieldVal( if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { return inst; } + try sema.ensureLayoutResolved(child_type, src, .field_used); const field_index_usize = child_type.enumFieldIndex(field_name, zcu) orelse return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); const field_index: u32 = @intCast(field_index_usize); @@ -26731,13 +25791,15 @@ fn fieldVal( }, .@"struct" => if (is_pointer_to) { // Avoid loading the entire struct by fetching a pointer and loading that - const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false); + try sema.ensureLayoutResolved(inner_ty, src, .ptr_access); + const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty); return sema.analyzeLoad(block, src, field_ptr, object_src); } else { return sema.structFieldVal(block, object, field_name, field_name_src, inner_ty); }, .@"union" => if (is_pointer_to) { // Avoid loading the entire union by fetching a pointer and loading that + try sema.ensureLayoutResolved(inner_ty, src, .ptr_access); const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false); return sema.analyzeLoad(block, src, field_ptr, object_src); } else { @@ -26784,10 +25846,10 @@ fn fieldPtr( .array => { if (field_name.eqlSlice("len", ip)) { const int_val = try pt.intValue(.usize, inner_ty.arrayLen(zcu)); - return uavRef(sema, int_val.toIntern()); + return uavRef(sema, int_val); } else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) { const ptr_info = object_ty.ptrInfo(zcu); - const new_ptr_ty = try pt.ptrTypeSema(.{ + const new_ptr_ty = try pt.ptrType(.{ .child = Type.fromInterned(ptr_info.child).childType(zcu).toIntern(), .sentinel = if (inner_ty.sentinel(zcu)) |s| s.toIntern() else .none, .flags = .{ @@ -26802,10 +25864,11 @@ fn fieldPtr( .packed_offset = ptr_info.packed_offset, }); const ptr_ptr_info = object_ptr_ty.ptrInfo(zcu); - const result_ty = try pt.ptrTypeSema(.{ + const result_ty = try pt.ptrType(.{ .child = new_ptr_ty.toIntern(), .sentinel = if (object_ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none, .flags = .{ + .size = .one, .alignment = ptr_ptr_info.flags.alignment, .is_const = ptr_ptr_info.flags.is_const, .is_volatile = ptr_ptr_info.flags.is_volatile, @@ -26836,7 +25899,7 @@ fn fieldPtr( if (field_name.eqlSlice("ptr", ip)) { const slice_ptr_ty = inner_ty.slicePtrFieldType(zcu); - const result_ty = try pt.ptrTypeSema(.{ + const result_ty = try pt.ptrType(.{ .child = slice_ptr_ty.toIntern(), .flags = .{ .is_const = !attr_ptr_ty.ptrIsMutable(zcu), @@ -26854,7 +25917,7 @@ fn fieldPtr( try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); return field_ptr; } else if (field_name.eqlSlice("len", ip)) { - const result_ty = try pt.ptrTypeSema(.{ + const result_ty = try pt.ptrType(.{ .child = .usize_type, .flags = .{ .is_const = !attr_ptr_ty.ptrIsMutable(zcu), @@ -26881,7 +25944,6 @@ fn fieldPtr( } }, .type => { - _ = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, object_ptr, undefined); const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src); const inner = if (is_pointer_to) try sema.analyzeLoad(block, src, result, object_ptr_src) @@ -26893,44 +25955,39 @@ fn fieldPtr( switch (child_type.zigTypeTag(zcu)) { .error_set => { - switch (ip.indexToKey(child_type.toIntern())) { - .error_set_type => |error_set_type| blk: { - if (error_set_type.nameIndex(ip, field_name) != null) { - break :blk; - } + const err_set_ty: Type = err_set: switch (ip.indexToKey(child_type.toIntern())) { + .inferred_error_set_type => |func_index| { + try sema.ensureFuncIesResolved(block, src, func_index); + const resolved_ies = ip.funcIesResolvedUnordered(func_index); + continue :err_set ip.indexToKey(resolved_ies); + }, + .error_set_type => |err_set| if (err_set.nameIndex(ip, field_name) == null) { return sema.fail(block, src, "no error named '{f}' in '{f}'", .{ field_name.fmt(ip), child_type.fmt(pt), }); - }, - .inferred_error_set_type => { - return sema.fail(block, src, "TODO handle inferred error sets here", .{}); - }, + } else child_type, .simple_type => |t| { assert(t == .anyerror); _ = try pt.getErrorValue(field_name); + break :err_set try pt.singleErrorSetType(field_name); }, else => unreachable, - } - - const error_set_type = if (!child_type.isAnyError(zcu)) - child_type - else - try pt.singleErrorSetType(field_name); - return uavRef(sema, try pt.intern(.{ .err = .{ - .ty = error_set_type.toIntern(), + }; + return uavRef(sema, .fromInterned(try pt.intern(.{ .err = .{ + .ty = err_set_ty.toIntern(), .name = field_name, - } })); + } }))); }, .@"union" => { if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { return inst; } - try child_type.resolveFields(pt); + try sema.ensureLayoutResolved(child_type, src, .field_used); if (child_type.unionTagType(zcu)) |enum_ty| { if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index| { const field_index_u32: u32 = @intCast(field_index); const idx_val = try pt.enumValueFieldIndex(enum_ty, field_index_u32); - return uavRef(sema, idx_val.toIntern()); + return uavRef(sema, idx_val); } } return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); @@ -26939,12 +25996,13 @@ fn fieldPtr( if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { return inst; } + try sema.ensureLayoutResolved(child_type, src, .field_used); const field_index = child_type.enumFieldIndex(field_name, zcu) orelse { return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); }; const field_index_u32: u32 = @intCast(field_index); const idx_val = try pt.enumValueFieldIndex(child_type, field_index_u32); - return uavRef(sema, idx_val.toIntern()); + return uavRef(sema, idx_val); }, .@"struct", .@"opaque" => { if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { @@ -26960,7 +26018,8 @@ fn fieldPtr( try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) else object_ptr; - const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); + try sema.ensureLayoutResolved(inner_ty, src, .ptr_access); + const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty); try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); return field_ptr; }, @@ -26969,6 +26028,7 @@ fn fieldPtr( try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) else object_ptr; + try sema.ensureLayoutResolved(inner_ty, src, .ptr_access); const field_ptr = try sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); return field_ptr; @@ -27012,6 +26072,7 @@ fn fieldCallBind( // Optionally dereference a second pointer to get the concrete type. const is_double_ptr = inner_ty.zigTypeTag(zcu) == .pointer and inner_ty.ptrSize(zcu) == .one; const concrete_ty = if (is_double_ptr) inner_ty.childType(zcu) else inner_ty; + try sema.ensureLayoutResolved(concrete_ty, src, .ptr_access); const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty; const object_ptr = if (is_double_ptr) try sema.analyzeLoad(block, src, raw_ptr, src) @@ -27021,10 +26082,8 @@ fn fieldCallBind( find_field: { switch (concrete_ty.zigTypeTag(zcu)) { .@"struct" => { - try concrete_ty.resolveFields(pt); if (zcu.typeToStruct(concrete_ty)) |struct_type| { - const field_index = struct_type.nameIndex(ip, field_name) orelse - break :find_field; + const field_index = struct_type.nameIndex(ip, field_name) orelse break :find_field; const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); return sema.finishFieldCallBind(block, src, ptr_ty, field_ty, field_index, object_ptr); @@ -27047,9 +26106,9 @@ fn fieldCallBind( } }, .@"union" => { - try concrete_ty.resolveFields(pt); const union_obj = zcu.typeToUnion(concrete_ty).?; - _ = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse break :find_field; + const enum_obj = ip.loadEnumType(union_obj.enum_tag_type); + if (enum_obj.nameIndex(ip, field_name) == null) break :find_field; const field_ptr = try unionFieldPtr(sema, block, src, object_ptr, field_name, field_name_src, concrete_ty, false); return .{ .direct = try sema.analyzeLoad(block, src, field_ptr, src) }; }, @@ -27163,7 +26222,7 @@ fn finishFieldCallBind( ) CompileError!ResolvedFieldCallee { const pt = sema.pt; const zcu = pt.zcu; - const ptr_field_ty = try pt.ptrTypeSema(.{ + const ptr_field_ty = try pt.ptrType(.{ .child = field_ty.toIntern(), .flags = .{ .is_const = !ptr_ty.ptrIsMutable(zcu), @@ -27174,7 +26233,6 @@ fn finishFieldCallBind( const container_ty = ptr_ty.childType(zcu); if (container_ty.zigTypeTag(zcu) == .@"struct") { if (container_ty.structFieldIsComptime(field_index, zcu)) { - try container_ty.resolveStructFieldInits(pt); const default_val = (try container_ty.structFieldValueComptime(pt, field_index)).?; return .{ .direct = Air.internedToRef(default_val.toIntern()) }; } @@ -27239,6 +26297,7 @@ fn namespaceLookupVal( return try sema.analyzeNavVal(block, src, nav); } +/// Asserts that the layout of `struct_ty` is already resolved. fn structFieldPtr( sema: *Sema, block: *Block, @@ -27247,33 +26306,33 @@ fn structFieldPtr( field_name: InternPool.NullTerminatedString, field_name_src: LazySrcLoc, struct_ty: Type, - initializing: bool, ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - assert(struct_ty.zigTypeTag(zcu) == .@"struct"); - try struct_ty.resolveFields(pt); - try struct_ty.resolveLayout(pt); + assert(struct_ty.zigTypeTag(zcu) == .@"struct"); + struct_ty.assertHasLayout(zcu); - if (struct_ty.isTuple(zcu)) { + const field_index: u32 = if (struct_ty.isTuple(zcu)) field_index: { if (field_name.eqlSlice("len", ip)) { const len_inst = try pt.intRef(.usize, struct_ty.structFieldCount(zcu)); - return sema.analyzeRef(block, src, len_inst); + return sema.analyzeRef(block, src, len_inst, .none); } - const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src); - return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing); - } - - const struct_type = zcu.typeToStruct(struct_ty).?; - - const field_index = struct_type.nameIndex(ip, field_name) orelse - return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name); + break :field_index try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src); + } else field_index: { + const struct_type = zcu.typeToStruct(struct_ty).?; + break :field_index struct_type.nameIndex(ip, field_name) orelse { + return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name); + }; + }; return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, struct_ty); } +/// Supports both structs and unions. +/// +/// Asserts that the layout of `struct_ty` is already resolved. fn structFieldPtrByIndex( sema: *Sema, block: *Block, @@ -27284,79 +26343,24 @@ fn structFieldPtrByIndex( ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; - const ip = &zcu.intern_pool; - - const struct_type = zcu.typeToStruct(struct_ty).?; - const field_is_comptime = struct_type.fieldIsComptime(ip, field_index); - // Comptime fields are handled later - if (!field_is_comptime) { - if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| { - const val = try struct_ptr_val.ptrField(field_index, pt); - return Air.internedToRef(val.toIntern()); - } - } - - const field_ty = struct_type.field_types.get(ip)[field_index]; + struct_ty.assertHasLayout(zcu); const struct_ptr_ty = sema.typeOf(struct_ptr); - const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu); - - var ptr_ty_data: InternPool.Key.PtrType = .{ - .child = field_ty, - .flags = .{ - .is_const = struct_ptr_ty_info.flags.is_const, - .is_volatile = struct_ptr_ty_info.flags.is_volatile, - .address_space = struct_ptr_ty_info.flags.address_space, - }, - }; - const parent_align = if (struct_ptr_ty_info.flags.alignment != .none) - struct_ptr_ty_info.flags.alignment - else - try Type.fromInterned(struct_ptr_ty_info.child).abiAlignmentSema(pt); - - if (struct_type.layout == .@"packed") { - assert(!field_is_comptime); - const packed_offset = struct_ty.packedStructFieldPtrInfo(struct_ptr_ty, field_index, pt); - ptr_ty_data.flags.alignment = parent_align; - ptr_ty_data.packed_offset = packed_offset; - } else if (struct_type.layout == .@"extern") { - assert(!field_is_comptime); - // For extern structs, field alignment might be bigger than type's - // natural alignment. Eg, in `extern struct { x: u32, y: u16 }` the - // second field is aligned as u32. - const field_offset = struct_ty.structFieldOffset(field_index, zcu); - ptr_ty_data.flags.alignment = if (parent_align == .none) - .none - else - @enumFromInt(@min(@intFromEnum(parent_align), @ctz(field_offset))); + if (struct_ty.structFieldIsComptime(field_index, zcu)) { + const field_ptr_ty = try struct_ptr_ty.fieldPtrType(field_index, pt); + return .fromIntern(try pt.intern(.{ .ptr = .{ + .ty = field_ptr_ty.toIntern(), + .base_addr = .{ .comptime_field = struct_ty.structFieldDefaultValue(field_index, zcu).?.toIntern() }, + .byte_offset = 0, + } })); + } else if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| { + return .fromValue(try struct_ptr_val.ptrField(field_index, pt)); } else { - // Our alignment is capped at the field alignment. - const field_align = try Type.fromInterned(field_ty).structFieldAlignmentSema( - struct_type.fieldAlign(ip, field_index), - struct_type.layout, - pt, - ); - ptr_ty_data.flags.alignment = if (struct_ptr_ty_info.flags.alignment == .none) - field_align - else - field_align.min(parent_align); + const field_ptr_ty = try struct_ptr_ty.fieldPtrType(field_index, pt); + return block.addStructFieldPtr(struct_ptr, field_index, field_ptr_ty); } - - const ptr_field_ty = try pt.ptrTypeSema(ptr_ty_data); - - if (field_is_comptime) { - try struct_ty.resolveStructFieldInits(pt); - const val = try pt.intern(.{ .ptr = .{ - .ty = ptr_field_ty.toIntern(), - .base_addr = .{ .comptime_field = struct_type.field_inits.get(ip)[field_index] }, - .byte_offset = 0, - } }); - return Air.internedToRef(val); - } - - return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty); -} +} fn structFieldVal( sema: *Sema, @@ -27370,8 +26374,8 @@ fn structFieldVal( const zcu = pt.zcu; const ip = &zcu.intern_pool; assert(struct_ty.zigTypeTag(zcu) == .@"struct"); - - try struct_ty.resolveFields(pt); + assert(sema.typeOf(struct_byval).toIntern() == struct_ty.toIntern()); + struct_ty.assertHasLayout(zcu); switch (ip.indexToKey(struct_ty.toIntern())) { .struct_type => { @@ -27379,24 +26383,19 @@ fn structFieldVal( const field_index = struct_type.nameIndex(ip, field_name) orelse return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name); - if (struct_type.fieldIsComptime(ip, field_index)) { - try struct_ty.resolveStructFieldInits(pt); - return Air.internedToRef(struct_type.field_inits.get(ip)[field_index]); + if (struct_type.field_is_comptime_bits.get(ip, field_index)) { + return .fromIntern(struct_type.field_defaults.get(ip)[field_index]); } const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); - if (try sema.typeHasOnePossibleValue(field_ty)) |field_val| - return Air.internedToRef(field_val.toIntern()); + if (try field_ty.onePossibleValue(pt)) |field_val| + return .fromValue(field_val); - if (try sema.resolveValue(struct_byval)) |struct_val| { + if (sema.resolveValue(struct_byval)) |struct_val| { if (struct_val.isUndef(zcu)) return pt.undefRef(field_ty); - if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| { - return Air.internedToRef(opv.toIntern()); - } - return Air.internedToRef((try struct_val.fieldValue(pt, field_index)).toIntern()); + return .fromValue(try struct_val.fieldValue(pt, field_index)); } - try field_ty.resolveLayout(pt); return block.addStructFieldVal(struct_byval, field_index, field_ty); }, .tuple_type => { @@ -27457,16 +26456,13 @@ fn tupleFieldValByIndex( const zcu = pt.zcu; const field_ty = tuple_ty.fieldType(field_index, zcu); - if (tuple_ty.structFieldIsComptime(field_index, zcu)) - try tuple_ty.resolveStructFieldInits(pt); if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_value| { return Air.internedToRef(default_value.toIntern()); } - if (try sema.resolveValue(tuple_byval)) |tuple_val| { - if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| { - return Air.internedToRef(opv.toIntern()); - } + if (try field_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); + + if (sema.resolveValue(tuple_byval)) |tuple_val| { return switch (zcu.intern_pool.indexToKey(tuple_val.toIntern())) { .undef => pt.undefRef(field_ty), .aggregate => |aggregate| Air.internedToRef(switch (aggregate.storage) { @@ -27478,10 +26474,10 @@ fn tupleFieldValByIndex( }; } - try field_ty.resolveLayout(pt); return block.addStructFieldVal(tuple_byval, field_index, field_ty); } +/// Asserts that the layout of `union_ty` is already resolved. fn unionFieldPtr( sema: *Sema, block: *Block, @@ -27497,35 +26493,17 @@ fn unionFieldPtr( const ip = &zcu.intern_pool; assert(union_ty.zigTypeTag(zcu) == .@"union"); + union_ty.assertHasLayout(zcu); - const union_ptr_ty = sema.typeOf(union_ptr); - const union_ptr_info = union_ptr_ty.ptrInfo(zcu); - try union_ty.resolveFields(pt); const union_obj = zcu.typeToUnion(union_ty).?; + const tag_ty: Type = .fromInterned(union_obj.enum_tag_type); + const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); - const ptr_field_ty = try pt.ptrTypeSema(.{ - .child = field_ty.toIntern(), - .flags = .{ - .is_const = union_ptr_info.flags.is_const, - .is_volatile = union_ptr_info.flags.is_volatile, - .address_space = union_ptr_info.flags.address_space, - .alignment = if (union_obj.flagsUnordered(ip).layout == .auto) blk: { - const union_align = if (union_ptr_info.flags.alignment != .none) - union_ptr_info.flags.alignment - else - try union_ty.abiAlignmentSema(pt); - const field_align = try union_ty.fieldAlignmentSema(field_index, pt); - break :blk union_align.min(field_align); - } else union_ptr_info.flags.alignment, - }, - .packed_offset = union_ptr_info.packed_offset, - }); - const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_ty).enumFieldIndex(field_name, zcu).?); - if (initializing and field_ty.zigTypeTag(zcu) == .noreturn) { + if (initializing and field_ty.classify(zcu) == .no_possible_value) { const msg = msg: { - const msg = try sema.errMsg(src, "cannot initialize 'noreturn' field of union", .{}); + const msg = try sema.errMsg(src, "cannot initialize union field with uninstantiable type '{f}'", .{field_ty.fmt(pt)}); errdefer msg.destroy(sema.gpa); try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{ @@ -27538,30 +26516,24 @@ fn unionFieldPtr( } if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: { - switch (union_obj.flagsUnordered(ip).layout) { + switch (union_obj.layout) { .auto => if (initializing) { if (!sema.isComptimeMutablePtr(union_ptr_val)) { // The initialization is a runtime operation. break :ct; } // Store to the union to initialize the tag. - const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index); - const payload_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); - const new_union_val = try pt.unionValue(union_ty, field_tag, try pt.undefValue(payload_ty)); + const field_tag = try pt.enumValueFieldIndex(tag_ty, field_index); + const payload_val = try field_ty.onePossibleValue(pt) orelse try pt.undefValue(field_ty); + const new_union_val = try pt.unionValue(union_ty, field_tag, payload_val); try sema.storePtrVal(block, src, union_ptr_val, new_union_val, union_ty); } else { - const union_val = (try sema.pointerDeref(block, src, union_ptr_val, union_ptr_ty)) orelse - break :ct; - if (union_val.isUndef(zcu)) { - return sema.failWithUseOfUndef(block, src, null); - } - const un = ip.indexToKey(union_val.toIntern()).un; - const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index); - const tag_matches = un.tag == field_tag.toIntern(); - if (!tag_matches) { + const union_val = try sema.pointerDeref(block, src, union_ptr_val, union_ptr_val.typeOf(zcu)) orelse break :ct; + if (union_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, null); + const active_index = tag_ty.enumTagFieldIndex(union_val.unionTag(zcu).?, zcu).?; + if (active_index != field_index) { const msg = msg: { - const active_index = Type.fromInterned(union_obj.enum_tag_ty).enumTagFieldIndex(Value.fromInterned(un.tag), zcu).?; - const active_field_name = Type.fromInterned(union_obj.enum_tag_ty).enumFieldName(active_index, zcu); + const active_field_name = tag_ty.enumFieldName(active_index, zcu); const msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{ field_name.fmt(ip), active_field_name.fmt(ip), @@ -27575,34 +26547,34 @@ fn unionFieldPtr( }, .@"packed", .@"extern" => {}, } - const field_ptr_val = try union_ptr_val.ptrField(field_index, pt); - return Air.internedToRef(field_ptr_val.toIntern()); + return .fromValue(try union_ptr_val.ptrField(field_index, pt)); } // If the union has a tag, we must either set or or safety check it depending on `initializing`. tag: { if (union_ty.containerLayout(zcu) != .auto) break :tag; - const tag_ty: Type = .fromInterned(union_obj.enum_tag_ty); - if (try sema.typeHasOnePossibleValue(tag_ty) != null) break :tag; + if (tag_ty.classify(zcu) == .one_possible_value) break :tag; // There is a hypothetical non-trivial tag. We must set it even if not there at runtime, but // only emit a safety check if it's available at runtime (i.e. it's safety-tagged). - const want_tag = try pt.enumValueFieldIndex(tag_ty, enum_field_index); + const want_tag = try pt.enumValueFieldIndex(tag_ty, field_index); if (initializing) { const set_tag_inst = try block.addBinOp(.set_union_tag, union_ptr, .fromValue(want_tag)); try sema.checkComptimeKnownStore(block, set_tag_inst, .unneeded); // `unneeded` since this isn't a "proper" store - } else if (block.wantSafety() and union_obj.hasTag(ip)) { - // The tag exists at runtime (safety tag), so emit a safety check. + } else if (block.wantSafety() and union_obj.has_runtime_tag) { + // The tag exists at runtime (actual or safety tag), so emit a safety check. // TODO would it be better if get_union_tag supported pointers to unions? const union_val = try block.addTyOp(.load, union_ty, union_ptr); const active_tag = try block.addTyOp(.get_union_tag, tag_ty, union_val); try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, .fromValue(want_tag)); } } - if (field_ty.zigTypeTag(zcu) == .noreturn) { + if (field_ty.classify(zcu) == .no_possible_value) { _ = try block.addNoOp(.unreach); return .unreachable_value; } - return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty); + + const field_ptr_ty = try sema.typeOf(union_ptr).fieldPtrType(field_index, pt); + return block.addStructFieldPtr(union_ptr, field_index, field_ptr_ty); } fn unionFieldVal( @@ -27618,71 +26590,57 @@ fn unionFieldVal( const zcu = pt.zcu; const ip = &zcu.intern_pool; assert(union_ty.zigTypeTag(zcu) == .@"union"); + assert(sema.typeOf(union_byval).toIntern() == union_ty.toIntern()); + union_ty.assertHasLayout(zcu); - try union_ty.resolveFields(pt); const union_obj = zcu.typeToUnion(union_ty).?; const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); - const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_ty).enumFieldIndex(field_name, zcu).?); + const enum_tag_ty: Type = .fromInterned(union_obj.enum_tag_type); - if (try sema.resolveValue(union_byval)) |union_val| { + if (sema.resolveValue(union_byval)) |union_val| { if (union_val.isUndef(zcu)) return pt.undefRef(field_ty); - - const un = ip.indexToKey(union_val.toIntern()).un; - const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index); - const tag_matches = un.tag == field_tag.toIntern(); - switch (union_obj.flagsUnordered(ip).layout) { + switch (union_obj.layout) { .auto => { - if (tag_matches) { - return Air.internedToRef(un.val); - } else { - const msg = msg: { - const active_index = Type.fromInterned(union_obj.enum_tag_ty).enumTagFieldIndex(Value.fromInterned(un.tag), zcu).?; - const active_field_name = Type.fromInterned(union_obj.enum_tag_ty).enumFieldName(active_index, zcu); - const msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{ - field_name.fmt(ip), active_field_name.fmt(ip), - }); - errdefer msg.destroy(sema.gpa); - try sema.addDeclaredHereNote(msg, union_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } + const active_tag_val = union_val.unionTag(zcu).?; + const active_index = enum_tag_ty.enumTagFieldIndex(active_tag_val, zcu).?; + if (active_index == field_index) return .fromValue(union_val.unionPayload(zcu)); + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{ + field_name.fmt(ip), enum_tag_ty.enumFieldName(active_index, zcu).fmt(ip), + }); + errdefer msg.destroy(zcu.comp.gpa); + try sema.addDeclaredHereNote(msg, union_ty); + break :msg msg; + }); }, - .@"extern" => if (tag_matches) { - // Fast path - no need to use bitcast logic. - return Air.internedToRef(un.val); - } else if (try sema.bitCastVal(union_val, field_ty, 0, 0, 0)) |field_val| { - return Air.internedToRef(field_val.toIntern()); + .@"extern" => if (try sema.bitCastVal(union_val, field_ty, 0, 0, 0)) |field_val| { + return .fromValue(field_val); + } else { + // Runtime-known due to a pointer-to-integer conversion. }, - .@"packed" => if (tag_matches) { - // Fast path - no need to use bitcast logic. - return Air.internedToRef(un.val); - } else if (try sema.bitCastVal(union_val, field_ty, 0, try union_ty.bitSizeSema(pt), 0)) |field_val| { - return Air.internedToRef(field_val.toIntern()); + .@"packed" => { + const field_val = try sema.bitCastVal(union_val, field_ty, 0, union_ty.bitSize(zcu), 0) orelse { + unreachable; // `null` is only possible if the input value contains a pointer, which a packed union cannot. + }; + return .fromValue(field_val); }, } } - if (union_obj.flagsUnordered(ip).layout == .auto and block.wantSafety() and - union_ty.unionTagTypeSafety(zcu) != null and union_obj.field_types.len > 1) - { - const wanted_tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index); - const wanted_tag = Air.internedToRef(wanted_tag_val.toIntern()); - const active_tag = try block.addTyOp(.get_union_tag, .fromInterned(union_obj.enum_tag_ty), union_byval); - try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, wanted_tag); + if (union_obj.layout == .auto and block.wantSafety() and union_obj.has_runtime_tag) { + const wanted_tag_val = try pt.enumValueFieldIndex(enum_tag_ty, field_index); + const active_tag = try block.addTyOp(.get_union_tag, enum_tag_ty, union_byval); + try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, .fromValue(wanted_tag_val)); } - if (field_ty.zigTypeTag(zcu) == .noreturn) { + if (field_ty.classify(zcu) == .no_possible_value) { _ = try block.addNoOp(.unreach); return .unreachable_value; } - if (try sema.typeHasOnePossibleValue(field_ty)) |field_only_value| { - return Air.internedToRef(field_only_value.toIntern()); - } + if (try field_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); - try field_ty.resolveLayout(pt); return block.addStructFieldVal(union_byval, field_index, field_ty); } @@ -27706,17 +26664,15 @@ fn elemPtr( else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{f}'", .{indexable_ptr_ty.fmt(pt)}), }; try sema.checkIndexable(block, src, indexable_ty); + try sema.ensureLayoutResolved(indexable_ty, src, .ptr_access); const elem_ptr = switch (indexable_ty.zigTypeTag(zcu)) { - .array, .vector => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety), - .@"struct" => blk: { - // Tuple field access. - const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index }); - const index: u32 = @intCast(try index_val.toUnsignedIntSema(pt)); - break :blk try sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init); - }, + .vector => try sema.elemPtrVector(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init), + .array => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety), + .@"struct" => try sema.tupleElemPtr(block, src, indexable_ptr, elem_index, elem_index_src), else => { const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); + try sema.ensureLayoutResolved(sema.typeOf(indexable).childType(zcu), src, .ptr_access); return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety); }, }; @@ -27725,7 +26681,7 @@ fn elemPtr( return elem_ptr; } -/// Asserts that the type of indexable is pointer. +/// Asserts that `indexable` is an indexable pointer whose child type has its layout already resolved. fn elemPtrOneLayerOnly( sema: *Sema, block: *Block, @@ -27741,28 +26697,31 @@ fn elemPtrOneLayerOnly( const pt = sema.pt; const zcu = pt.zcu; - try sema.checkIndexable(block, src, indexable_ty); + assert(indexable_ty.isIndexable(zcu)); + assert(indexable_ty.zigTypeTag(zcu) == .pointer); + const child_ty = indexable_ty.childType(zcu); + child_ty.assertHasLayout(zcu); switch (indexable_ty.ptrSize(zcu)) { .slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), .many, .c => { const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable); const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); + const maybe_index: ?u64 = if (maybe_index_val) |val| val.toUnsignedInt(zcu) else null; ct: { const ptr_val = maybe_ptr_val orelse break :ct; - const index_val = maybe_index_val orelse break :ct; - const index: usize = @intCast(try index_val.toUnsignedIntSema(pt)); - const elem_ptr = try ptr_val.ptrElem(index, pt); - return Air.internedToRef(elem_ptr.toIntern()); + const index: usize = @intCast(maybe_index orelse break :ct); + return .fromValue(try ptr_val.ptrElem(index, pt)); } try sema.checkLogicalPtrOperation(block, src, indexable_ty); - const result_ty = try indexable_ty.elemPtrType(null, pt); + + const result_ty = try indexable_ty.elemPtrType(maybe_index, pt); try sema.validateRuntimeElemAccess(block, elem_index_src, result_ty, indexable_ty, indexable_src); try sema.validateRuntimeValue(block, indexable_src, indexable); - if (!try result_ty.childType(zcu).hasRuntimeBitsIgnoreComptimeSema(pt)) { + if (child_ty.abiSize(zcu) == 0) { // zero-bit child type; just bitcast the pointer return block.addBitCast(result_ty, indexable); } @@ -27770,15 +26729,10 @@ fn elemPtrOneLayerOnly( return block.addPtrElemPtr(indexable, elem_index, result_ty); }, .one => { - const child_ty = indexable_ty.childType(zcu); const elem_ptr = switch (child_ty.zigTypeTag(zcu)) { - .array, .vector => try sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety), - .@"struct" => blk: { - assert(child_ty.isTuple(zcu)); - const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index }); - const index: u32 = @intCast(try index_val.toUnsignedIntSema(pt)); - break :blk try sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false); - }, + .vector => try sema.elemPtrVector(block, indexable_src, indexable, elem_index_src, elem_index, init), + .array => try sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety), + .@"struct" => try sema.tupleElemPtr(block, indexable_src, indexable, elem_index, elem_index_src), else => unreachable, // Guaranteed by checkIndexable }; try sema.checkKnownAllocPtr(block, indexable, elem_ptr); @@ -27808,45 +26762,51 @@ fn elemVal( const elem_index = try sema.coerce(block, .usize, elem_index_uncasted, elem_index_src); switch (indexable_ty.zigTypeTag(zcu)) { - .pointer => switch (indexable_ty.ptrSize(zcu)) { - .slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), - .many, .c => { - const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable); - const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); - const elem_ty = indexable_ty.elemType2(zcu); - - ct: { - const indexable_val = maybe_indexable_val orelse break :ct; - const index_val = maybe_index_val orelse break :ct; - const index: usize = @intCast(try index_val.toUnsignedIntSema(pt)); - const many_ptr_ty = try pt.manyConstPtrType(elem_ty); - const many_ptr_val = try pt.getCoerced(indexable_val, many_ptr_ty); - const elem_ptr_ty = try pt.singleConstPtrType(elem_ty); - const elem_ptr_val = try many_ptr_val.ptrElem(index, pt); - const elem_val = try sema.pointerDeref(block, indexable_src, elem_ptr_val, elem_ptr_ty) orelse break :ct; - return Air.internedToRef((try pt.getCoerced(elem_val, elem_ty)).toIntern()); - } + .pointer => { + const child_ty = indexable_ty.childType(zcu); + try sema.ensureLayoutResolved(child_ty, src, .ptr_access); + switch (indexable_ty.ptrSize(zcu)) { + .slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), + .many, .c => { + const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable); + const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); + + ct: { + const indexable_val = maybe_indexable_val orelse break :ct; + const index_val = maybe_index_val orelse break :ct; + const index: usize = @intCast(index_val.toUnsignedInt(zcu)); + const many_ptr_ty = try pt.manyConstPtrType(child_ty); + const many_ptr_val = try pt.getCoerced(indexable_val, many_ptr_ty); + const elem_ptr_val = try many_ptr_val.ptrElem(index, pt); + return sema.analyzeLoad(block, src, .fromValue(elem_ptr_val), indexable_src); + } - if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_only_value| { - return Air.internedToRef(elem_only_value.toIntern()); - } + try sema.validateRuntimeElemAccess(block, elem_index_src, child_ty, indexable_ty, src); + switch (child_ty.classify(zcu)) { + .runtime => {}, + .one_possible_value => return .fromValue((try child_ty.onePossibleValue(pt)).?), + .no_possible_value => switch (child_ty.zigTypeTag(zcu)) { + .@"opaque" => return sema.fail(block, src, "cannot load opaque type '{f}'", .{child_ty.fmt(pt)}), + else => return sema.fail(block, src, "cannot load uninstantiable type '{f}'", .{child_ty.fmt(pt)}), + }, + .partially_comptime, .fully_comptime => unreachable, // caught by `validateRuntimeElemAccess` + } - try sema.checkLogicalPtrOperation(block, src, indexable_ty); - return block.addBinOp(.ptr_elem_val, indexable, elem_index); - }, - .one => { - arr_sent: { - const inner_ty = indexable_ty.childType(zcu); - if (inner_ty.zigTypeTag(zcu) != .array) break :arr_sent; - const sentinel = inner_ty.sentinel(zcu) orelse break :arr_sent; - const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent; - const index = try sema.usizeCast(block, src, try index_val.toUnsignedIntSema(pt)); - if (index != inner_ty.arrayLen(zcu)) break :arr_sent; - return Air.internedToRef(sentinel.toIntern()); - } - const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety); - return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src); - }, + return block.addBinOp(.ptr_elem_val, indexable, elem_index); + }, + .one => { + arr_sent: { + if (child_ty.zigTypeTag(zcu) != .array) break :arr_sent; + const sentinel = child_ty.sentinel(zcu) orelse break :arr_sent; + const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent; + const index = try sema.usizeCast(block, src, index_val.toUnsignedInt(zcu)); + if (index != child_ty.arrayLen(zcu)) break :arr_sent; + return .fromValue(sentinel); + } + const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety); + return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src); + }, + } }, .array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), .vector => { @@ -27856,7 +26816,7 @@ fn elemVal( .@"struct" => { // Tuple field access. const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index }); - const index: u32 = @intCast(try index_val.toUnsignedIntSema(pt)); + const index: u32 = @intCast(index_val.toUnsignedInt(zcu)); return sema.tupleField(block, indexable_src, indexable, elem_index_src, index); }, else => unreachable, @@ -27864,6 +26824,7 @@ fn elemVal( } /// Called when the index or indexable is runtime known. +/// Asserts that the layout of `elem_ty` is already resolved. fn validateRuntimeElemAccess( sema: *Sema, block: *Block, @@ -27875,16 +26836,16 @@ fn validateRuntimeElemAccess( const pt = sema.pt; const zcu = pt.zcu; - if (try elem_ty.comptimeOnlySema(sema.pt)) { + if (elem_ty.comptimeOnly(zcu)) { const msg = msg: { const msg = try sema.errMsg( elem_index_src, "values of type '{f}' must be comptime-known, but index value is runtime-known", - .{parent_ty.fmt(sema.pt)}, + .{elem_ty.fmt(sema.pt)}, ); errdefer msg.destroy(sema.gpa); - try sema.explainWhyTypeIsComptime(msg, parent_src, parent_ty); + try sema.explainWhyTypeIsComptime(msg, parent_src, elem_ty); break :msg msg; }; @@ -27900,71 +26861,38 @@ fn validateRuntimeElemAccess( } } -fn tupleFieldPtr( +/// Validates `elem_index`, and returns a pointer to that field using `structFieldPtrByIndex`. +/// +/// Asserts that the type of `tuple_ptr` is a single-item pointer whose child type is a tuple. +fn tupleElemPtr( sema: *Sema, block: *Block, - tuple_ptr_src: LazySrcLoc, + src: LazySrcLoc, tuple_ptr: Air.Inst.Ref, - field_index_src: LazySrcLoc, - field_index: u32, - init: bool, + elem_index: Air.Inst.Ref, + elem_index_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; const tuple_ptr_ty = sema.typeOf(tuple_ptr); - const tuple_ptr_info = tuple_ptr_ty.ptrInfo(zcu); - const tuple_ty: Type = .fromInterned(tuple_ptr_info.child); - try tuple_ty.resolveFields(pt); - const field_count = tuple_ty.structFieldCount(zcu); + assert(tuple_ptr_ty.isSinglePointer(zcu)); + const tuple_ty = tuple_ptr_ty.childType(zcu); + assert(tuple_ty.isTuple(zcu)); + const field_count = tuple_ty.structFieldCount(zcu); if (field_count == 0) { - return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{}); + return sema.fail(block, src, "indexing into empty tuple is not allowed", .{}); } - if (field_index >= field_count) { - return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{ - field_index, field_count, + const elem_index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index }); + const index = elem_index_val.getUnsignedInt(zcu); + if (index == null or index.? >= field_count) { + return sema.fail(block, elem_index_src, "index '{f}' out of bounds of tuple '{f}'", .{ + elem_index_val.fmtValueSema(pt, sema), tuple_ty.fmt(pt), }); } - const field_ty = tuple_ty.fieldType(field_index, zcu); - const ptr_field_ty = try pt.ptrTypeSema(.{ - .child = field_ty.toIntern(), - .flags = .{ - .is_const = tuple_ptr_info.flags.is_const, - .is_volatile = tuple_ptr_info.flags.is_volatile, - .address_space = tuple_ptr_info.flags.address_space, - .alignment = a: { - if (tuple_ptr_info.flags.alignment == .none) break :a .none; - // The tuple pointer isn't naturally aligned, so the field pointer might be underaligned. - const tuple_align = tuple_ptr_info.flags.alignment; - const field_align = try field_ty.abiAlignmentSema(pt); - break :a tuple_align.min(field_align); - }, - }, - }); - - if (tuple_ty.structFieldIsComptime(field_index, zcu)) - try tuple_ty.resolveStructFieldInits(pt); - - if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_val| { - return Air.internedToRef((try pt.intern(.{ .ptr = .{ - .ty = ptr_field_ty.toIntern(), - .base_addr = .{ .comptime_field = default_val.toIntern() }, - .byte_offset = 0, - } }))); - } - - if (try sema.resolveValue(tuple_ptr)) |tuple_ptr_val| { - const field_ptr_val = try tuple_ptr_val.ptrField(field_index, pt); - return Air.internedToRef(field_ptr_val.toIntern()); - } - - if (!init) { - try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_ptr_src); - } - - return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty); + return sema.structFieldPtrByIndex(block, src, tuple_ptr, @intCast(index.?), tuple_ty); } fn tupleField( @@ -27978,7 +26906,6 @@ fn tupleField( const pt = sema.pt; const zcu = pt.zcu; const tuple_ty = sema.typeOf(tuple); - try tuple_ty.resolveFields(pt); const field_count = tuple_ty.structFieldCount(zcu); if (field_count == 0) { @@ -27993,20 +26920,17 @@ fn tupleField( const field_ty = tuple_ty.fieldType(field_index, zcu); - if (tuple_ty.structFieldIsComptime(field_index, zcu)) - try tuple_ty.resolveStructFieldInits(pt); if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_value| { return Air.internedToRef(default_value.toIntern()); // comptime field } - if (try sema.resolveValue(tuple)) |tuple_val| { + if (sema.resolveValue(tuple)) |tuple_val| { if (tuple_val.isUndef(zcu)) return pt.undefRef(field_ty); return Air.internedToRef((try tuple_val.fieldValue(pt, field_index)).toIntern()); } try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_src); - try field_ty.resolveLayout(pt); return block.addStructFieldVal(tuple, field_index, field_ty); } @@ -28032,12 +26956,12 @@ fn elemValArray( return sema.fail(block, array_src, "indexing into empty array is not allowed", .{}); } - const maybe_undef_array_val = try sema.resolveValue(array); + const maybe_undef_array_val = sema.resolveValue(array); // index must be defined since it can access out of bounds const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); if (maybe_index_val) |index_val| { - const index: usize = @intCast(try index_val.toUnsignedIntSema(pt)); + const index: usize = @intCast(index_val.toUnsignedInt(zcu)); if (array_sent) |s| { if (index == array_len) { return Air.internedToRef(s.toIntern()); @@ -28053,10 +26977,11 @@ fn elemValArray( return pt.undefRef(elem_ty); } if (maybe_index_val) |index_val| { - const index: usize = @intCast(try index_val.toUnsignedIntSema(pt)); - const elem_val = try array_val.elemValue(pt, index); - return Air.internedToRef(elem_val.toIntern()); + const index: usize = @intCast(index_val.toUnsignedInt(zcu)); + return .fromValue(try array_val.elemValue(pt, index)); } + // Since the array is comptime-known, it might be OPV, in which case the index is irrelevant. + if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); } try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_ty, array_src); @@ -28071,12 +26996,105 @@ fn elemValArray( } } - if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_val| - return Air.internedToRef(elem_val.toIntern()); - return block.addBinOp(.array_elem_val, array, elem_index); } +fn elemPtrVector( + sema: *Sema, + block: *Block, + vector_ptr_src: LazySrcLoc, + vector_ptr: Air.Inst.Ref, + elem_index_src: LazySrcLoc, + elem_index: Air.Inst.Ref, + init: bool, +) CompileError!Air.Inst.Ref { + const pt = sema.pt; + const zcu = pt.zcu; + const vector_ptr_ty = sema.typeOf(vector_ptr); + const vector_ty = vector_ptr_ty.childType(zcu); + assert(vector_ty.zigTypeTag(zcu) == .vector); + const vector_len = vector_ty.vectorLen(zcu); + + if (vector_len == 0) { + return sema.fail(block, vector_ptr_src, "cannot index into empty vector", .{}); + } + + const maybe_vector_ptr_val = sema.resolveValue(vector_ptr); + // The index must not be undefined since it can be out of bounds. + const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse { + return sema.fail(block, elem_index_src, "vector index not comptime known", .{}); + }; + const index = index_val.toUnsignedInt(zcu); + if (index >= vector_len) { + return sema.fail(block, elem_index_src, "index {d} outside vector of length {d}", .{ index, vector_len }); + } + + const elem_ty = vector_ty.childType(zcu); + const elem_bits = elem_ty.bitSize(zcu); + // Exiting this block means the operation is a runtime one. + const elem_ptr_ty: Type = if (elem_bits < 8 or !std.math.isPowerOfTwo(elem_bits)) elem_ptr_ty: { + // Use a packed pointer (i.e. vector_index != 0) + const vector_ptr_info = vector_ptr_ty.ptrInfo(zcu); + const elem_ptr_ty = try pt.ptrType(.{ + .child = elem_ty.toIntern(), + .flags = .{ + .size = .one, + .alignment = vector_ptr_info.flags.alignment, + .is_const = vector_ptr_info.flags.is_const, + .is_volatile = vector_ptr_info.flags.is_volatile, + .is_allowzero = vector_ptr_info.flags.is_allowzero, + .address_space = vector_ptr_info.flags.address_space, + .vector_index = @enumFromInt(index), + }, + .packed_offset = .{ + .host_size = @intCast(vector_len), + .bit_offset = 0, + }, + }); + if (maybe_vector_ptr_val) |ptr_val| { + if (ptr_val.isUndef(zcu)) return pt.undefRef(elem_ptr_ty); + return .fromValue(try pt.getCoerced(ptr_val, elem_ptr_ty)); + } + break :elem_ptr_ty elem_ptr_ty; + } else elem_ptr_ty: { + // Use a normal pointer (i.e. vector_index == 0) + const vector_ptr_info = vector_ptr_ty.ptrInfo(zcu); + const elem_ptr_ty = try pt.ptrType(.{ + .child = elem_ty.toIntern(), + .flags = .{ + .size = .one, + // TODO: this logic was ported from old code, but it's bogus. This entire block will + // go away when https://github.com/ziglang/zig/issues/24061 is implemented anyway. + .alignment = switch (vector_ptr_info.flags.alignment) { + .none => .none, + else => |vec_align| switch (index * elem_ty.abiSize(zcu)) { + 0 => vec_align, + else => |byte_offset| .minStrict(vec_align, .fromLog2Units(@ctz(byte_offset))), + }, + }, + .is_const = vector_ptr_info.flags.is_const, + .is_volatile = vector_ptr_info.flags.is_volatile, + .is_allowzero = vector_ptr_info.flags.is_allowzero, + .address_space = vector_ptr_info.flags.address_space, + }, + }); + if (maybe_vector_ptr_val) |ptr_val| { + if (ptr_val.isUndef(zcu)) return pt.undefRef(elem_ptr_ty); + const bit_offset = index * @divExact(elem_ty.bitSize(zcu), 8); + return .fromValue(try ptr_val.getOffsetPtr(bit_offset, elem_ptr_ty, pt)); + } + break :elem_ptr_ty elem_ptr_ty; + }; + + if (!init) { + try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, vector_ty, vector_ptr_src); + try sema.validateRuntimeValue(block, vector_ptr_src, vector_ptr); + } + + return block.addPtrElemPtr(vector_ptr, elem_index, elem_ptr_ty); +} + +/// Asserts that the layout of the array is already resolved. fn elemPtrArray( sema: *Sema, block: *Block, @@ -28091,19 +27109,21 @@ fn elemPtrArray( const pt = sema.pt; const zcu = pt.zcu; const array_ptr_ty = sema.typeOf(array_ptr); + assert(array_ptr_ty.ptrSize(zcu) == .one); const array_ty = array_ptr_ty.childType(zcu); + assert(array_ty.zigTypeTag(zcu) == .array); const array_sent = array_ty.sentinel(zcu) != null; const array_len = array_ty.arrayLen(zcu); const array_len_s = array_len + @intFromBool(array_sent); if (array_len_s == 0) { - return sema.fail(block, array_ptr_src, "indexing into empty array is not allowed", .{}); + return sema.fail(block, array_ptr_src, "cannot index into empty array", .{}); } - const maybe_undef_array_ptr_val = try sema.resolveValue(array_ptr); + const maybe_undef_array_ptr_val = sema.resolveValue(array_ptr); // The index must not be undefined since it can be out of bounds. - const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { - const index = try sema.usizeCast(block, elem_index_src, try index_val.toUnsignedIntSema(pt)); + const maybe_index: ?u64 = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { + const index = index_val.toUnsignedInt(zcu); if (index >= array_len_s) { const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else ""; return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); @@ -28111,37 +27131,39 @@ fn elemPtrArray( break :o index; } else null; - if (offset == null and array_ty.zigTypeTag(zcu) == .vector) { - return sema.fail(block, elem_index_src, "vector index not comptime known", .{}); - } - - const elem_ptr_ty = try array_ptr_ty.elemPtrType(offset, pt); + array_ty.assertHasLayout(zcu); + const elem_ptr_ty = try array_ptr_ty.elemPtrType(maybe_index, pt); if (maybe_undef_array_ptr_val) |array_ptr_val| { if (array_ptr_val.isUndef(zcu)) { return pt.undefRef(elem_ptr_ty); } - if (offset) |index| { - const elem_ptr = try array_ptr_val.ptrElem(index, pt); - return Air.internedToRef(elem_ptr.toIntern()); + if (maybe_index) |index| { + return .fromValue(try array_ptr_val.ptrElem(index, pt)); } } if (!init) { - try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.elemType2(zcu), array_ty, array_ptr_src); + try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.childType(zcu), array_ty, array_ptr_src); try sema.validateRuntimeValue(block, array_ptr_src, array_ptr); } // Runtime check is only needed if unable to comptime check. - if (oob_safety and block.wantSafety() and offset == null) { + if (oob_safety and block.wantSafety() and maybe_index == null) { const len_inst = try pt.intRef(.usize, array_len); const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt; try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); } + if (array_ty.childType(zcu).abiSize(zcu) == 0) { + // zero-bit child type; just bitcast the pointer + return block.addBitCast(elem_ptr_ty, array_ptr); + } + return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty); } +/// Asserts that the layout of the slice element type is already resolved. fn elemValSlice( sema: *Sema, block: *Block, @@ -28155,9 +27177,11 @@ fn elemValSlice( const pt = sema.pt; const zcu = pt.zcu; const slice_ty = sema.typeOf(slice); + assert(slice_ty.isSlice(zcu)); const slice_sent = slice_ty.sentinel(zcu) != null; - const elem_ty = slice_ty.elemType2(zcu); - var runtime_src = slice_src; + const elem_ty = slice_ty.childType(zcu); + + elem_ty.assertHasLayout(zcu); // slice must be defined since it can dereferenced as null const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice); @@ -28165,37 +27189,30 @@ fn elemValSlice( const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); if (maybe_slice_val) |slice_val| { - runtime_src = elem_index_src; - const slice_len = try slice_val.sliceLen(pt); + const slice_len = slice_val.sliceLen(zcu); const slice_len_s = slice_len + @intFromBool(slice_sent); if (slice_len_s == 0) { return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); } if (maybe_index_val) |index_val| { - const index: usize = @intCast(try index_val.toUnsignedIntSema(pt)); + const index: usize = @intCast(index_val.toUnsignedInt(zcu)); if (index >= slice_len_s) { const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); } - const elem_ptr_ty = try slice_ty.elemPtrType(index, pt); const elem_ptr_val = try slice_val.ptrElem(index, pt); - if (try sema.pointerDeref(block, slice_src, elem_ptr_val, elem_ptr_ty)) |elem_val| { - return Air.internedToRef(elem_val.toIntern()); - } - runtime_src = slice_src; + return sema.analyzeLoad(block, src, .fromValue(elem_ptr_val), slice_src); } } - if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_only_value| { - return Air.internedToRef(elem_only_value.toIntern()); - } + if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_ty, slice_src); try sema.validateRuntimeValue(block, slice_src, slice); if (oob_safety and block.wantSafety()) { const len_inst = if (maybe_slice_val) |slice_val| - try pt.intRef(.usize, try slice_val.sliceLen(pt)) + try pt.intRef(.usize, slice_val.sliceLen(zcu)) else try block.addTyOp(.slice_len, .usize, slice); const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; @@ -28204,6 +27221,7 @@ fn elemValSlice( return block.addBinOp(.slice_elem_val, slice, elem_index); } +/// Asserts that the layout of the slice element type is already resolved. fn elemPtrSlice( sema: *Sema, block: *Block, @@ -28217,33 +27235,35 @@ fn elemPtrSlice( const pt = sema.pt; const zcu = pt.zcu; const slice_ty = sema.typeOf(slice); + assert(slice_ty.isSlice(zcu)); const slice_sent = slice_ty.sentinel(zcu) != null; + const elem_ty = slice_ty.childType(zcu); + elem_ty.assertHasLayout(zcu); - const maybe_undef_slice_val = try sema.resolveValue(slice); + const maybe_undef_slice_val = sema.resolveValue(slice); // The index must not be undefined since it can be out of bounds. - const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { - const index = try sema.usizeCast(block, elem_index_src, try index_val.toUnsignedIntSema(pt)); - break :o index; + const offset: ?u64 = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { + break :o index_val.toUnsignedInt(zcu); } else null; const elem_ptr_ty = try slice_ty.elemPtrType(offset, pt); + assert(elem_ptr_ty.childType(zcu).toIntern() == elem_ty.toIntern()); if (maybe_undef_slice_val) |slice_val| { if (slice_val.isUndef(zcu)) { return pt.undefRef(elem_ptr_ty); } - const slice_len = try slice_val.sliceLen(pt); + const slice_len = slice_val.sliceLen(zcu); const slice_len_s = slice_len + @intFromBool(slice_sent); if (slice_len_s == 0) { - return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); + return sema.fail(block, slice_src, "cannot index into empty slice", .{}); } if (offset) |index| { if (index >= slice_len_s) { const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); } - const elem_ptr_val = try slice_val.ptrElem(index, pt); - return Air.internedToRef(elem_ptr_val.toIntern()); + return .fromValue(try slice_val.ptrElem(index, pt)); } } @@ -28254,13 +27274,13 @@ fn elemPtrSlice( const len_inst = len: { if (maybe_undef_slice_val) |slice_val| if (!slice_val.isUndef(zcu)) - break :len try pt.intRef(.usize, try slice_val.sliceLen(pt)); + break :len try pt.intRef(.usize, slice_val.sliceLen(zcu)); break :len try block.addTyOp(.slice_len, .usize, slice); }; const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); } - if (!try slice_ty.childType(zcu).hasRuntimeBitsIgnoreComptimeSema(pt)) { + if (elem_ty.abiSize(zcu) == 0) { // zero-bit child type; just extract the pointer and bitcast it const slice_ptr = try block.addTyOp(.slice_ptr, slice_ty.slicePtrFieldType(zcu), slice); return block.addBitCast(elem_ptr_ty, slice_ptr); @@ -28331,15 +27351,17 @@ fn coerceExtra( if (dest_ty.isGenericPoison()) return inst; const dest_ty_src = inst_src; // TODO better source location - try dest_ty.resolveFields(pt); const inst_ty = sema.typeOf(inst); - try inst_ty.resolveFields(pt); const target = zcu.getTarget(); + + inst_ty.assertHasLayout(zcu); + try sema.ensureLayoutResolved(dest_ty, inst_src, .coerce); + // If the types are the same, we can return the operand. if (dest_ty.eql(inst_ty, zcu)) return inst; - const maybe_inst_val = try sema.resolveValue(inst); + const maybe_inst_val = sema.resolveValue(inst); var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val); if (in_memory_result == .ok) { @@ -28357,7 +27379,7 @@ fn coerceExtra( if (maybe_inst_val) |val| { // undefined sets the optional bit also to undefined. if (val.toIntern() == .undef) { - return pt.undefRef(dest_ty); + return .fromValue(try dest_ty.onePossibleValue(pt) orelse try pt.undefValue(dest_ty)); } // null to ?T @@ -28372,11 +27394,11 @@ fn coerceExtra( // cast from ?*T and ?[*]T to ?*anyopaque // but don't do it if the source type is a double pointer if (dest_ty.isPtrLikeOptional(zcu) and - dest_ty.elemType2(zcu).toIntern() == .anyopaque_type and + dest_ty.nullablePtrElem(zcu).toIntern() == .anyopaque_type and inst_ty.isPtrAtRuntime(zcu)) anyopaque_check: { if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :optional; - const elem_ty = inst_ty.elemType2(zcu); + const elem_ty = inst_ty.nullablePtrElem(zcu); if (elem_ty.zigTypeTag(zcu) == .pointer or elem_ty.isPtrLikeOptional(zcu)) { in_memory_result = .{ .double_ptr_to_anyopaque = .{ .actual = inst_ty, @@ -28409,7 +27431,7 @@ fn coerceExtra( // Function body to function pointer. if (inst_ty.zigTypeTag(zcu) == .@"fn") { - const fn_val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined); + const fn_val = sema.resolveValue(inst).?; const fn_nav = switch (zcu.intern_pool.indexToKey(fn_val.toIntern())) { .func => |f| f.owner_nav, .@"extern" => |e| e.owner_nav, @@ -28430,7 +27452,7 @@ fn coerceExtra( const array_elem_ty = array_ty.childType(zcu); if (array_ty.arrayLen(zcu) != 1) break :single_item; const dest_is_mut = !dest_info.flags.is_const; - switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, maybe_inst_val)) { + switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, null)) { .ok => {}, else => break :single_item, } @@ -28448,7 +27470,7 @@ fn coerceExtra( const dest_is_mut = !dest_info.flags.is_const; const dst_elem_type: Type = .fromInterned(dest_info.child); - const elem_res = try sema.coerceInMemoryAllowed(block, dst_elem_type, array_elem_type, dest_is_mut, target, dest_ty_src, inst_src, maybe_inst_val); + const elem_res = try sema.coerceInMemoryAllowed(block, dst_elem_type, array_elem_type, dest_is_mut, target, dest_ty_src, inst_src, null); switch (elem_res) { .ok => {}, else => { @@ -28509,7 +27531,7 @@ fn coerceExtra( const src_elem_ty = inst_ty.childType(zcu); const dest_is_mut = !dest_info.flags.is_const; const dst_elem_type: Type = .fromInterned(dest_info.child); - switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, maybe_inst_val)) { + switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, null)) { .ok => {}, else => break :src_c_ptr, } @@ -28520,7 +27542,7 @@ fn coerceExtra( // but don't do it if the source type is a double pointer if (dest_info.child == .anyopaque_type and inst_ty.zigTypeTag(zcu) == .pointer) to_anyopaque: { if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; - const elem_ty = inst_ty.elemType2(zcu); + const elem_ty = inst_ty.childType(zcu); if (elem_ty.zigTypeTag(zcu) == .pointer or elem_ty.isPtrLikeOptional(zcu)) { in_memory_result = .{ .double_ptr_to_anyopaque = .{ .actual = inst_ty, @@ -28580,7 +27602,7 @@ fn coerceExtra( target, dest_ty_src, inst_src, - maybe_inst_val, + null, )) { .ok => {}, else => break :p, @@ -28616,16 +27638,14 @@ fn coerceExtra( // empty tuple to zero-length slice // note that this allows coercing to a mutable slice. if (inst_child_ty.structFieldCount(zcu) == 0) { - const align_val = try dest_ty.ptrAlignmentSema(pt); - return Air.internedToRef(try pt.intern(.{ .slice = .{ - .ty = dest_ty.toIntern(), - .ptr = try pt.intern(.{ .ptr = .{ - .ty = dest_ty.slicePtrFieldType(zcu).toIntern(), - .base_addr = .int, - .byte_offset = align_val.toByteUnits().?, - } }), - .len = .zero_usize, - } })); + const empty_array_ty = try pt.arrayType(.{ + .len = 0, + .child = dest_info.child, + .sentinel = dest_info.sentinel, + }); + const empty_array_val = try pt.aggregateValue(empty_array_ty, &.{}); + const empty_array_ptr = try sema.uavRef(empty_array_val); + return sema.coerceArrayPtrToSlice(block, dest_ty, empty_array_ptr, inst_src); } // pointer to tuple to slice @@ -28653,7 +27673,7 @@ fn coerceExtra( target, dest_ty_src, inst_src, - maybe_inst_val, + null, )) { .ok => {}, else => break :p, @@ -28684,12 +27704,12 @@ fn coerceExtra( .int, .comptime_int => { if (maybe_inst_val) |val| { // comptime-known integer to other number - if (!(try sema.intFitsInType(val, dest_ty, null))) { + if (!val.intFitsInType(dest_ty, null, zcu)) { if (!opts.report_err) return error.NotCoercible; return sema.fail(block, inst_src, "type '{f}' cannot represent integer value '{f}'", .{ dest_ty.fmt(pt), val.fmtValueSema(pt, sema) }); } return switch (zcu.intern_pool.indexToKey(val.toIntern())) { - .undef => try pt.undefRef(dest_ty), + .undef => .fromValue(try dest_ty.onePossibleValue(pt) orelse try pt.undefValue(dest_ty)), .int => |int| Air.internedToRef( try zcu.intern_pool.getCoercedInts(gpa, io, pt.tid, int, dest_ty.toIntern()), ), @@ -28717,7 +27737,7 @@ fn coerceExtra( }, .float, .comptime_float => switch (inst_ty.zigTypeTag(zcu)) { .comptime_float => { - const val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined); + const val = sema.resolveValue(inst).?; const result_val = try val.floatCast(dest_ty, pt); return Air.internedToRef(result_val.toIntern()); }, @@ -28768,28 +27788,26 @@ fn coerceExtra( } break :int; }; - const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, pt, .sema); - const fits: bool = switch (ip.indexToKey(result_val.toIntern())) { - else => unreachable, - .undef => true, - .float => |float| fits: { - var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined; - const operand_big_int = val.toBigInt(&buffer, zcu); - switch (float.storage) { - inline else => |x| { - if (!std.math.isFinite(x)) break :fits false; - var result_big_int: std.math.big.int.Mutable = .{ - .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(x)), - .len = undefined, - .positive = undefined, - }; - switch (result_big_int.setFloat(x, .nearest_even)) { - .inexact => break :fits false, - .exact => {}, - } - break :fits result_big_int.toConst().eql(operand_big_int); - }, + if (val.isUndef(zcu)) { + return .fromValue(try pt.undefValue(dest_ty)); + } + const result_val = try pt.floatValue(dest_ty, val.toFloat(f128, zcu)); + const float = ip.indexToKey(result_val.toIntern()).float; + var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined; + const operand_big_int = val.toBigInt(&buffer, zcu); + const fits = switch (float.storage) { + inline else => |x| fits: { + if (!std.math.isFinite(x)) break :fits false; + var result_big_int: std.math.big.int.Mutable = .{ + .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(x)), + .len = undefined, + .positive = undefined, + }; + switch (result_big_int.setFloat(x, .nearest_even)) { + .inexact => break :fits false, + .exact => {}, } + break :fits result_big_int.toConst().eql(operand_big_int); }, }; if (!fits) return sema.fail( @@ -28805,7 +27823,7 @@ fn coerceExtra( .@"enum" => switch (inst_ty.zigTypeTag(zcu)) { .enum_literal => { // enum literal to enum - const val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined); + const val = sema.resolveValue(inst).?; const string = zcu.intern_pool.indexToKey(val.toIntern()).enum_literal; const field_index = dest_ty.enumFieldIndex(string, zcu) orelse { return sema.fail(block, inst_src, "no field named '{f}' in enum '{f}'", .{ @@ -28814,33 +27832,36 @@ fn coerceExtra( }; return Air.internedToRef((try pt.enumValueFieldIndex(dest_ty, @intCast(field_index))).toIntern()); }, - .@"union" => blk: { + .@"union" => if (inst_ty.unionTagType(zcu)) |enum_tag_ty| { // union to its own tag type - const union_tag_ty = inst_ty.unionTagType(zcu) orelse break :blk; - if (union_tag_ty.eql(dest_ty, zcu)) { - return sema.unionToTag(block, dest_ty, inst, inst_src); + if (enum_tag_ty.toIntern() == dest_ty.toIntern()) { + return sema.unionToTag(block, inst); } }, else => {}, }, .error_union => switch (inst_ty.zigTypeTag(zcu)) { - .error_set => { - // E to E!T - return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src); + // E to E!T + .error_set => if (sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src)) |res| { + return res; + } else |err| switch (err) { + error.NotCoercible => if (in_memory_result == .no_match) { + // Try to give more useful notes + const err_set_type = dest_ty.errorUnionSet(zcu); + in_memory_result = try sema.coerceInMemoryAllowed(block, err_set_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val); + }, + else => |e| return e, }, - else => eu: { - // T to E!T - return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src) catch |err| switch (err) { - error.NotCoercible => { - if (in_memory_result == .no_match) { - const payload_type = dest_ty.errorUnionPayload(zcu); - // Try to give more useful notes - in_memory_result = try sema.coerceInMemoryAllowed(block, payload_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val); - } - break :eu; - }, - else => |e| return e, - }; + // T to E!T + else => if (sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src)) |res| { + return res; + } else |err| switch (err) { + error.NotCoercible => if (in_memory_result == .no_match) { + // Try to give more useful notes + const payload_type = dest_ty.errorUnionPayload(zcu); + in_memory_result = try sema.coerceInMemoryAllowed(block, payload_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val); + }, + else => |e| return e, }, }, .@"union" => switch (inst_ty.zigTypeTag(zcu)) { @@ -28858,7 +27879,7 @@ fn coerceExtra( target, dest_ty_src, inst_src, - maybe_inst_val, + null, )) { break :array_to_array; } @@ -28900,18 +27921,16 @@ fn coerceExtra( else => {}, } - const can_coerce_to = switch (dest_ty.zigTypeTag(zcu)) { - .noreturn, .@"opaque" => false, - else => true, + const dest_is_npv = switch (dest_ty.classify(zcu)) { + .no_possible_value => true, + .one_possible_value => if (inst == .undef) { + return .fromValue((try dest_ty.onePossibleValue(pt)).?); + } else false, + .runtime, .fully_comptime, .partially_comptime => if (inst == .undef) { + return .fromValue(try pt.undefValue(dest_ty)); + } else false, }; - if (can_coerce_to) { - // undefined to anything. We do this after the big switch above so that - // special logic has a chance to run first, such as `*[N]T` to `[]T` which - // should initialize the length field of the slice. - if (maybe_inst_val) |val| if (val.toIntern() == .undef) return pt.undefRef(dest_ty); - } - if (!opts.report_err) return error.NotCoercible; if (opts.is_ret and dest_ty.zigTypeTag(zcu) == .noreturn) { @@ -28933,13 +27952,13 @@ fn coerceExtra( const msg = try sema.typeMismatchErrMsg(inst_src, dest_ty, inst_ty); errdefer msg.destroy(sema.gpa); - if (!can_coerce_to) { - try sema.errNote(inst_src, msg, "cannot coerce to '{f}'", .{dest_ty.fmt(pt)}); + if (dest_is_npv) { + try sema.errNote(inst_src, msg, "cannot coerce to uninstantiable type '{f}'", .{dest_ty.fmt(pt)}); } // E!T to T if (inst_ty.zigTypeTag(zcu) == .error_union and - (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(zcu), dest_ty, false, target, dest_ty_src, inst_src, maybe_inst_val)) == .ok) + (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(zcu), dest_ty, false, target, dest_ty_src, inst_src, null)) == .ok) { try sema.errNote(inst_src, msg, "cannot convert error union to payload type", .{}); try sema.errNote(inst_src, msg, "consider using 'try', 'catch', or 'if'", .{}); @@ -28947,7 +27966,7 @@ fn coerceExtra( // ?T to T if (inst_ty.zigTypeTag(zcu) == .optional and - (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(zcu), dest_ty, false, target, dest_ty_src, inst_src, maybe_inst_val)) == .ok) + (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(zcu), dest_ty, false, target, dest_ty_src, inst_src, null)) == .ok) { try sema.errNote(inst_src, msg, "cannot convert optional to payload type", .{}); try sema.errNote(inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{}); @@ -29394,6 +28413,10 @@ pub fn coerceInMemoryAllowed( const pt = sema.pt; const zcu = pt.zcu; + if (src_val) |val| { + assert(val.typeOf(zcu).toIntern() == src_ty.toIntern()); + } + if (dest_ty.eql(src_ty, zcu)) return .ok; @@ -29428,7 +28451,7 @@ pub fn coerceInMemoryAllowed( // Comptime int to regular int. if (dest_tag == .int and src_tag == .comptime_int) { if (src_val) |val| { - if (!(try sema.intFitsInType(val, dest_ty, null))) { + if (!val.intFitsInType(dest_ty, null, zcu)) { return .{ .comptime_int_not_coercible = .{ .wanted = dest_ty, .actual = val } }; } } @@ -29444,17 +28467,13 @@ pub fn coerceInMemoryAllowed( } // Pointers / Pointer-like Optionals - const maybe_dest_ptr_ty = try sema.typePtrOrOptionalPtrTy(dest_ty); - const maybe_src_ptr_ty = try sema.typePtrOrOptionalPtrTy(src_ty); - if (maybe_dest_ptr_ty) |dest_ptr_ty| { - if (maybe_src_ptr_ty) |src_ptr_ty| { - return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target, dest_src, src_src); - } + if (dest_ty.isPtrAtRuntime(zcu) and src_ty.isPtrAtRuntime(zcu)) { + return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src); } // Slices if (dest_ty.isSlice(zcu) and src_ty.isSlice(zcu)) { - return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src); + return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src); } // Functions @@ -29554,7 +28573,8 @@ pub fn coerceInMemoryAllowed( // Optionals if (dest_tag == .optional and src_tag == .optional) { - if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) { + if (dest_ty.isPtrAtRuntime(zcu) or src_ty.isPtrAtRuntime(zcu)) { + // Only one is, because we already handled when both are. return .{ .optional_shape = .{ .actual = src_ty, .wanted = dest_ty, @@ -29581,7 +28601,6 @@ pub fn coerceInMemoryAllowed( const field_count = dest_ty.structFieldCount(zcu); for (0..field_count) |field_idx| { if (dest_ty.structFieldIsComptime(field_idx, zcu) != src_ty.structFieldIsComptime(field_idx, zcu)) break :tuple; - if (dest_ty.fieldAlignment(field_idx, zcu) != src_ty.fieldAlignment(field_idx, zcu)) break :tuple; const dest_field_ty = dest_ty.fieldType(field_idx, zcu); const src_field_ty = src_ty.fieldType(field_idx, zcu); const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src, null); @@ -29609,89 +28628,62 @@ fn coerceInMemoryAllowedErrorSets( const gpa = sema.gpa; const ip = &zcu.intern_pool; - // Coercion to `anyerror`. Note that this check can return false negatives - // in case the error sets did not get resolved. - if (dest_ty.isAnyError(zcu)) { - return .ok; - } - - if (dest_ty.toIntern() == .adhoc_inferred_error_set_type) { - // We are trying to coerce an error set to the current function's - // inferred error set. - const dst_ies = sema.fn_ret_ty_ies.?; - try dst_ies.addErrorSet(src_ty, ip, sema.arena); - return .ok; - } - - if (ip.isInferredErrorSetType(dest_ty.toIntern())) { - const dst_ies_func_index = ip.iesFuncIndex(dest_ty.toIntern()); - if (sema.fn_ret_ty_ies) |dst_ies| { - if (dst_ies.func == dst_ies_func_index) { - // We are trying to coerce an error set to the current function's - // inferred error set. - try dst_ies.addErrorSet(src_ty, ip, sema.arena); - return .ok; - } - } - switch (try sema.resolveInferredErrorSet(block, dest_src, dest_ty.toIntern())) { - // isAnyError might have changed from a false negative to a true - // positive after resolution. - .anyerror_type => return .ok, - else => {}, - } - } - - var missing_error_buf = std.array_list.Managed(InternPool.NullTerminatedString).init(gpa); - defer missing_error_buf.deinit(); - - switch (src_ty.toIntern()) { - .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) { - .simple_type => unreachable, // filtered out above - .error_set_type, .inferred_error_set_type => return .from_anyerror, - else => unreachable, + const dest_set: InternPool.Key.ErrorSetType = err_set: switch (dest_ty.toIntern()) { + .anyerror_type => return .ok, + .adhoc_inferred_error_set_type => { + // We are trying to coerce an error set to the current function's + // inferred error set. + const dst_ies = sema.fn_ret_ty_ies.?; + try dst_ies.addErrorSet(src_ty, ip, sema.arena); + return .ok; }, - - else => switch (ip.indexToKey(src_ty.toIntern())) { - .inferred_error_set_type => { - const resolved_src_ty = try sema.resolveInferredErrorSet(block, src_src, src_ty.toIntern()); - // src anyerror status might have changed after the resolution. - if (resolved_src_ty == .anyerror_type) { - // dest_ty.isAnyError(zcu) == true is already checked for at this point. - return .from_anyerror; - } - - for (ip.indexToKey(resolved_src_ty).error_set_type.names.get(ip)) |key| { - if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), key)) { - try missing_error_buf.append(key); + else => |err_set_ty| switch (ip.indexToKey(err_set_ty)) { + .inferred_error_set_type => |func_index| { + if (sema.fn_ret_ty_ies) |dst_ies| { + if (dst_ies.func == func_index) { + // We are trying to coerce an error set to the current function's + // inferred error set. + try dst_ies.addErrorSet(src_ty, ip, sema.arena); + return .ok; } } - - if (missing_error_buf.items.len != 0) { - return InMemoryCoercionResult{ - .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items), - }; - } - - return .ok; + try sema.ensureFuncIesResolved(block, dest_src, func_index); + continue :err_set ip.funcIesResolvedUnordered(func_index); }, - .error_set_type => |error_set_type| { - for (error_set_type.names.get(ip)) |name| { - if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), name)) { - try missing_error_buf.append(name); - } - } - - if (missing_error_buf.items.len != 0) { - return InMemoryCoercionResult{ - .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items), - }; - } + .error_set_type => |err_set| err_set, + else => unreachable, + }, + }; - return .ok; + const src_names: InternPool.NullTerminatedString.Slice = err_set: switch (src_ty.toIntern()) { + .anyerror_type => return .from_anyerror, + else => |err_set_ty| switch (ip.indexToKey(err_set_ty)) { + .inferred_error_set_type => |func_index| { + try sema.ensureFuncIesResolved(block, src_src, func_index); + continue :err_set ip.funcIesResolvedUnordered(func_index); }, + .error_set_type => |err_set| err_set.names, else => unreachable, }, + }; + + var missing_error_buf: std.ArrayList(InternPool.NullTerminatedString) = .empty; + defer missing_error_buf.deinit(gpa); + + for (src_names.get(ip)) |name| { + if (dest_set.nameIndex(ip, name) == null) { + try missing_error_buf.append(gpa, name); + } + } + + if (missing_error_buf.items.len != 0) { + return .{ .missing_error = try sema.arena.dupe( + InternPool.NullTerminatedString, + missing_error_buf.items, + ) }; } + + return .ok; } fn coerceInMemoryAllowedFns( @@ -29714,11 +28706,7 @@ fn coerceInMemoryAllowedFns( { if (dest_info.is_var_args != src_info.is_var_args) { - return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args }; - } - - if (dest_info.is_generic != src_info.is_generic) { - return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic }; + return .{ .fn_var_args = dest_info.is_var_args }; } const callconv_ok = callconvCoerceAllowed(target, src_info.cc, dest_info.cc) and @@ -29731,6 +28719,12 @@ fn coerceInMemoryAllowedFns( } }; } + try sema.ensureLayoutResolved(src_ty, src_src, .coerce); + try sema.ensureLayoutResolved(dest_ty, dest_src, .coerce); + const src_is_runtime = src_ty.fnHasRuntimeBits(zcu); + const dest_is_runtime = dest_ty.fnHasRuntimeBits(zcu); + if (src_is_runtime != dest_is_runtime) return .{ .fn_generic = !dest_is_runtime }; + if (!switch (src_info.return_type) { .generic_poison_type => true, .noreturn_type => !dest_is_mut, @@ -29780,7 +28774,7 @@ fn coerceInMemoryAllowedFns( const src_is_comptime = src_info.paramIsComptime(@intCast(param_i)); const dest_is_comptime = dest_info.paramIsComptime(@intCast(param_i)); if (src_is_comptime == dest_is_comptime) break :comptime_param; - if (!dest_is_mut and src_is_comptime and !dest_is_comptime and try dest_param_ty.comptimeOnlySema(pt)) { + if (!dest_is_mut and src_is_comptime and !dest_is_comptime and dest_param_ty.comptimeOnly(zcu)) { // A parameter which is marked `comptime` can drop that annotation if the type is comptime-only. // The function remains generic, and the parameter is going to be comptime-resolved either way, // so this just affects whether or not the argument is comptime-evaluated at the call site. @@ -29861,8 +28855,6 @@ fn coerceInMemoryAllowedPtrs( block: *Block, dest_ty: Type, src_ty: Type, - dest_ptr_ty: Type, - src_ptr_ty: Type, /// If set, the coercion must be valid in both directions. dest_is_mut: bool, target: *const std.Target, @@ -29875,8 +28867,8 @@ fn coerceInMemoryAllowedPtrs( const gpa = comp.gpa; const io = comp.io; - const dest_info = dest_ptr_ty.ptrInfo(zcu); - const src_info = src_ptr_ty.ptrInfo(zcu); + const dest_info = dest_ty.ptrInfo(zcu); + const src_info = src_ty.ptrInfo(zcu); const ok_ptr_size = src_info.flags.size == dest_info.flags.size or src_info.flags.size == .c or dest_info.flags.size == .c; @@ -30008,16 +29000,14 @@ fn coerceInMemoryAllowedPtrs( if (src_info.flags.alignment != .none or dest_info.flags.alignment != .none or dest_info.child != src_info.child) { - const src_align = if (src_info.flags.alignment != .none) - src_info.flags.alignment - else - try Type.fromInterned(src_info.child).abiAlignmentSema(pt); - - const dest_align = if (dest_info.flags.alignment != .none) - dest_info.flags.alignment - else - try Type.fromInterned(dest_info.child).abiAlignmentSema(pt); - + const src_align = if (src_info.flags.alignment == .none) a: { + try sema.ensureLayoutResolved(src_child, src_src, .align_check); + break :a src_child.abiAlignment(zcu); + } else src_info.flags.alignment; + const dest_align = if (dest_info.flags.alignment == .none) a: { + try sema.ensureLayoutResolved(dest_child, dest_src, .align_check); + break :a dest_child.abiAlignment(zcu); + } else dest_info.flags.alignment; if (dest_align.compare(if (dest_is_mut) .neq else .gt, src_align)) { return InMemoryCoercionResult{ .ptr_alignment = .{ .actual = src_align, @@ -30049,7 +29039,7 @@ fn coerceVarArgParam( .{}, ), .@"fn" => fn_ptr: { - const fn_val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined); + const fn_val = sema.resolveValue(inst).?; const fn_nav = zcu.funcInfo(fn_val.toIntern()).owner_nav; break :fn_ptr try sema.analyzeNavRef(block, inst_src, fn_nav); }, @@ -30066,7 +29056,7 @@ fn coerceVarArgParam( } }, else => if (uncasted_ty.isAbiInt(zcu)) int: { - if (!try sema.validateExternType(uncasted_ty, .param_ty)) break :int inst; + if (!uncasted_ty.validateExtern(.param_ty, zcu)) break :int inst; const target = zcu.getTarget(); const uncasted_info = uncasted_ty.intInfo(zcu); if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) { @@ -30095,7 +29085,7 @@ fn coerceVarArgParam( }; const coerced_ty = sema.typeOf(coerced); - if (!try sema.validateExternType(coerced_ty, .param_ty)) { + if (!coerced_ty.validateExtern(.param_ty, zcu)) { const msg = msg: { const msg = try sema.errMsg(inst_src, "cannot pass '{f}' to variadic function", .{coerced_ty.fmt(pt)}); errdefer msg.destroy(sema.gpa); @@ -30140,38 +29130,20 @@ fn storePtr2( const elem_ty = ptr_ty.childType(zcu); - // To generate better code for tuples, we detect a tuple operand here, and - // analyze field loads and stores directly. This avoids an extra allocation + memcpy - // which would occur if we used `coerce`. - // However, we avoid this mechanism if the destination element type is a tuple, - // because the regular store will be better for this case. - // If the destination type is a struct we don't want this mechanism to trigger, because - // this code does not handle tuple-to-struct coercion which requires dealing with missing - // fields. - const operand_ty = sema.typeOf(uncasted_operand); - if (operand_ty.isTuple(zcu) and elem_ty.zigTypeTag(zcu) == .array) { - const field_count = operand_ty.structFieldCount(zcu); - var i: u32 = 0; - while (i < field_count) : (i += 1) { - const elem_src = operand_src; // TODO better source location - const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i); - const elem_index = try pt.intRef(.usize, i); - const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false, true); - try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store); - } - return; - } - - // TODO do the same thing for anon structs as for tuples above. - // However, beware of the need to handle missing/extra fields. - const is_ret = air_tag == .ret_ptr; const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) { error.NotCoercible => unreachable, else => |e| return e, }; - const maybe_operand_val = try sema.resolveValue(operand); + const maybe_operand_val = sema.resolveValue(operand); + + const comptime_only = switch (elem_ty.classify(zcu)) { + .no_possible_value => unreachable, // the coercion should have failed + .one_possible_value => return, // no actual store operation is necessary + .runtime => false, + .partially_comptime, .fully_comptime => true, + }; const runtime_src = rs: { const ptr_val = try sema.resolveDefinedValue(block, ptr_src, ptr) orelse break :rs ptr_src; @@ -30180,22 +29152,13 @@ fn storePtr2( return sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty); }; - // We're performing the store at runtime; as such, we need to make sure the pointee type - // is not comptime-only. We can hit this case with a `@ptrFromInt` pointer. - if (try elem_ty.comptimeOnlySema(pt)) { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "cannot store comptime-only type '{f}' at runtime", .{elem_ty.fmt(pt)}); - errdefer msg.destroy(sema.gpa); - try sema.errNote(ptr_src, msg, "operation is runtime due to this pointer", .{}); - break :msg msg; - }); - } - - // We do this after the possible comptime store above, for the case of field_ptr stores - // to unions because we want the comptime tag to be set, even if the field type is void. - if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) { - return; - } + // We're performing the store at runtime, so the pointee type must not be comptime-only. + if (comptime_only) return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(src, "cannot store comptime-only type '{f}' at runtime", .{elem_ty.fmt(pt)}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(ptr_src, msg, "operation is runtime due to this pointer", .{}); + break :msg msg; + }); try sema.requireRuntimeBlock(block, src, runtime_src); @@ -30223,7 +29186,7 @@ fn checkComptimeKnownStore(sema: *Sema, block: *Block, store_inst_ref: Air.Inst. const maybe_base_alloc = sema.base_allocs.get(ptr) orelse break :known; const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(maybe_base_alloc) orelse break :known; - if ((try sema.resolveValue(operand)) != null and + if (sema.resolveValue(operand) != null and block.runtime_index == maybe_comptime_alloc.runtime_index) { try maybe_comptime_alloc.stores.append(sema.arena, .{ @@ -30272,7 +29235,7 @@ fn checkKnownAllocPtr(sema: *Sema, block: *Block, base_ptr: Air.Inst.Ref, new_pt // If the index value is runtime-known, this pointer is also runtime-known, so // we must in turn make the alloc value runtime-known. - if (null == try sema.resolveValue(index_ref)) { + if (null == sema.resolveValue(index_ref)) { try sema.markMaybeComptimeAllocRuntime(block, alloc_inst); } }, @@ -30361,10 +29324,10 @@ fn bitCast( ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; - try dest_ty.resolveLayout(pt); - const old_ty = sema.typeOf(inst); - try old_ty.resolveLayout(pt); + + old_ty.assertHasLayout(zcu); + try sema.ensureLayoutResolved(dest_ty, inst_src, .init); const dest_bits = dest_ty.bitSize(zcu); const old_bits = old_ty.bitSize(zcu); @@ -30378,7 +29341,7 @@ fn bitCast( }); } - if (try sema.resolveValue(inst)) |val| { + if (sema.resolveValue(inst)) |val| { if (val.isUndef(zcu)) return pt.undefRef(dest_ty); if (old_ty.zigTypeTag(zcu) == .error_set and dest_ty.zigTypeTag(zcu) == .error_set) { @@ -30404,7 +29367,7 @@ fn coerceArrayPtrToSlice( ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; - if (try sema.resolveValue(inst)) |val| { + if (sema.resolveValue(inst)) |val| { const ptr_array_ty = sema.typeOf(inst); const array_ty = ptr_array_ty.childType(zcu); const slice_ptr_ty = dest_ty.slicePtrFieldType(zcu); @@ -30499,7 +29462,7 @@ fn coerceCompatiblePtrs( const pt = sema.pt; const zcu = pt.zcu; const inst_ty = sema.typeOf(inst); - if (try sema.resolveValue(inst)) |val| { + if (sema.resolveValue(inst)) |val| { if (!val.isUndef(zcu) and val.isNull(zcu) and !dest_ty.isAllowzeroPtr(zcu)) { return sema.fail(block, inst_src, "null pointer casted to type '{f}'", .{dest_ty.fmt(pt)}); } @@ -30510,9 +29473,7 @@ fn coerceCompatiblePtrs( } try sema.requireRuntimeBlock(block, inst_src, null); const inst_allows_zero = inst_ty.zigTypeTag(zcu) != .pointer or inst_ty.ptrAllowsZero(zcu); - if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero(zcu) and - (try dest_ty.elemType2(zcu).hasRuntimeBitsSema(pt) or dest_ty.elemType2(zcu).zigTypeTag(zcu) == .@"fn")) - { + if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero(zcu)) { try sema.checkLogicalPtrOperation(block, inst_src, inst_ty); const actual_ptr = if (inst_ty.isSlice(zcu)) try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty) @@ -30532,6 +29493,7 @@ fn coerceCompatiblePtrs( return new_ptr; } +/// Asserts that the layout of `union_ty` is already resolved. fn coerceEnumToUnion( sema: *Sema, block: *Block, @@ -30545,18 +29507,21 @@ fn coerceEnumToUnion( const ip = &zcu.intern_pool; const inst_ty = sema.typeOf(inst); - const tag_ty = union_ty.unionTagType(zcu) orelse { - const msg = msg: { - const msg = try sema.typeMismatchErrMsg(inst_src, union_ty, inst_ty); - errdefer msg.destroy(sema.gpa); - try sema.errNote(union_ty_src, msg, "cannot coerce enum to untagged union", .{}); - try sema.addDeclaredHereNote(msg, union_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - }; + union_ty.assertHasLayout(zcu); + + const union_obj = zcu.typeToUnion(union_ty).?; + const enum_ty: Type = .fromInterned(union_obj.enum_tag_type); + const enum_obj = ip.loadEnumType(enum_ty.toIntern()); + + if (union_obj.tag_usage != .tagged) return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.typeMismatchErrMsg(inst_src, union_ty, inst_ty); + errdefer msg.destroy(sema.gpa); + try sema.errNote(union_ty_src, msg, "cannot coerce enum to untagged union", .{}); + try sema.addDeclaredHereNote(msg, union_ty); + break :msg msg; + }); - const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src); + const enum_tag = try sema.coerce(block, enum_ty, inst, inst_src); if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| { const field_index = union_ty.unionTagFieldIndex(val, pt.zcu) orelse { return sema.fail(block, inst_src, "union '{f}' has no tag with value '{f}'", .{ @@ -30564,101 +29529,88 @@ fn coerceEnumToUnion( }); }; - const union_obj = zcu.typeToUnion(union_ty).?; + const field_name = enum_obj.field_names.get(ip)[field_index]; const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); - try field_ty.resolveFields(pt); - if (field_ty.zigTypeTag(zcu) == .noreturn) { - const msg = msg: { - const msg = try sema.errMsg(inst_src, "cannot initialize 'noreturn' field of union", .{}); + switch (field_ty.classify(zcu)) { + .one_possible_value => return .fromValue(try pt.unionValue( + union_ty, + val, + (try field_ty.onePossibleValue(pt)).?, + )), + + .no_possible_value => return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(inst_src, "cannot initialize union field with uninstantiable type '{f}'", .{field_ty.fmt(pt)}); errdefer msg.destroy(sema.gpa); - - const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index]; try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{ field_name.fmt(ip), }); try sema.addDeclaredHereNote(msg, union_ty); break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - const opv = (try sema.typeHasOnePossibleValue(field_ty)) orelse { - const msg = msg: { - const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index]; + }), + + else => return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(inst_src, "coercion from enum '{f}' to union '{f}' must initialize '{f}' field '{f}'", .{ inst_ty.fmt(pt), union_ty.fmt(pt), field_ty.fmt(pt), field_name.fmt(ip), }); errdefer msg.destroy(sema.gpa); - try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{ - field_name.fmt(ip), - }); + try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{field_name.fmt(ip)}); try sema.addDeclaredHereNote(msg, union_ty); break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - }; - - return Air.internedToRef((try pt.unionValue(union_ty, val, opv)).toIntern()); + }), + } } try sema.requireRuntimeBlock(block, inst_src, null); - if (tag_ty.isNonexhaustiveEnum(zcu)) { + if (enum_ty.isNonexhaustiveEnum(zcu)) { const msg = msg: { const msg = try sema.errMsg(inst_src, "runtime coercion to union '{f}' from non-exhaustive enum", .{ union_ty.fmt(pt), }); errdefer msg.destroy(sema.gpa); - try sema.addDeclaredHereNote(msg, tag_ty); + try sema.addDeclaredHereNote(msg, enum_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(block, msg); } - const union_obj = zcu.typeToUnion(union_ty).?; - { - var msg: ?*Zcu.ErrorMsg = null; - errdefer if (msg) |some| some.destroy(sema.gpa); - - for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| { - if (Type.fromInterned(field_ty).zigTypeTag(zcu) == .noreturn) { - const err_msg = msg orelse try sema.errMsg( - inst_src, - "runtime coercion from enum '{f}' to union '{f}' which has a 'noreturn' field", - .{ tag_ty.fmt(pt), union_ty.fmt(pt) }, - ); - msg = err_msg; - - try sema.addFieldErrNote(union_ty, field_index, err_msg, "'noreturn' field here", .{}); - } - } - if (msg) |some| { - msg = null; - try sema.addDeclaredHereNote(some, union_ty); - return sema.failWithOwnedErrorMsg(block, some); + for (union_obj.field_types.get(ip)) |field_ty_ip| { + if (Type.fromInterned(field_ty_ip).classify(zcu) != .one_possible_value) break; + } else { + // All fields are OPV, so the coercion is okay. + if (try union_ty.onePossibleValue(pt)) |opv| { + // The tag had redundant bits, but we've omitted the tag from the union's runtime layout, so the union is OPV and hence runtime-known. + return .fromValue(opv); + } else { + // The union layout is just the tag, so we can bitcast the enum straight to the union. + return block.addBitCast(union_ty, enum_tag); } } - // If the union has all fields 0 bits, the union value is just the enum value. - if (union_ty.unionHasAllZeroBitFieldTypes(zcu)) { - return block.addBitCast(union_ty, enum_tag); - } + // The coercion is invalid because one or more fields is not OPV. const msg = msg: { const msg = try sema.errMsg( inst_src, "runtime coercion from enum '{f}' to union '{f}' which has non-void fields", - .{ tag_ty.fmt(pt), union_ty.fmt(pt) }, + .{ enum_ty.fmt(pt), union_ty.fmt(pt) }, ); errdefer msg.destroy(sema.gpa); for (0..union_obj.field_types.len) |field_index| { - const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index]; + const field_name = enum_obj.field_names.get(ip)[field_index]; const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); - if (!(try field_ty.hasRuntimeBitsSema(pt))) continue; - try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' has type '{f}'", .{ + const ty_description: []const u8 = switch (field_ty.classify(zcu)) { + .one_possible_value => continue, + .no_possible_value => "uninstantiable type", + else => "type", + }; + if (field_ty.classify(zcu) == .one_possible_value) continue; + try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' has {s} '{f}'", .{ field_name.fmt(ip), + ty_description, field_ty.fmt(pt), }); } @@ -30685,7 +29637,7 @@ fn coerceArrayLike( // try coercion of the whole array const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src, null); if (in_memory_result == .ok) { - if (try sema.resolveValue(inst)) |inst_val| { + if (sema.resolveValue(inst)) |inst_val| { // These types share the same comptime value representation. return sema.coerceInMemory(inst_val, dest_ty); } @@ -30708,7 +29660,7 @@ fn coerceArrayLike( } const dest_elem_ty = dest_ty.childType(zcu); - if (dest_ty.isVector(zcu) and inst_ty.isVector(zcu) and (try sema.resolveValue(inst)) == null) { + if (dest_ty.isVector(zcu) and inst_ty.isVector(zcu) and sema.resolveValue(inst) == null) { const inst_elem_ty = inst_ty.childType(zcu); switch (dest_elem_ty.zigTypeTag(zcu)) { .int => if (inst_elem_ty.isInt(zcu)) { @@ -30748,7 +29700,7 @@ fn coerceArrayLike( const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); ref.* = coerced; if (runtime_src == null) { - if (try sema.resolveValue(coerced)) |elem_val| { + if (sema.resolveValue(coerced)) |elem_val| { val.* = elem_val.toIntern(); } else { runtime_src = elem_src; @@ -30809,7 +29761,7 @@ fn coerceTupleToArray( const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); ref.* = coerced; if (runtime_src == null) { - if (try sema.resolveValue(coerced)) |elem_val| { + if (sema.resolveValue(coerced)) |elem_val| { val.* = elem_val.toIntern(); } else { runtime_src = elem_src; @@ -30845,10 +29797,7 @@ fn coerceTupleToSlicePtrs( .child = slice_info.child, }); const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src); - if (slice_info.flags.alignment != .none) { - return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{}); - } - const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst); + const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst, slice_info.flags.alignment); return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src); } @@ -30867,10 +29816,7 @@ fn coerceTupleToArrayPtrs( const ptr_info = ptr_array_ty.ptrInfo(zcu); const array_ty: Type = .fromInterned(ptr_info.child); const array_inst = try sema.coerceTupleToArray(block, array_ty, array_ty_src, tuple, tuple_src); - if (ptr_info.flags.alignment != .none) { - return sema.fail(block, array_ty_src, "TODO: override the alignment of the array decl we create here", .{}); - } - const ptr_array = try sema.analyzeRef(block, array_ty_src, array_inst); + const ptr_array = try sema.analyzeRef(block, array_ty_src, array_inst, ptr_info.flags.alignment); return ptr_array; } @@ -30904,24 +29850,21 @@ fn coerceTupleToTuple( const field_i: u32 = @intCast(field_index_usize); const field_src = inst_src; // TODO better source location - const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) { - .tuple_type => |tuple_type| tuple_type.types.get(ip)[field_index_usize], - .struct_type => ip.loadStructType(tuple_ty.toIntern()).field_types.get(ip)[field_index_usize], - else => unreachable, - }; - const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { - .tuple_type => |tuple_type| tuple_type.values.get(ip)[field_index_usize], - .struct_type => ip.loadStructType(tuple_ty.toIntern()).fieldInit(ip, field_index_usize), - else => unreachable, - }; - const field_index: u32 = @intCast(field_index_usize); + const field_ty, const default_val = field: { + const tuple_type = ip.indexToKey(tuple_ty.toIntern()).tuple_type; + break :field .{ + tuple_type.types.get(ip)[field_index], + tuple_type.values.get(ip)[field_index], + }; + }; + const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i); const coerced = try sema.coerce(block, .fromInterned(field_ty), elem_ref, field_src); field_refs[field_index] = coerced; if (default_val != .none) { - const init_val = (try sema.resolveValue(coerced)) orelse { + const init_val = sema.resolveValue(coerced) orelse { return sema.failWithNeededComptime(block, field_src, .{ .simple = .stored_to_comptime_field }); }; @@ -30930,7 +29873,7 @@ fn coerceTupleToTuple( } } if (runtime_src == null) { - if (try sema.resolveValue(coerced)) |field_val| { + if (sema.resolveValue(coerced)) |field_val| { field_vals[field_index] = field_val.toIntern(); } else { runtime_src = field_src; @@ -30946,11 +29889,7 @@ fn coerceTupleToTuple( const i: u32 = @intCast(i_usize); if (field_ref.* != .none) continue; - const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { - .tuple_type => |tuple_type| tuple_type.values.get(ip)[i], - .struct_type => ip.loadStructType(tuple_ty.toIntern()).fieldInit(ip, i), - else => unreachable, - }; + const default_val = ip.indexToKey(tuple_ty.toIntern()).tuple_type.values.get(ip)[i]; const field_src = inst_src; // TODO better source location if (default_val == .none) { @@ -30993,7 +29932,7 @@ fn analyzeNavVal( return sema.analyzeLoad(block, src, ref, src); } -fn addReferenceEntry( +pub fn addReferenceEntry( sema: *Sema, opt_block: ?*Block, src: LazySrcLoc, @@ -31005,7 +29944,6 @@ fn addReferenceEntry( .func => |f| assert(ip.unwrapCoercedFunc(f) == f), // for `.{ .func = f }`, `f` must be uncoerced else => {}, } - if (!zcu.comp.config.incremental and zcu.comp.reference_trace == 0) return; const gop = try sema.references.getOrPut(sema.gpa, referenced_unit); if (gop.found_existing) return; try zcu.addUnitReference(sema.owner, referenced_unit, src, inline_frame: { @@ -31019,13 +29957,12 @@ fn addReferenceEntry( pub fn addTypeReferenceEntry( sema: *Sema, src: LazySrcLoc, - referenced_type: InternPool.Index, + referenced_type: Type, ) !void { const zcu = sema.pt.zcu; - if (!zcu.comp.config.incremental and zcu.comp.reference_trace == 0) return; - const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type); + const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type.toIntern()); if (gop.found_existing) return; - try zcu.addTypeReference(sema.owner, referenced_type, src); + try zcu.addTypeReference(sema.owner, referenced_type.toIntern(), src); } fn ensureMemoizedStateResolved(sema: *Sema, src: LazySrcLoc, stage: InternPool.MemoizedStateStage) SemaError!void { @@ -31035,10 +29972,11 @@ fn ensureMemoizedStateResolved(sema: *Sema, src: LazySrcLoc, stage: InternPool.M try sema.addReferenceEntry(null, src, unit); try sema.declareDependency(.{ .memoized_state = stage }); + const reason: Zcu.DependencyReason = .{ .src = src, .type_layout_reason = undefined }; if (pt.zcu.analysis_in_progress.contains(unit)) { - return sema.failWithOwnedErrorMsg(null, try sema.errMsg(src, "dependency loop detected", .{})); + return sema.failWithDependencyLoop(unit, &reason); } - try pt.ensureMemoizedStateUpToDate(stage); + try pt.ensureMemoizedStateUpToDate(stage, &reason); } pub fn ensureNavResolved(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index, kind: enum { type, fully }) CompileError!void { @@ -31052,11 +29990,6 @@ pub fn ensureNavResolved(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: return; } - try sema.declareDependency(switch (kind) { - .type => .{ .nav_ty = nav_index }, - .fully => .{ .nav_val = nav_index }, - }); - // Note that even if `nav.status == .resolved`, we must still trigger `ensureNavValUpToDate` // to make sure the value is up-to-date on incremental updates. @@ -31065,32 +29998,37 @@ pub fn ensureNavResolved(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: .fully => .{ .nav_val = nav_index }, }); try sema.addReferenceEntry(block, src, anal_unit); + try sema.declareDependency(switch (kind) { + .type => .{ .nav_ty = nav_index }, + .fully => .{ .nav_val = nav_index }, + }); + + const reason: Zcu.DependencyReason = .{ .src = src, .type_layout_reason = undefined }; if (zcu.analysis_in_progress.contains(anal_unit)) { - return sema.failWithOwnedErrorMsg(null, try sema.errMsg(.{ - .base_node_inst = nav.analysis.?.zir_index, - .offset = LazySrcLoc.Offset.nodeOffset(.zero), - }, "dependency loop detected", .{})); + return sema.failWithDependencyLoop(anal_unit, &reason); } switch (kind) { .type => { try zcu.ensureNavValAnalysisQueued(nav_index); - return pt.ensureNavTypeUpToDate(nav_index); + return pt.ensureNavTypeUpToDate(nav_index, &reason); }, - .fully => return pt.ensureNavValUpToDate(nav_index), + .fully => return pt.ensureNavValUpToDate(nav_index, &reason), } } fn optRefValue(sema: *Sema, opt_val: ?Value) !Value { const pt = sema.pt; const ptr_anyopaque_ty = try pt.singleConstPtrType(.anyopaque); - return Value.fromInterned(try pt.intern(.{ .opt = .{ - .ty = (try pt.optionalType(ptr_anyopaque_ty.toIntern())).toIntern(), - .val = if (opt_val) |val| (try pt.getCoerced( - Value.fromInterned(try pt.refValue(val.toIntern())), - ptr_anyopaque_ty, - )).toIntern() else .none, + const opt_ptr_anyopaque_ty = try pt.optionalType(ptr_anyopaque_ty.toIntern()); + return .fromInterned(try pt.intern(.{ .opt = .{ + .ty = opt_ptr_anyopaque_ty.toIntern(), + .val = payload: { + const val = opt_val orelse break :payload .none; + const ptr_val = try pt.getCoerced(try pt.uavValue(val), ptr_anyopaque_ty); + break :payload ptr_val.toIntern(); + }, } })); } @@ -31143,7 +30081,7 @@ fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_inde .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const }, .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", r.is_const }, }; - const ptr_ty = try pt.ptrTypeSema(.{ + const ptr_ty = try pt.ptrType(.{ .child = ty, .flags = .{ .alignment = alignment, @@ -31185,7 +30123,7 @@ fn maybeQueueFuncBodyAnalysis(sema: *Sema, block: *Block, src: LazySrcLoc, nav_i try sema.ensureNavResolved(block, src, nav_index, .type); const nav_ty: Type = .fromInterned(ip.getNav(nav_index).typeOf(ip)); if (nav_ty.zigTypeTag(zcu) != .@"fn") return; - if (!try nav_ty.fnHasRuntimeBitsSema(pt)) return; + if (!nav_ty.fnHasRuntimeBits(zcu)) return; try sema.ensureNavResolved(block, src, nav_index, .fully); const nav_val = zcu.navValue(nav_index); @@ -31201,34 +30139,48 @@ fn analyzeRef( block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref, + alignment: Alignment, ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; const operand_ty = sema.typeOf(operand); - if (try sema.resolveValue(operand)) |val| { + const address_space = target_util.defaultAddressSpace(zcu.getTarget(), .local); + const ptr_type = try pt.ptrType(.{ + .child = operand_ty.toIntern(), + .flags = .{ + .alignment = alignment, + .is_const = true, + .address_space = address_space, + }, + }); + + if (sema.resolveValue(operand)) |val| { switch (zcu.intern_pool.indexToKey(val.toIntern())) { .@"extern" => |e| return sema.analyzeNavRef(block, src, e.owner_nav), .func => |f| return sema.analyzeNavRef(block, src, f.owner_nav), - else => return uavRef(sema, val.toIntern()), + else => return .fromIntern(try pt.intern(.{ .ptr = .{ + .ty = ptr_type.toIntern(), + .base_addr = .{ .uav = .{ + .val = val.toIntern(), + .orig_ty = ptr_type.toIntern(), + } }, + .byte_offset = 0, + } })), } } // No `requireRuntimeBlock`; it's okay to `ref` to a runtime value in a comptime context, // it's just that we can only use the *type* of the result, since the value is runtime-known. - const address_space = target_util.defaultAddressSpace(zcu.getTarget(), .local); - const ptr_type = try pt.ptrTypeSema(.{ + const mut_ptr_type = try pt.ptrType(.{ .child = operand_ty.toIntern(), .flags = .{ - .is_const = true, + .alignment = alignment, + .is_const = false, .address_space = address_space, }, }); - const mut_ptr_type = try pt.ptrTypeSema(.{ - .child = operand_ty.toIntern(), - .flags = .{ .address_space = address_space }, - }); const alloc = try block.addTy(.alloc, mut_ptr_type); // In a comptime context, the store would fail, since the operand is runtime-known. But that's @@ -31257,13 +30209,18 @@ fn analyzeLoad( .pointer => ptr_ty.childType(zcu), else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ty.fmt(pt)}), }; - if (elem_ty.zigTypeTag(zcu) == .@"opaque") { - return sema.fail(block, ptr_src, "cannot load opaque type '{f}'", .{elem_ty.fmt(pt)}); - } - if (try sema.typeHasOnePossibleValue(elem_ty)) |opv| { - return Air.internedToRef(opv.toIntern()); - } + try sema.ensureLayoutResolved(elem_ty, src, .ptr_access); + + const comptime_only = switch (elem_ty.classify(zcu)) { + .no_possible_value => switch (elem_ty.zigTypeTag(zcu)) { + .@"opaque" => return sema.fail(block, src, "cannot load opaque type '{f}'", .{elem_ty.fmt(pt)}), + else => return sema.fail(block, src, "cannot load uninstantiable type '{f}'", .{elem_ty.fmt(pt)}), + }, + .one_possible_value => return .fromValue((try elem_ty.onePossibleValue(pt)).?), + .runtime => false, + .partially_comptime, .fully_comptime => true, + }; if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |elem_val| { @@ -31271,6 +30228,13 @@ fn analyzeLoad( } } + if (comptime_only) return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(src, "cannot load comptime-only type '{f}'", .{elem_ty.fmt(pt)}); + errdefer msg.destroy(zcu.gpa); + try sema.errNote(ptr_src, msg, "pointer of type '{f}' is runtime-known", .{ptr_ty.fmt(pt)}); + break :msg msg; + }); + return block.addTyOp(.load, elem_ty, ptr); } @@ -31284,7 +30248,7 @@ fn analyzeSlicePtr( const pt = sema.pt; const zcu = pt.zcu; const result_ty = slice_ty.slicePtrFieldType(zcu); - if (try sema.resolveValue(slice)) |val| { + if (sema.resolveValue(slice)) |val| { if (val.isUndef(zcu)) return pt.undefRef(result_ty); return Air.internedToRef(val.slicePtr(zcu).toIntern()); } @@ -31304,7 +30268,7 @@ fn analyzeOptionalSlicePtr( const slice_ty = opt_slice_ty.optionalChild(zcu); const result_ty = slice_ty.slicePtrFieldType(zcu); - if (try sema.resolveValue(opt_slice)) |opt_val| { + if (sema.resolveValue(opt_slice)) |opt_val| { if (opt_val.isUndef(zcu)) return pt.undefRef(result_ty); const slice_ptr: InternPool.Index = if (opt_val.optionalValue(zcu)) |val| val.slicePtr(zcu).toIntern() @@ -31328,11 +30292,11 @@ fn analyzeSliceLen( ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; - if (try sema.resolveValue(slice_inst)) |slice_val| { + if (sema.resolveValue(slice_inst)) |slice_val| { if (slice_val.isUndef(zcu)) { return .undef_usize; } - return pt.intRef(.usize, try slice_val.sliceLen(pt)); + return pt.intRef(.usize, slice_val.sliceLen(zcu)); } try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.slice_len, .usize, slice_inst); @@ -31341,25 +30305,25 @@ fn analyzeSliceLen( fn analyzeIsNull( sema: *Sema, block: *Block, + src: LazySrcLoc, operand: Air.Inst.Ref, invert_logic: bool, ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; - const result_ty: Type = .bool; - if (try sema.resolveValue(operand)) |opt_val| { + + if (try sema.resolveIsNullFromType(block, src, sema.typeOf(operand))) |is_null| { + return .fromValue(.makeBool(is_null != invert_logic)); // XOR + } + + if (sema.resolveValue(operand)) |opt_val| { if (opt_val.isUndef(zcu)) { - return pt.undefRef(result_ty); + return pt.undefRef(.bool); } const is_null = opt_val.isNull(zcu); - const bool_value = if (invert_logic) !is_null else is_null; - return if (bool_value) .bool_true else .bool_false; + return .fromValue(.makeBool(is_null != invert_logic)); // XOR } - if (sema.typeOf(operand).isNullFromType(zcu)) |is_null| { - const result = is_null != invert_logic; - return if (result) .bool_true else .bool_false; - } const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null; return block.addUnOp(air_tag, operand); } @@ -31381,7 +30345,7 @@ fn resolvePtrIsNonErrVal( } assert(child_ty.zigTypeTag(zcu) == .error_union); - if (try sema.resolveValue(operand)) |eu_ptr_val| { + if (sema.resolveValue(operand)) |eu_ptr_val| { if (eu_ptr_val.isUndef(zcu)) return .undef_bool; if (try sema.pointerDeref(block, src, eu_ptr_val, ptr_ty)) |err_union| { if (err_union.isUndef(zcu)) return .undef_bool; @@ -31404,7 +30368,7 @@ fn resolveIsNonErrVal( } assert(sema.typeOf(operand).zigTypeTag(zcu) == .error_union); - if (try sema.resolveValue(operand)) |err_union| { + if (sema.resolveValue(operand)) |err_union| { if (err_union.isUndef(zcu)) return .undef_bool; return .makeBool(err_union.getErrorName(zcu) == .none); } @@ -31412,6 +30376,35 @@ fn resolveIsNonErrVal( return null; } +fn resolveIsNullFromType( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + ty: Type, +) CompileError!?bool { + const zcu = sema.pt.zcu; + return switch (ty.zigTypeTag(zcu)) { + else => false, + .null => true, + .pointer => switch (ty.ptrSize(zcu)) { + .c => null, + else => false, + }, + .optional => { + const payload_ty = ty.optionalChild(zcu); + if (payload_ty.classify(zcu) == .no_possible_value) { + return true; // e.g. `?noreturn` + } + if (payload_ty.zigTypeTag(zcu) == .error_set and + try sema.resolveErrSetIsEmpty(block, src, payload_ty)) + { + return true; // e.g. `?error{}` + } + return null; + }, + }; +} + fn resolveIsNonErrFromType( sema: *Sema, block: *Block, @@ -31420,89 +30413,71 @@ fn resolveIsNonErrFromType( ) CompileError!?Value { const pt = sema.pt; const zcu = pt.zcu; - const ip = &zcu.intern_pool; const ot = operand_ty.zigTypeTag(zcu); if (ot != .error_set and ot != .error_union) return .true; if (ot == .error_set) return .false; assert(ot == .error_union); const payload_ty = operand_ty.errorUnionPayload(zcu); - if (payload_ty.zigTypeTag(zcu) == .noreturn) { + if (payload_ty.classify(zcu) == .no_possible_value) { return .false; } + if (try sema.resolveErrSetIsEmpty(block, src, operand_ty.errorUnionSet(zcu))) { + return .true; + } + return null; +} - // exception if the error union error set is known to be empty, - // we allow the comparison but always make it comptime-known. - const set_ty = ip.errorUnionSet(operand_ty.toIntern()); - switch (set_ty) { - .anyerror_type => {}, - .adhoc_inferred_error_set_type => if (sema.fn_ret_ty_ies) |ies| blk: { - // If the error set is empty, we must return a comptime true or false. - // However we want to avoid unnecessarily resolving an inferred error set - // in case it is already non-empty. - switch (ies.resolved) { - .anyerror_type => break :blk, - .none => {}, - else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk, - } - - if (ies.errors.count() != 0) return null; - switch (ies.resolved) { - .anyerror_type => return null, - .none => {}, - else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) { - 0 => return .true, - else => return null, - }, - } - // We do not have a comptime answer because this inferred error - // set is not resolved, and an instruction later in this function - // body may or may not cause an error to be added to this set. - return null; - }, - else => switch (ip.indexToKey(set_ty)) { - .error_set_type => |error_set_type| { - if (error_set_type.names.len == 0) return .true; - }, - .inferred_error_set_type => |func_index| blk: { - // If the error set is empty, we must return a comptime true or false. - // However we want to avoid unnecessarily resolving an inferred error set - // in case it is already non-empty. - try zcu.maybeUnresolveIes(func_index); - switch (ip.funcIesResolvedUnordered(func_index)) { - .anyerror_type => break :blk, - .none => {}, - else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk, - } +/// Returns `true` iff the error set type `orig_err_set_ty` contains no errors. +/// +/// This is used to give comptime answers for whether `error{}!T` is an error or a payload, as well +/// as whether `?error{}` is null. The type `error{}` cannot be NPV, as it has runtime bits, but the +/// only value of that type which can exist is `undefined`; semantically it has no "legal" value. +/// TODO: this runs into some unsolved language design questions about such types. Performing a +/// coercion from `@as(E, undefined)` to `E!T` needs to semantically result in an `undefined` error +/// union if our implementation is to be legal, and likewise for coercing `@as(E, undefined)` to +/// `?E` (for an error set `E`) because our implementation uses the zero error value at runtime to +/// represent `null`. The unsolved problem is the exact rules for `undefined` propagation through +/// these types: for instance, what if `@as(u32, undfined)` is coerced to `?u32`? What about error +/// union *payloads*, i.e. `@as(u32, undefined)` to `E!u32`? That one is analagous to the optional +/// example in some ways, but right now I believe there is code which relies on that coercion giving +/// a well-defined error union with an `undefined` payload. +/// Relevant issues/discussions: +/// * https://github.com/ziglang/zig/issues/1831 +/// * https://github.com/ziglang/zig/issues/6762 +/// * https://github.com/ziglang/zig/issues/1831#issuecomment-722129239 +fn resolveErrSetIsEmpty( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + orig_err_set_ty: Type, +) CompileError!bool { + const ip = &sema.pt.zcu.intern_pool; + err_set: switch (orig_err_set_ty.toIntern()) { + .anyerror_type => return false, + .adhoc_inferred_error_set_type => { + // This is *our* error set; that is, we're currently analyzing the function + // which owns it. Trying to resolve it now would cause a dependency loop. + // Instead, accept that we don't know. + return false; + }, + else => |err_set_ty| switch (ip.indexToKey(err_set_ty)) { + .error_set_type => |es| return es.names.len == 0, + .inferred_error_set_type => |func_index| { if (sema.fn_ret_ty_ies) |ies| { if (ies.func == func_index) { - // Try to avoid resolving inferred error set if possible. - if (ies.errors.count() != 0) return null; - switch (ies.resolved) { - .anyerror_type => return null, - .none => {}, - else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) { - 0 => return .true, - else => return null, - }, - } - // We do not have a comptime answer because this inferred error - // set is not resolved, and an instruction later in this function - // body may or may not cause an error to be added to this set. - return null; + // This is *our* error set; that is, we're currently analyzing the function + // which owns it. Trying to resolve it now would cause a dependency loop. + // Instead, accept that we don't know. + return false; } } - const resolved_ty = try sema.resolveInferredErrorSet(block, src, set_ty); - if (resolved_ty == .anyerror_type) - break :blk; - if (ip.indexToKey(resolved_ty).error_set_type.names.len == 0) - return .true; + try sema.ensureFuncIesResolved(block, src, func_index); + continue :err_set ip.funcIesResolvedUnordered(func_index); }, else => unreachable, }, } - - return null; } fn analyzeIsNonErr( @@ -31682,6 +30657,8 @@ fn analyzeSlice( else => return sema.fail(block, src, "slice of non-array type '{f}'", .{ptr_ptr_child_ty.fmt(pt)}), } + try sema.ensureLayoutResolved(elem_ty, src, .ptr_access); + const ptr = if (slice_ty.isSlice(zcu)) try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty) else if (array_ty.zigTypeTag(zcu) == .array) ptr: { @@ -31690,11 +30667,11 @@ fn analyzeSlice( assert(manyptr_ty_key.flags.size == .one); manyptr_ty_key.child = elem_ty.toIntern(); manyptr_ty_key.flags.size = .many; - break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(manyptr_ty_key), ptr_or_slice, ptr_src); + break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrType(manyptr_ty_key), ptr_or_slice, ptr_src); } else ptr_or_slice; const start = try sema.coerce(block, .usize, uncasted_start, start_src); - const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src); + const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, start_src); const new_ptr_ty = sema.typeOf(new_ptr); // true if and only if the end index of the slice, implicitly or explicitly, equals @@ -31754,12 +30731,12 @@ fn analyzeSlice( break :end try sema.coerce(block, .usize, uncasted_end, end_src); } else try sema.coerce(block, .usize, uncasted_end_opt, end_src); if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { - if (try sema.resolveValue(ptr_or_slice)) |slice_val| { + if (sema.resolveValue(ptr_or_slice)) |slice_val| { if (slice_val.isUndef(zcu)) { return sema.fail(block, src, "slice of undefined", .{}); } const has_sentinel = slice_ty.sentinel(zcu) != null; - const slice_len = try slice_val.sliceLen(pt); + const slice_len = slice_val.sliceLen(zcu); const len_plus_sent = slice_len + @intFromBool(has_sentinel); const slice_len_val_with_sentinel = try pt.intValue(.usize, len_plus_sent); if (!(try sema.compareAll(end_val, .lte, slice_len_val_with_sentinel, .usize))) { @@ -31774,7 +30751,7 @@ fn analyzeSlice( "end index {f} out of bounds for slice of length {d}{s}", .{ end_val.fmtValueSema(pt, sema), - try slice_val.sliceLen(pt), + slice_val.sliceLen(zcu), sentinel_label, }, ); @@ -31832,7 +30809,7 @@ fn analyzeSlice( break :msg msg; }); } - return sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src); + return sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, start_src); }; const sentinel = s: { @@ -31876,7 +30853,7 @@ fn analyzeSlice( ); } checked_start_lte_end = true; - if (try sema.resolveValue(new_ptr)) |ptr_val| sentinel_check: { + if (sema.resolveValue(new_ptr)) |ptr_val| sentinel_check: { const expected_sentinel = sentinel orelse break :sentinel_check; const start_int = start_val.toUnsignedInt(zcu); const end_int = end_val.toUnsignedInt(zcu); @@ -31943,9 +30920,9 @@ fn analyzeSlice( const new_allowzero = new_ptr_ty_info.flags.is_allowzero and sema.typeOf(ptr).ptrSize(zcu) != .c; if (opt_new_len_val) |new_len_val| { - const new_len_int = try new_len_val.toUnsignedIntSema(pt); + const new_len_int = new_len_val.toUnsignedInt(zcu); - const return_ty = try pt.ptrTypeSema(.{ + const return_ty = try pt.ptrType(.{ .child = (try pt.arrayType(.{ .len = new_len_int, .sentinel = if (sentinel) |s| s.toIntern() else .none, @@ -31960,13 +30937,13 @@ fn analyzeSlice( }, }); - const opt_new_ptr_val = try sema.resolveValue(new_ptr); + const opt_new_ptr_val = sema.resolveValue(new_ptr); const new_ptr_val = opt_new_ptr_val orelse { const result = try block.addBitCast(return_ty, new_ptr); if (block.wantSafety()) { // requirement: slicing C ptr is non-null if (ptr_ptr_child_ty.isCPtr(zcu)) { - const is_non_null = try sema.analyzeIsNull(block, ptr, true); + const is_non_null = try block.addUnOp(.is_non_null, ptr); try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); } @@ -32009,7 +30986,7 @@ fn analyzeSlice( return sema.fail(block, src, "non-zero length slice of undefined pointer", .{}); } - const return_ty = try pt.ptrTypeSema(.{ + const return_ty = try pt.ptrType(.{ .child = elem_ty.toIntern(), .sentinel = if (sentinel) |s| s.toIntern() else .none, .flags = .{ @@ -32026,7 +31003,7 @@ fn analyzeSlice( if (block.wantSafety()) { // requirement: slicing C ptr is non-null if (ptr_ptr_child_ty.isCPtr(zcu)) { - const is_non_null = try sema.analyzeIsNull(block, ptr, true); + const is_non_null = try block.addUnOp(.is_non_null, ptr); try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); } @@ -32037,7 +31014,7 @@ fn analyzeSlice( if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| { // we don't need to add one for sentinels because the // underlying value data includes the sentinel - break :blk try pt.intRef(.usize, try slice_val.sliceLen(pt)); + break :blk try pt.intRef(.usize, slice_val.sliceLen(zcu)); } const slice_len_inst = try block.addTyOp(.slice_len, .usize, ptr_or_slice); @@ -32107,8 +31084,8 @@ fn cmpNumeric( else uncasted_rhs; - const maybe_lhs_val = try sema.resolveValue(lhs); - const maybe_rhs_val = try sema.resolveValue(rhs); + const maybe_lhs_val = sema.resolveValue(lhs); + const maybe_rhs_val = sema.resolveValue(rhs); // If the LHS is const, check if there is a guaranteed result which does not depend on ths RHS value. if (maybe_lhs_val) |lhs_val| { @@ -32158,16 +31135,10 @@ fn cmpNumeric( const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| rs: { if (maybe_rhs_val) |rhs_val| { - const res = try Value.compareHeteroSema(lhs_val, op, rhs_val, pt); - return if (res) .bool_true else .bool_false; + return .fromValue(.makeBool(Value.compareHetero(lhs_val, op, rhs_val, zcu))); } else break :rs rhs_src; } else lhs_src; - // TODO handle comparisons against lazy zero values - // Some values can be compared against zero without being runtime-known or without forcing - // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to - // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout - // of this function if we don't need to. try sema.requireRuntimeBlock(block, src, runtime_src); // For floats, emit a float comparison instruction. @@ -32207,11 +31178,11 @@ fn cmpNumeric( // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float, // add/subtract 1. const lhs_is_signed = if (maybe_lhs_val) |lhs_val| - !(try lhs_val.compareAllWithZeroSema(.gte, pt)) + !lhs_val.compareAllWithZero(.gte, zcu) else (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt(zcu)); const rhs_is_signed = if (maybe_rhs_val) |rhs_val| - !(try rhs_val.compareAllWithZeroSema(.gte, pt)) + !rhs_val.compareAllWithZero(.gte, zcu) else (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(zcu)); const dest_int_is_signed = lhs_is_signed or rhs_is_signed; @@ -32219,10 +31190,9 @@ fn cmpNumeric( var dest_float_type: ?Type = null; var lhs_bits: usize = undefined; - if (maybe_lhs_val) |unresolved_lhs_val| { - const lhs_val = try sema.resolveLazyValue(unresolved_lhs_val); + if (maybe_lhs_val) |lhs_val| { if (!rhs_is_signed) { - switch (lhs_val.orderAgainstZero(zcu)) { + switch (Value.order(lhs_val, .zero_comptime_int, zcu)) { .gt => {}, .eq => switch (op) { // LHS = 0, RHS is unsigned .lte => return .bool_true, @@ -32263,10 +31233,9 @@ fn cmpNumeric( } var rhs_bits: usize = undefined; - if (maybe_rhs_val) |unresolved_rhs_val| { - const rhs_val = try sema.resolveLazyValue(unresolved_rhs_val); + if (maybe_rhs_val) |rhs_val| { if (!lhs_is_signed) { - switch (rhs_val.orderAgainstZero(zcu)) { + switch (Value.order(rhs_val, .zero_comptime_int, zcu)) { .gt => {}, .eq => switch (op) { // RHS = 0, LHS is unsigned .gte => return .bool_true, @@ -32328,7 +31297,7 @@ fn compareIntsOnlyPossibleResult( lhs_val: Value, op: std.math.CompareOperator, rhs_ty: Type, -) SemaError!?bool { +) Allocator.Error!?bool { const pt = sema.pt; const zcu = pt.zcu; @@ -32337,11 +31306,11 @@ fn compareIntsOnlyPossibleResult( if (min_rhs.toIntern() == max_rhs.toIntern()) { // RHS is effectively comptime-known. - return try Value.compareHeteroSema(lhs_val, op, min_rhs, pt); + return Value.compareHetero(lhs_val, op, min_rhs, zcu); } - const against_min = try lhs_val.orderAdvanced(min_rhs, .sema, zcu, pt.tid); - const against_max = try lhs_val.orderAdvanced(max_rhs, .sema, zcu, pt.tid); + const against_min = lhs_val.order(min_rhs, zcu); + const against_max = lhs_val.order(max_rhs, zcu); switch (op) { .eq => { @@ -32401,8 +31370,8 @@ fn cmpVector( .child = .bool_type, }); - const maybe_lhs_val = try sema.resolveValue(casted_lhs); - const maybe_rhs_val = try sema.resolveValue(casted_rhs); + const maybe_lhs_val = sema.resolveValue(casted_lhs); + const maybe_rhs_val = sema.resolveValue(casted_rhs); if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty); if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty); @@ -32424,7 +31393,7 @@ fn wrapOptional( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) !Air.Inst.Ref { - if (try sema.resolveValue(inst)) |val| { + if (sema.resolveValue(inst)) |val| { return Air.internedToRef((try sema.pt.intern(.{ .opt = .{ .ty = dest_ty.toIntern(), .val = val.toIntern(), @@ -32446,7 +31415,7 @@ fn wrapErrorUnionPayload( const zcu = pt.zcu; const dest_payload_ty = dest_ty.errorUnionPayload(zcu); const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false }); - if (try sema.resolveValue(coerced)) |val| { + if (sema.resolveValue(coerced)) |val| { return Air.internedToRef((try pt.intern(.{ .error_union = .{ .ty = dest_ty.toIntern(), .val = .{ .payload = val.toIntern() }, @@ -32466,80 +31435,40 @@ fn wrapErrorUnionSet( const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - const inst_ty = sema.typeOf(inst); const dest_err_set_ty = dest_ty.errorUnionSet(zcu); - if (try sema.resolveValue(inst)) |val| { - const expected_name = zcu.intern_pool.indexToKey(val.toIntern()).err.name; - switch (dest_err_set_ty.toIntern()) { - .anyerror_type => {}, - .adhoc_inferred_error_set_type => ok: { - const ies = sema.fn_ret_ty_ies.?; - switch (ies.resolved) { - .anyerror_type => break :ok, - .none => if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) { - break :ok; - }, - else => |i| if (ip.indexToKey(i).error_set_type.nameIndex(ip, expected_name) != null) { - break :ok; - }, - } - return sema.failWithTypeMismatch(block, inst_src, dest_err_set_ty, inst_ty); - }, - else => switch (ip.indexToKey(dest_err_set_ty.toIntern())) { - .error_set_type => |error_set_type| ok: { - if (error_set_type.nameIndex(ip, expected_name) != null) break :ok; - return sema.failWithTypeMismatch(block, inst_src, dest_err_set_ty, inst_ty); - }, - .inferred_error_set_type => |func_index| ok: { - // We carefully do this in an order that avoids unnecessarily - // resolving the destination error set type. - try zcu.maybeUnresolveIes(func_index); - switch (ip.funcIesResolvedUnordered(func_index)) { - .anyerror_type => break :ok, - .none => if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) { - break :ok; - }, - else => |i| if (ip.indexToKey(i).error_set_type.nameIndex(ip, expected_name) != null) { - break :ok; - }, - } - - return sema.failWithTypeMismatch(block, inst_src, dest_err_set_ty, inst_ty); - }, - else => unreachable, - }, - } - return Air.internedToRef((try pt.intern(.{ .error_union = .{ + const coerced = try sema.coerceExtra(block, dest_err_set_ty, inst, inst_src, .{ .report_err = false }); + if (try sema.resolveDefinedValue(block, inst_src, coerced)) |error_val| { + return .fromIntern(try pt.intern(.{ .error_union = .{ .ty = dest_ty.toIntern(), - .val = .{ .err_name = expected_name }, - } }))); + .val = .{ .err_name = ip.indexToKey(error_val.toIntern()).err.name }, + } })); + } else { + return block.addTyOp(.wrap_errunion_err, dest_ty, coerced); } - - try sema.requireRuntimeBlock(block, inst_src, null); - const coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src); - return block.addTyOp(.wrap_errunion_err, dest_ty, coerced); } -fn unionToTag( - sema: *Sema, - block: *Block, - enum_ty: Type, - un: Air.Inst.Ref, - un_src: LazySrcLoc, -) !Air.Inst.Ref { +/// Returns the enum tag value for the active tag of a tagged union value. +/// +/// Asserts that the type of `un` is a tagged union type. +fn unionToTag(sema: *Sema, block: *Block, un: Air.Inst.Ref) !Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; - if ((try sema.typeHasOnePossibleValue(enum_ty))) |opv| { - return Air.internedToRef(opv.toIntern()); + const ip = &zcu.intern_pool; + const union_obj = ip.loadUnionType(sema.typeOf(un).toIntern()); + assert(union_obj.tag_usage == .tagged); + if (sema.resolveValue(un)) |un_val| { + return .fromValue(un_val.unionTag(zcu).?); } - if (try sema.resolveValue(un)) |un_val| { - const tag_val = un_val.unionTag(zcu).?; - if (tag_val.isUndef(zcu)) - return try pt.undefRef(enum_ty); - return Air.internedToRef(tag_val.toIntern()); + const enum_tag_ty: Type = .fromInterned(union_obj.enum_tag_type); + if (!union_obj.has_runtime_tag) { + // This means that only one field is possible. + const field_index = for (union_obj.field_types.get(ip), 0..) |field_ty_ip, field_index| { + const field_ty: Type = .fromInterned(field_ty_ip); + if (field_ty.classify(zcu) != .no_possible_value) break field_index; + } else unreachable; + return .fromValue(try pt.enumValueFieldIndex(enum_tag_ty, @intCast(field_index))); } - try sema.requireRuntimeBlock(block, un_src, null); - return block.addTyOp(.get_union_tag, enum_ty, un); + return block.addTyOp(.get_union_tag, enum_tag_ty, un); } const PeerResolveStrategy = enum { @@ -32879,7 +31808,7 @@ fn resolvePeerTypes( for (instructions, peer_tys, peer_vals) |inst, *ty, *val| { ty.* = sema.typeOf(inst); - val.* = try sema.resolveValue(inst); + val.* = sema.resolveValue(inst); } switch (try sema.resolvePeerTypesInner(block, src, peer_tys, peer_vals)) { @@ -33240,18 +32169,24 @@ fn resolvePeerTypesInner( ptr_info.sentinel = .none; } - // Note that the align can be always non-zero; Zcu.ptrType will canonicalize it - ptr_info.flags.alignment = InternPool.Alignment.min( - if (ptr_info.flags.alignment != .none) - ptr_info.flags.alignment - else - Type.fromInterned(ptr_info.child).abiAlignment(zcu), - - if (peer_info.flags.alignment != .none) - peer_info.flags.alignment - else - Type.fromInterned(peer_info.child).abiAlignment(zcu), - ); + ptr_info.flags.alignment = a: { + // If both alignments are implicit, the result alignment is implicit. + // e.g. '[*c]u32' + '[*c]c_uint' -> '[*c]u32' + if (ptr_info.flags.alignment == .none and peer_info.flags.alignment == .none) { + break :a .none; + } + // Otherwise (if either alignment is explicit), the result alignment is explicit. + // e.g. '[*c]u32' + '[*c]align(4) c_uint' -> '[*c]align(4) u32' + const cur_align = switch (ptr_info.flags.alignment) { + .none => Type.fromInterned(ptr_info.child).abiAlignment(zcu), + else => ptr_info.flags.alignment, + }; + const new_align = switch (peer_info.flags.alignment) { + .none => Type.fromInterned(peer_info.child).abiAlignment(zcu), + else => peer_info.flags.alignment, + }; + break :a .minStrict(cur_align, new_align); + }; if (ptr_info.flags.address_space != peer_info.flags.address_space) { return .{ .conflict = .{ .peer_idx_a = first_idx, @@ -33273,7 +32208,7 @@ fn resolvePeerTypesInner( opt_ptr_info = ptr_info; } - return .{ .success = try pt.ptrTypeSema(opt_ptr_info.?) }; + return .{ .success = try pt.ptrType(opt_ptr_info.?) }; }, .ptr => { @@ -33281,7 +32216,6 @@ fn resolvePeerTypesInner( // if there were no actual slices. Else, we want the slice index to report a conflict. var opt_slice_idx: ?usize = null; - var any_abi_aligned = false; var opt_ptr_info: ?InternPool.Key.PtrType = null; var first_idx: usize = undefined; var other_idx: usize = undefined; // We sometimes need a second peer index to report a generic error @@ -33325,15 +32259,24 @@ fn resolvePeerTypesInner( .peer_idx_b = i, } }; - // Note that the align can be always non-zero; Type.ptr will canonicalize it - if (peer_info.flags.alignment == .none) { - any_abi_aligned = true; - } else if (ptr_info.flags.alignment == .none) { - any_abi_aligned = true; - ptr_info.flags.alignment = peer_info.flags.alignment; - } else { - ptr_info.flags.alignment = ptr_info.flags.alignment.minStrict(peer_info.flags.alignment); - } + ptr_info.flags.alignment = a: { + // If both alignments are implicit, the result alignment is implicit. + // e.g. '*u32' + '*c_uint' -> '*u32' + if (ptr_info.flags.alignment == .none and peer_info.flags.alignment == .none) { + break :a .none; + } + // Otherwise (if either alignment is explicit), the result alignment is explicit. + // e.g. '*u32' + '*align(4) c_uint' -> '*align(4) u32' + const cur_align = switch (ptr_info.flags.alignment) { + .none => Type.fromInterned(ptr_info.child).abiAlignment(zcu), + else => ptr_info.flags.alignment, + }; + const new_align = switch (peer_info.flags.alignment) { + .none => Type.fromInterned(peer_info.child).abiAlignment(zcu), + else => peer_info.flags.alignment, + }; + break :a .minStrict(cur_align, new_align); + }; if (ptr_info.flags.address_space != peer_info.flags.address_space) { return generic_err; @@ -33582,13 +32525,7 @@ fn resolvePeerTypesInner( }, } - if (any_abi_aligned and opt_ptr_info.?.flags.alignment != .none) { - opt_ptr_info.?.flags.alignment = opt_ptr_info.?.flags.alignment.minStrict( - try Type.fromInterned(pointee).abiAlignmentSema(pt), - ); - } - - return .{ .success = try pt.ptrTypeSema(opt_ptr_info.?) }; + return .{ .success = try pt.ptrType(opt_ptr_info.?) }; }, .func => { @@ -33731,7 +32668,7 @@ fn resolvePeerTypesInner( .peer_idx_b = i, } }; any_comptime_known = true; - ptr_opt_val.* = try sema.resolveLazyValue(opt_val.?); + ptr_opt_val.* = opt_val.?; continue; }, .int => {}, @@ -33924,7 +32861,6 @@ fn resolvePeerTypesInner( var comptime_val: ?Value = null; for (peer_tys) |opt_ty| { const struct_ty = opt_ty orelse continue; - try struct_ty.resolveStructFieldInits(pt); const uncoerced_field_val = try struct_ty.structFieldValueComptime(pt, field_index) orelse { comptime_val = null; @@ -33939,2242 +32875,274 @@ fn resolvePeerTypesInner( }, else => |e| return e, }; - const coerced_val = (try sema.resolveValue(coerced_inst)) orelse continue; + const coerced_val = sema.resolveValue(coerced_inst) orelse continue; const existing = comptime_val orelse { comptime_val = coerced_val; - continue; - }; - if (!coerced_val.eql(existing, .fromInterned(field_ty.*), zcu)) { - comptime_val = null; - break; - } - } - - field_val.* = if (comptime_val) |v| v.toIntern() else .none; - } - - const final_ty = try ip.getTupleType(gpa, io, pt.tid, .{ - .types = field_types, - .values = field_vals, - }); - - return .{ .success = .fromInterned(final_ty) }; - }, - - .exact => { - var expect_ty: ?Type = null; - var first_idx: usize = undefined; - for (peer_tys, 0..) |opt_ty, i| { - const ty = opt_ty orelse continue; - if (expect_ty) |expect| { - if (!ty.eql(expect, zcu)) return .{ .conflict = .{ - .peer_idx_a = first_idx, - .peer_idx_b = i, - } }; - } else { - expect_ty = ty; - first_idx = i; - } - } - return .{ .success = expect_ty.? }; - }, - } -} - -fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type { - // e0 -> e1 - if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e1, e0, src, src)) { - return e1; - } - - // e1 -> e0 - if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e0, e1, src, src)) { - return e0; - } - - return sema.errorSetMerge(e0, e1); -} - -fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type { - const target = sema.pt.zcu.getTarget(); - - // ty_b -> ty_a - if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, false, target, src, src, null)) { - return ty_a; - } - - // ty_a -> ty_b - if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, false, target, src, src, null)) { - return ty_b; - } - - return null; -} - -const ArrayLike = struct { - len: u64, - /// `noreturn` indicates that this type is `struct{}` so can coerce to anything - elem_ty: Type, -}; -fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike { - const pt = sema.pt; - const zcu = pt.zcu; - return switch (ty.zigTypeTag(zcu)) { - .array => .{ - .len = ty.arrayLen(zcu), - .elem_ty = ty.childType(zcu), - }, - .@"struct" => { - const field_count = ty.structFieldCount(zcu); - if (field_count == 0) return .{ - .len = 0, - .elem_ty = .noreturn, - }; - if (!ty.isTuple(zcu)) return null; - const elem_ty = ty.fieldType(0, zcu); - for (1..field_count) |i| { - if (!ty.fieldType(i, zcu).eql(elem_ty, zcu)) { - return null; - } - } - return .{ - .len = field_count, - .elem_ty = elem_ty, - }; - }, - else => null, - }; -} - -pub fn resolveIes(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError!void { - const pt = sema.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - - if (sema.fn_ret_ty_ies) |ies| { - try sema.resolveInferredErrorSetPtr(block, src, ies); - assert(ies.resolved != .none); - ip.funcIesResolved(sema.func_index).* = ies.resolved; - } -} - -pub fn resolveFnTypes(sema: *Sema, fn_ty: Type, src: LazySrcLoc) CompileError!void { - const pt = sema.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const fn_ty_info = zcu.typeToFunc(fn_ty).?; - - try Type.fromInterned(fn_ty_info.return_type).resolveFully(pt); - - if (zcu.comp.config.any_error_tracing and - Type.fromInterned(fn_ty_info.return_type).isError(zcu)) - { - // Ensure the type exists so that backends can assume that. - _ = try sema.getBuiltinType(src, .StackTrace); - } - - for (0..fn_ty_info.param_types.len) |i| { - try Type.fromInterned(fn_ty_info.param_types.get(ip)[i]).resolveFully(pt); - } -} - -fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value { - return val.resolveLazy(sema.arena, sema.pt); -} - -/// Resolve a struct's alignment only without triggering resolution of its layout. -/// Asserts that the alignment is not yet resolved and the layout is non-packed. -pub fn resolveStructAlignment( - sema: *Sema, - ty: InternPool.Index, - struct_type: InternPool.LoadedStructType, -) SemaError!void { - const pt = sema.pt; - const zcu = pt.zcu; - const io = zcu.comp.io; - const ip = &zcu.intern_pool; - const target = zcu.getTarget(); - - assert(sema.owner.unwrap().type == ty); - - assert(struct_type.layout != .@"packed"); - assert(struct_type.flagsUnordered(ip).alignment == .none); - - const ptr_align = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)); - - // We'll guess "pointer-aligned", if the struct has an - // underaligned pointer field then some allocations - // might require explicit alignment. - if (struct_type.assumePointerAlignedIfFieldTypesWip(ip, io, ptr_align)) return; - - try sema.resolveStructFieldTypes(ty, struct_type); - - // We'll guess "pointer-aligned", if the struct has an - // underaligned pointer field then some allocations - // might require explicit alignment. - if (struct_type.assumePointerAlignedIfWip(ip, io, ptr_align)) return; - defer struct_type.clearAlignmentWip(ip, io); - - // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis. - // It's just triggering *other* analysis, alongside a simple loop over already-resolved info. - - var alignment: Alignment = .@"1"; - - for (0..struct_type.field_types.len) |i| { - const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); - if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt)) - continue; - const field_align = try field_ty.structFieldAlignmentSema( - struct_type.fieldAlign(ip, i), - struct_type.layout, - pt, - ); - alignment = alignment.maxStrict(field_align); - } - - struct_type.setAlignment(ip, io, alignment); -} - -pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void { - const pt = sema.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const io = zcu.comp.io; - const struct_type = zcu.typeToStruct(ty) orelse return; - - assert(sema.owner.unwrap().type == ty.toIntern()); - - if (struct_type.haveLayout(ip)) - return; - - try sema.resolveStructFieldTypes(ty.toIntern(), struct_type); - - // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis. - // It's just triggering *other* analysis, alongside a simple loop over already-resolved info. - - if (struct_type.layout == .@"packed") { - sema.backingIntType(struct_type) catch |err| switch (err) { - error.AnalysisFail, error.OutOfMemory, error.Canceled => |e| return e, - error.ComptimeBreak, error.ComptimeReturn => unreachable, - }; - return; - } - - if (struct_type.setLayoutWip(ip, io)) { - const msg = try sema.errMsg( - ty.srcLoc(zcu), - "struct '{f}' depends on itself", - .{ty.fmt(pt)}, - ); - return sema.failWithOwnedErrorMsg(null, msg); - } - defer struct_type.clearLayoutWip(ip, io); - - const aligns = try sema.arena.alloc(Alignment, struct_type.field_types.len); - const sizes = try sema.arena.alloc(u64, struct_type.field_types.len); - - var big_align: Alignment = .@"1"; - - for (aligns, sizes, 0..) |*field_align, *field_size, i| { - const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); - if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt)) { - struct_type.offsets.get(ip)[i] = 0; - field_size.* = 0; - field_align.* = .none; - continue; - } - - field_size.* = field_ty.abiSizeSema(pt) catch |err| switch (err) { - error.AnalysisFail => { - const msg = sema.err orelse return err; - try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{}); - return err; - }, - else => return err, - }; - field_align.* = try field_ty.structFieldAlignmentSema( - struct_type.fieldAlign(ip, i), - struct_type.layout, - pt, - ); - big_align = big_align.maxStrict(field_align.*); - } - - if (struct_type.flagsUnordered(ip).assumed_runtime_bits and !(try ty.hasRuntimeBitsSema(pt))) { - const msg = try sema.errMsg( - ty.srcLoc(zcu), - "struct layout depends on it having runtime bits", - .{}, - ); - return sema.failWithOwnedErrorMsg(null, msg); - } - - if (struct_type.flagsUnordered(ip).assumed_pointer_aligned and - big_align.compareStrict(.neq, Alignment.fromByteUnits(@divExact(zcu.getTarget().ptrBitWidth(), 8)))) - { - const msg = try sema.errMsg( - ty.srcLoc(zcu), - "struct layout depends on being pointer aligned", - .{}, - ); - return sema.failWithOwnedErrorMsg(null, msg); - } - - if (struct_type.hasReorderedFields()) { - const runtime_order = struct_type.runtime_order.get(ip); - - for (runtime_order, 0..) |*ro, i| { - const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); - if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt)) { - ro.* = .omitted; - } else { - ro.* = @enumFromInt(i); - } - } - - const RuntimeOrder = InternPool.LoadedStructType.RuntimeOrder; - - const AlignSortContext = struct { - aligns: []const Alignment, - - fn lessThan(ctx: @This(), a: RuntimeOrder, b: RuntimeOrder) bool { - if (a == .omitted) return false; - if (b == .omitted) return true; - const a_align = ctx.aligns[@intFromEnum(a)]; - const b_align = ctx.aligns[@intFromEnum(b)]; - return a_align.compare(.gt, b_align); - } - }; - if (!zcu.backendSupportsFeature(.field_reordering)) { - // TODO: we should probably also reorder tuple fields? This is a bit weird because it'll involve - // mutating the `InternPool` for a non-container type. - // - // TODO: implement field reordering support in all the backends! - // - // This logic does not reorder fields; it only moves the omitted ones to the end - // so that logic elsewhere does not need to special-case here. - var i: usize = 0; - var off: usize = 0; - while (i + off < runtime_order.len) { - if (runtime_order[i + off] == .omitted) { - off += 1; - continue; - } - runtime_order[i] = runtime_order[i + off]; - i += 1; - } - @memset(runtime_order[i..], .omitted); - } else { - mem.sortUnstable(RuntimeOrder, runtime_order, AlignSortContext{ - .aligns = aligns, - }, AlignSortContext.lessThan); - } - } - - // Calculate size, alignment, and field offsets. - const offsets = struct_type.offsets.get(ip); - var it = struct_type.iterateRuntimeOrder(ip); - var offset: u64 = 0; - while (it.next()) |i| { - offsets[i] = @intCast(aligns[i].forward(offset)); - offset = offsets[i] + sizes[i]; - } - const size = std.math.cast(u32, big_align.forward(offset)) orelse { - const msg = try sema.errMsg( - ty.srcLoc(zcu), - "struct layout requires size {d}, this compiler implementation supports up to {d}", - .{ big_align.forward(offset), std.math.maxInt(u32) }, - ); - return sema.failWithOwnedErrorMsg(null, msg); - }; - struct_type.setLayoutResolved(ip, io, size, big_align); - _ = try ty.comptimeOnlySema(pt); -} - -fn backingIntType( - sema: *Sema, - struct_type: InternPool.LoadedStructType, -) CompileError!void { - const pt = sema.pt; - const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = comp.gpa; - const io = comp.io; - const ip = &zcu.intern_pool; - - var analysis_arena = std.heap.ArenaAllocator.init(gpa); - defer analysis_arena.deinit(); - - var block: Block = .{ - .parent = null, - .sema = sema, - .namespace = struct_type.namespace, - .instructions = .{}, - .inlining = null, - .comptime_reason = null, // set below if needed - .src_base_inst = struct_type.zir_index, - .type_name_ctx = struct_type.name, - }; - defer assert(block.instructions.items.len == 0); - - const fields_bit_sum = blk: { - var accumulator: u64 = 0; - for (0..struct_type.field_types.len) |i| { - const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); - accumulator += try field_ty.bitSizeSema(pt); - } - break :blk accumulator; - }; - - const zir = zcu.namespacePtr(struct_type.namespace).fileScope(zcu).zir.?; - const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail; - const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; - assert(extended.opcode == .struct_decl); - const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); - - if (small.has_backing_int) { - var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len; - const captures_len = if (small.has_captures_len) blk: { - const captures_len = zir.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - extra_index += @intFromBool(small.has_fields_len); - extra_index += @intFromBool(small.has_decls_len); - - extra_index += captures_len * 2; - - const backing_int_body_len = zir.extra[extra_index]; - extra_index += 1; - - const backing_int_src: LazySrcLoc = .{ - .base_node_inst = struct_type.zir_index, - .offset = .{ .node_offset_container_tag = .zero }, - }; - block.comptime_reason = .{ .reason = .{ - .src = backing_int_src, - .r = .{ .simple = .type }, - } }; - const backing_int_ty = blk: { - if (backing_int_body_len == 0) { - const backing_int_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); - break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref); - } else { - const body = zir.bodySlice(extra_index, backing_int_body_len); - const ty_ref = try sema.resolveInlineBody(&block, body, zir_index); - break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref); - } - }; - - try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum); - struct_type.setBackingIntType(ip, io, backing_int_ty.toIntern()); - } else { - if (fields_bit_sum > std.math.maxInt(u16)) { - return sema.fail(&block, block.nodeOffset(.zero), "size of packed struct '{d}' exceeds maximum bit width of 65535", .{fields_bit_sum}); - } - const backing_int_ty = try pt.intType(.unsigned, @intCast(fields_bit_sum)); - struct_type.setBackingIntType(ip, io, backing_int_ty.toIntern()); - } - - try sema.flushExports(); -} - -fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void { - const pt = sema.pt; - const zcu = pt.zcu; - - if (!backing_int_ty.isInt(zcu)) { - return sema.fail(block, src, "expected backing integer type, found '{f}'", .{backing_int_ty.fmt(pt)}); - } - if (backing_int_ty.bitSize(zcu) != fields_bit_sum) { - return sema.fail( - block, - src, - "backing integer type '{f}' has bit size {d} but the struct fields have a total bit size of {d}", - .{ backing_int_ty.fmt(pt), backing_int_ty.bitSize(zcu), fields_bit_sum }, - ); - } -} - -fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { - const pt = sema.pt; - if (!ty.isIndexable(pt.zcu)) { - const msg = msg: { - const msg = try sema.errMsg(src, "type '{f}' does not support indexing", .{ty.fmt(pt)}); - errdefer msg.destroy(sema.gpa); - try sema.errNote(src, msg, "operand must be an array, slice, tuple, or vector", .{}); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } -} - -fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { - const pt = sema.pt; - const zcu = pt.zcu; - if (ty.zigTypeTag(zcu) == .pointer) { - switch (ty.ptrSize(zcu)) { - .slice, .many, .c => return, - .one => { - const elem_ty = ty.childType(zcu); - if (elem_ty.zigTypeTag(zcu) == .array) return; - // TODO https://github.com/ziglang/zig/issues/15479 - // if (elem_ty.isTuple()) return; - }, - } - } - const msg = msg: { - const msg = try sema.errMsg(src, "type '{f}' is not an indexable pointer", .{ty.fmt(pt)}); - errdefer msg.destroy(sema.gpa); - try sema.errNote(src, msg, "operand must be a slice, a many pointer or a pointer to an array", .{}); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); -} - -/// Resolve a unions's alignment only without triggering resolution of its layout. -/// Asserts that the alignment is not yet resolved. -pub fn resolveUnionAlignment( - sema: *Sema, - ty: Type, - union_type: InternPool.LoadedUnionType, -) SemaError!void { - const pt = sema.pt; - const zcu = pt.zcu; - const io = zcu.comp.io; - const ip = &zcu.intern_pool; - const target = zcu.getTarget(); - - assert(sema.owner.unwrap().type == ty.toIntern()); - - assert(!union_type.haveLayout(ip)); - - const ptr_align = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)); - - // We'll guess "pointer-aligned", if the union has an - // underaligned pointer field then some allocations - // might require explicit alignment. - if (union_type.assumePointerAlignedIfFieldTypesWip(ip, io, ptr_align)) return; - - try sema.resolveUnionFieldTypes(ty, union_type); - - // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis. - // It's just triggering *other* analysis, alongside a simple loop over already-resolved info. - - var max_align: Alignment = .@"1"; - for (0..union_type.field_types.len) |field_index| { - const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]); - if (!(try field_ty.hasRuntimeBitsSema(pt))) continue; - - const explicit_align = union_type.fieldAlign(ip, field_index); - const field_align = if (explicit_align != .none) - explicit_align - else - try field_ty.abiAlignmentSema(sema.pt); - - max_align = max_align.max(field_align); - } - - union_type.setAlignment(ip, io, max_align); -} - -/// This logic must be kept in sync with `Type.getUnionLayout`. -pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void { - const pt = sema.pt; - const io = pt.zcu.comp.io; - const ip = &pt.zcu.intern_pool; - - try sema.resolveUnionFieldTypes(ty, ip.loadUnionType(ty.ip_index)); - - // Load again, since the tag type might have changed due to resolution. - const union_type = ip.loadUnionType(ty.ip_index); - - assert(sema.owner.unwrap().type == ty.toIntern()); - - const old_flags = union_type.flagsUnordered(ip); - switch (old_flags.status) { - .none, .have_field_types => {}, - .field_types_wip, .layout_wip => { - const msg = try sema.errMsg( - ty.srcLoc(pt.zcu), - "union '{f}' depends on itself", - .{ty.fmt(pt)}, - ); - return sema.failWithOwnedErrorMsg(null, msg); - }, - .have_layout, .fully_resolved_wip, .fully_resolved => return, - } - - errdefer union_type.setStatusIfLayoutWip(ip, io, old_flags.status); - - union_type.setStatus(ip, io, .layout_wip); - - // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis. - // It's just triggering *other* analysis, alongside a simple loop over already-resolved info. - - var max_size: u64 = 0; - var max_align: Alignment = .@"1"; - for (0..union_type.field_types.len) |field_index| { - const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]); - if (field_ty.isNoReturn(pt.zcu)) continue; - - // We need to call `hasRuntimeBits` before calling `abiSize` to prevent reachable `unreachable`s, - // but `hasRuntimeBits` only resolves field types and so may infinite recurse on a layout wip type, - // so we must resolve the layout manually first, instead of waiting for `abiSize` to do it for us. - // This is arguably just hacking around bugs in both `abiSize` for not allowing arbitrary types to - // be queried, enabling failures to be handled with the emission of a compile error, and also in - // `hasRuntimeBits` for ever being able to infinite recurse in the first place. - try field_ty.resolveLayout(pt); - - if (try field_ty.hasRuntimeBitsSema(pt)) { - max_size = @max(max_size, field_ty.abiSizeSema(pt) catch |err| switch (err) { - error.AnalysisFail => { - const msg = sema.err orelse return err; - try sema.addFieldErrNote(ty, field_index, msg, "while checking this field", .{}); - return err; - }, - else => return err, - }); - } - - const explicit_align = union_type.fieldAlign(ip, field_index); - const field_align = if (explicit_align != .none) - explicit_align - else - try field_ty.abiAlignmentSema(pt); - max_align = max_align.max(field_align); - } - - const has_runtime_tag = union_type.flagsUnordered(ip).runtime_tag.hasTag() and - try Type.fromInterned(union_type.enum_tag_ty).hasRuntimeBitsSema(pt); - const size, const alignment, const padding = if (has_runtime_tag) layout: { - const enum_tag_type: Type = .fromInterned(union_type.enum_tag_ty); - const tag_align = try enum_tag_type.abiAlignmentSema(pt); - const tag_size = try enum_tag_type.abiSizeSema(pt); - - // Put the tag before or after the payload depending on which one's - // alignment is greater. - var size: u64 = 0; - var padding: u32 = 0; - if (tag_align.order(max_align).compare(.gte)) { - // {Tag, Payload} - size += tag_size; - size = max_align.forward(size); - size += max_size; - const prev_size = size; - size = tag_align.forward(size); - padding = @intCast(size - prev_size); - } else { - // {Payload, Tag} - size += max_size; - size = switch (pt.zcu.getTarget().ofmt) { - .c => max_align, - else => tag_align, - }.forward(size); - size += tag_size; - const prev_size = size; - size = max_align.forward(size); - padding = @intCast(size - prev_size); - } - - break :layout .{ size, max_align.max(tag_align), padding }; - } else .{ max_align.forward(max_size), max_align, 0 }; - - const casted_size = std.math.cast(u32, size) orelse { - const msg = try sema.errMsg( - ty.srcLoc(pt.zcu), - "union layout requires size {d}, this compiler implementation supports up to {d}", - .{ size, std.math.maxInt(u32) }, - ); - return sema.failWithOwnedErrorMsg(null, msg); - }; - union_type.setHaveLayout(ip, io, casted_size, padding, alignment); - - if (union_type.flagsUnordered(ip).assumed_runtime_bits and !(try ty.hasRuntimeBitsSema(pt))) { - const msg = try sema.errMsg( - ty.srcLoc(pt.zcu), - "union layout depends on it having runtime bits", - .{}, - ); - return sema.failWithOwnedErrorMsg(null, msg); - } - - if (union_type.flagsUnordered(ip).assumed_pointer_aligned and - alignment.compareStrict(.neq, Alignment.fromByteUnits(@divExact(pt.zcu.getTarget().ptrBitWidth(), 8)))) - { - const msg = try sema.errMsg( - ty.srcLoc(pt.zcu), - "union layout depends on being pointer aligned", - .{}, - ); - return sema.failWithOwnedErrorMsg(null, msg); - } - _ = try ty.comptimeOnlySema(pt); -} - -/// Returns `error.AnalysisFail` if any of the types (recursively) failed to -/// be resolved. -pub fn resolveStructFully(sema: *Sema, ty: Type) SemaError!void { - try sema.resolveStructLayout(ty); - try sema.resolveStructFieldInits(ty); - - const pt = sema.pt; - const zcu = pt.zcu; - const io = zcu.comp.io; - const ip = &zcu.intern_pool; - const struct_type = zcu.typeToStruct(ty).?; - - assert(sema.owner.unwrap().type == ty.toIntern()); - - if (struct_type.setFullyResolved(ip, io)) return; - errdefer struct_type.clearFullyResolved(ip, io); - - // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis. - // It's just triggering *other* analysis, alongside a simple loop over already-resolved info. - - // After we have resolve struct layout we have to go over the fields again to - // make sure pointer fields get their child types resolved as well. - // See also similar code for unions. - - for (0..struct_type.field_types.len) |i| { - const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); - try field_ty.resolveFully(pt); - } -} - -pub fn resolveUnionFully(sema: *Sema, ty: Type) SemaError!void { - try sema.resolveUnionLayout(ty); - - const pt = sema.pt; - const zcu = pt.zcu; - const io = zcu.comp.io; - const ip = &zcu.intern_pool; - const union_obj = zcu.typeToUnion(ty).?; - - assert(sema.owner.unwrap().type == ty.toIntern()); - - switch (union_obj.flagsUnordered(ip).status) { - .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {}, - .fully_resolved_wip, .fully_resolved => return, - } - - // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis. - // It's just triggering *other* analysis, alongside a simple loop over already-resolved info. - - { - // After we have resolve union layout we have to go over the fields again to - // make sure pointer fields get their child types resolved as well. - // See also similar code for structs. - const prev_status = union_obj.flagsUnordered(ip).status; - errdefer union_obj.setStatus(ip, io, prev_status); - - union_obj.setStatus(ip, io, .fully_resolved_wip); - for (0..union_obj.field_types.len) |field_index| { - const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); - try field_ty.resolveFully(pt); - } - union_obj.setStatus(ip, io, .fully_resolved); - } - - // And let's not forget comptime-only status. - _ = try ty.comptimeOnlySema(pt); -} - -pub fn resolveStructFieldTypes( - sema: *Sema, - ty: InternPool.Index, - struct_type: InternPool.LoadedStructType, -) SemaError!void { - const pt = sema.pt; - const zcu = pt.zcu; - const io = zcu.comp.io; - const ip = &zcu.intern_pool; - - assert(sema.owner.unwrap().type == ty); - - if (struct_type.haveFieldTypes(ip)) return; - - if (struct_type.setFieldTypesWip(ip, io)) { - const msg = try sema.errMsg( - Type.fromInterned(ty).srcLoc(zcu), - "struct '{f}' depends on itself", - .{Type.fromInterned(ty).fmt(pt)}, - ); - return sema.failWithOwnedErrorMsg(null, msg); - } - defer struct_type.clearFieldTypesWip(ip, io); - - // can't happen earlier than this because we only want the progress node if not already resolved - const tracked_unit = zcu.trackUnitSema(struct_type.name.toSlice(ip), null); - defer tracked_unit.end(zcu); - - sema.structFields(struct_type) catch |err| switch (err) { - error.AnalysisFail, error.OutOfMemory, error.Canceled => |e| return e, - error.ComptimeBreak, error.ComptimeReturn => unreachable, - }; -} - -pub fn resolveStructFieldInits(sema: *Sema, ty: Type) SemaError!void { - const pt = sema.pt; - const zcu = pt.zcu; - const io = zcu.comp.io; - const ip = &zcu.intern_pool; - const struct_type = zcu.typeToStruct(ty) orelse return; - - assert(sema.owner.unwrap().type == ty.toIntern()); - - // Inits can start as resolved - if (struct_type.haveFieldInits(ip)) return; - - try sema.resolveStructLayout(ty); - - if (struct_type.setInitsWip(ip, io)) { - const msg = try sema.errMsg( - ty.srcLoc(zcu), - "struct '{f}' depends on itself", - .{ty.fmt(pt)}, - ); - return sema.failWithOwnedErrorMsg(null, msg); - } - defer struct_type.clearInitsWip(ip, io); - - // can't happen earlier than this because we only want the progress node if not already resolved - const tracked_unit = zcu.trackUnitSema(struct_type.name.toSlice(ip), null); - defer tracked_unit.end(zcu); - - sema.structFieldInits(struct_type) catch |err| switch (err) { - error.AnalysisFail, error.OutOfMemory, error.Canceled => |e| return e, - error.ComptimeBreak, error.ComptimeReturn => unreachable, - }; - struct_type.setHaveFieldInits(ip, io); -} - -pub fn resolveUnionFieldTypes(sema: *Sema, ty: Type, union_type: InternPool.LoadedUnionType) SemaError!void { - const pt = sema.pt; - const zcu = pt.zcu; - const io = zcu.comp.io; - const ip = &zcu.intern_pool; - - assert(sema.owner.unwrap().type == ty.toIntern()); - - switch (union_type.flagsUnordered(ip).status) { - .none => {}, - .field_types_wip => { - const msg = try sema.errMsg(ty.srcLoc(zcu), "union '{f}' depends on itself", .{ty.fmt(pt)}); - return sema.failWithOwnedErrorMsg(null, msg); - }, - .have_field_types, - .have_layout, - .layout_wip, - .fully_resolved_wip, - .fully_resolved, - => return, - } - - // can't happen earlier than this because we only want the progress node if not already resolved - const tracked_unit = zcu.trackUnitSema(union_type.name.toSlice(ip), null); - defer tracked_unit.end(zcu); - - union_type.setStatus(ip, io, .field_types_wip); - errdefer union_type.setStatus(ip, io, .none); - sema.unionFields(ty.toIntern(), union_type) catch |err| switch (err) { - error.AnalysisFail, error.OutOfMemory, error.Canceled => |e| return e, - error.ComptimeBreak, error.ComptimeReturn => unreachable, - }; - union_type.setStatus(ip, io, .have_field_types); -} - -/// Returns a normal error set corresponding to the fully populated inferred -/// error set. -fn resolveInferredErrorSet( - sema: *Sema, - block: *Block, - src: LazySrcLoc, - ies_index: InternPool.Index, -) CompileError!InternPool.Index { - const pt = sema.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const func_index = ip.iesFuncIndex(ies_index); - const func = zcu.funcInfo(func_index); - - try sema.declareDependency(.{ .interned = func_index }); // resolved IES - - try zcu.maybeUnresolveIes(func_index); - const resolved_ty = func.resolvedErrorSetUnordered(ip); - if (resolved_ty != .none) return resolved_ty; - - if (zcu.analysis_in_progress.contains(.wrap(.{ .func = func_index }))) { - return sema.fail(block, src, "unable to resolve inferred error set", .{}); - } - - // In order to ensure that all dependencies are properly added to the set, - // we need to ensure the function body is analyzed of the inferred error - // set. However, in the case of comptime/inline function calls with - // inferred error sets, each call gets an adhoc InferredErrorSet object, which - // has no corresponding function body. - const ies_func_info = zcu.typeToFunc(.fromInterned(func.ty)).?; - // if ies declared by a inline function with generic return type, the return_type should be generic_poison, - // because inline function does not create a new declaration, and the ies has been filled with analyzeCall, - // so here we can simply skip this case. - if (ies_func_info.return_type == .generic_poison_type) { - assert(ies_func_info.cc == .@"inline"); - } else if (ip.errorUnionSet(ies_func_info.return_type) == ies_index) { - if (ies_func_info.is_generic) { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "unable to resolve inferred error set of generic function", .{}); - errdefer msg.destroy(sema.gpa); - try sema.errNote(zcu.navSrcLoc(func.owner_nav), msg, "generic function declared here", .{}); - break :msg msg; - }); - } - // In this case we are dealing with the actual InferredErrorSet object that - // corresponds to the function, not one created to track an inline/comptime call. - const orig_func_index = ip.unwrapCoercedFunc(func_index); - try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_func_index })); - try pt.ensureFuncBodyUpToDate(orig_func_index); - } - - // This will now have been resolved by the logic at the end of `Zcu.analyzeFnBody` - // which calls `resolveInferredErrorSetPtr`. - const final_resolved_ty = func.resolvedErrorSetUnordered(ip); - assert(final_resolved_ty != .none); - return final_resolved_ty; -} - -pub fn resolveInferredErrorSetPtr( - sema: *Sema, - block: *Block, - src: LazySrcLoc, - ies: *InferredErrorSet, -) CompileError!void { - const pt = sema.pt; - const ip = &pt.zcu.intern_pool; - - if (ies.resolved != .none) return; - - const ies_index = ip.errorUnionSet(sema.fn_ret_ty.toIntern()); - - for (ies.inferred_error_sets.keys()) |other_ies_index| { - if (ies_index == other_ies_index) continue; - switch (try sema.resolveInferredErrorSet(block, src, other_ies_index)) { - .anyerror_type => { - ies.resolved = .anyerror_type; - return; - }, - else => |error_set_ty_index| { - const names = ip.indexToKey(error_set_ty_index).error_set_type.names; - for (names.get(ip)) |name| { - try ies.errors.put(sema.arena, name, {}); - } - }, - } - } - - const resolved_error_set_ty = try pt.errorSetFromUnsortedNames(ies.errors.keys()); - ies.resolved = resolved_error_set_ty.toIntern(); -} - -fn resolveAdHocInferredErrorSet( - sema: *Sema, - block: *Block, - src: LazySrcLoc, - value: InternPool.Index, -) CompileError!InternPool.Index { - const pt = sema.pt; - const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = comp.gpa; - const io = comp.io; - const ip = &zcu.intern_pool; - - const new_ty = try resolveAdHocInferredErrorSetTy(sema, block, src, ip.typeOf(value)); - if (new_ty == .none) return value; - return ip.getCoerced(gpa, io, pt.tid, value, new_ty); -} - -fn resolveAdHocInferredErrorSetTy( - sema: *Sema, - block: *Block, - src: LazySrcLoc, - ty: InternPool.Index, -) CompileError!InternPool.Index { - const ies = sema.fn_ret_ty_ies orelse return .none; - const pt = sema.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const error_union_info = switch (ip.indexToKey(ty)) { - .error_union_type => |x| x, - else => return .none, - }; - if (error_union_info.error_set_type != .adhoc_inferred_error_set_type) - return .none; - - try sema.resolveInferredErrorSetPtr(block, src, ies); - const new_ty = try pt.intern(.{ .error_union_type = .{ - .error_set_type = ies.resolved, - .payload_type = error_union_info.payload_type, - } }); - return new_ty; -} - -fn resolveInferredErrorSetTy( - sema: *Sema, - block: *Block, - src: LazySrcLoc, - ty: InternPool.Index, -) CompileError!InternPool.Index { - const pt = sema.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - if (ty == .anyerror_type) return ty; - switch (ip.indexToKey(ty)) { - .error_set_type => return ty, - .inferred_error_set_type => return sema.resolveInferredErrorSet(block, src, ty), - else => unreachable, - } -} - -fn structZirInfo(zir: Zir, zir_index: Zir.Inst.Index) struct { - /// fields_len - usize, - Zir.Inst.StructDecl.Small, - /// extra_index - usize, -} { - const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; - assert(extended.opcode == .struct_decl); - const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); - var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len; - - const captures_len = if (small.has_captures_len) blk: { - const captures_len = zir.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - - const fields_len = if (small.has_fields_len) blk: { - const fields_len = zir.extra[extra_index]; - extra_index += 1; - break :blk fields_len; - } else 0; - - const decls_len = if (small.has_decls_len) decls_len: { - const decls_len = zir.extra[extra_index]; - extra_index += 1; - break :decls_len decls_len; - } else 0; - - extra_index += captures_len * 2; - - // The backing integer cannot be handled until `resolveStructLayout()`. - if (small.has_backing_int) { - const backing_int_body_len = zir.extra[extra_index]; - extra_index += 1; // backing_int_body_len - if (backing_int_body_len == 0) { - extra_index += 1; // backing_int_ref - } else { - extra_index += backing_int_body_len; // backing_int_body_inst - } - } - - // Skip over decls. - extra_index += decls_len; - - return .{ fields_len, small, extra_index }; -} - -fn structFields( - sema: *Sema, - struct_type: InternPool.LoadedStructType, -) CompileError!void { - const pt = sema.pt; - const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = comp.gpa; - const io = comp.io; - const ip = &zcu.intern_pool; - - const namespace_index = struct_type.namespace; - const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?; - const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail; - - const fields_len, _, var extra_index = structZirInfo(zir, zir_index); - - if (fields_len == 0) switch (struct_type.layout) { - .@"packed" => { - try sema.backingIntType(struct_type); - return; - }, - .auto, .@"extern" => { - struct_type.setLayoutResolved(ip, io, 0, .none); - return; - }, - }; - - var block_scope: Block = .{ - .parent = null, - .sema = sema, - .namespace = namespace_index, - .instructions = .{}, - .inlining = null, - .comptime_reason = .{ .reason = .{ - .src = .{ - .base_node_inst = struct_type.zir_index, - .offset = .nodeOffset(.zero), - }, - .r = .{ .simple = .type }, - } }, - .src_base_inst = struct_type.zir_index, - .type_name_ctx = struct_type.name, - }; - defer assert(block_scope.instructions.items.len == 0); - - const Field = struct { - type_body_len: u32 = 0, - align_body_len: u32 = 0, - init_body_len: u32 = 0, - type_ref: Zir.Inst.Ref = .none, - }; - const fields = try sema.arena.alloc(Field, fields_len); - - var any_inits = false; - var any_aligned = false; - - { - const bits_per_field = 4; - const fields_per_u32 = 32 / bits_per_field; - const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; - const flags_index = extra_index; - var bit_bag_index: usize = flags_index; - extra_index += bit_bags_count; - var cur_bit_bag: u32 = undefined; - var field_i: u32 = 0; - while (field_i < fields_len) : (field_i += 1) { - if (field_i % fields_per_u32 == 0) { - cur_bit_bag = zir.extra[bit_bag_index]; - bit_bag_index += 1; - } - const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - const has_init = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - const is_comptime = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - - if (is_comptime) struct_type.setFieldComptime(ip, field_i); - - const field_name_zir: [:0]const u8 = zir.nullTerminatedString(@enumFromInt(zir.extra[extra_index])); - extra_index += 1; // field_name - - fields[field_i] = .{}; - - if (has_type_body) { - fields[field_i].type_body_len = zir.extra[extra_index]; - } else { - fields[field_i].type_ref = @enumFromInt(zir.extra[extra_index]); - } - extra_index += 1; - - // This string needs to outlive the ZIR code. - const field_name = try ip.getOrPutString(gpa, io, pt.tid, field_name_zir, .no_embedded_nulls); - assert(struct_type.addFieldName(ip, field_name) == null); - - if (has_align) { - fields[field_i].align_body_len = zir.extra[extra_index]; - extra_index += 1; - any_aligned = true; - } - if (has_init) { - fields[field_i].init_body_len = zir.extra[extra_index]; - extra_index += 1; - any_inits = true; - } - } - } - - // Next we do only types and alignments, saving the inits for a second pass, - // so that init values may depend on type layout. - - for (fields, 0..) |zir_field, field_i| { - const ty_src: LazySrcLoc = .{ - .base_node_inst = struct_type.zir_index, - .offset = .{ .container_field_type = @intCast(field_i) }, - }; - const field_ty: Type = ty: { - if (zir_field.type_ref != .none) { - break :ty try sema.resolveType(&block_scope, ty_src, zir_field.type_ref); - } - assert(zir_field.type_body_len != 0); - const body = zir.bodySlice(extra_index, zir_field.type_body_len); - extra_index += body.len; - const ty_ref = try sema.resolveInlineBody(&block_scope, body, zir_index); - break :ty try sema.analyzeAsType(&block_scope, ty_src, ty_ref); - }; - - struct_type.field_types.get(ip)[field_i] = field_ty.toIntern(); - - if (field_ty.zigTypeTag(zcu) == .@"opaque") { - const msg = msg: { - const msg = try sema.errMsg(ty_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); - errdefer msg.destroy(sema.gpa); - - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(&block_scope, msg); - } - if (field_ty.zigTypeTag(zcu) == .noreturn) { - const msg = msg: { - const msg = try sema.errMsg(ty_src, "struct fields cannot be 'noreturn'", .{}); - errdefer msg.destroy(sema.gpa); - - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(&block_scope, msg); - } - switch (struct_type.layout) { - .@"extern" => if (!try sema.validateExternType(field_ty, .struct_field)) { - const msg = msg: { - const msg = try sema.errMsg(ty_src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); - errdefer msg.destroy(sema.gpa); - - try sema.explainWhyTypeIsNotExtern(msg, ty_src, field_ty, .struct_field); - - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(&block_scope, msg); - }, - .@"packed" => if (!try sema.validatePackedType(field_ty)) { - const msg = msg: { - const msg = try sema.errMsg(ty_src, "packed structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); - errdefer msg.destroy(sema.gpa); - - try sema.explainWhyTypeIsNotPacked(msg, ty_src, field_ty); - - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(&block_scope, msg); - }, - else => {}, - } - - if (zir_field.align_body_len > 0) { - const body = zir.bodySlice(extra_index, zir_field.align_body_len); - extra_index += body.len; - const align_ref = try sema.resolveInlineBody(&block_scope, body, zir_index); - const align_src: LazySrcLoc = .{ - .base_node_inst = struct_type.zir_index, - .offset = .{ .container_field_align = @intCast(field_i) }, - }; - const field_align = try sema.analyzeAsAlign(&block_scope, align_src, align_ref); - struct_type.field_aligns.get(ip)[field_i] = field_align; - } - - extra_index += zir_field.init_body_len; - } - - struct_type.clearFieldTypesWip(ip, io); - if (!any_inits) struct_type.setHaveFieldInits(ip, io); - - try sema.flushExports(); -} - -// This logic must be kept in sync with `structFields` -fn structFieldInits( - sema: *Sema, - struct_type: InternPool.LoadedStructType, -) CompileError!void { - const pt = sema.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - - assert(!struct_type.haveFieldInits(ip)); - - const namespace_index = struct_type.namespace; - const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?; - const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail; - const fields_len, _, var extra_index = structZirInfo(zir, zir_index); - - var block_scope: Block = .{ - .parent = null, - .sema = sema, - .namespace = namespace_index, - .instructions = .{}, - .inlining = null, - .comptime_reason = undefined, // set when `block_scope` is used - .src_base_inst = struct_type.zir_index, - .type_name_ctx = struct_type.name, - }; - defer assert(block_scope.instructions.items.len == 0); - - const Field = struct { - type_body_len: u32 = 0, - align_body_len: u32 = 0, - init_body_len: u32 = 0, - }; - const fields = try sema.arena.alloc(Field, fields_len); - - var any_inits = false; - - { - const bits_per_field = 4; - const fields_per_u32 = 32 / bits_per_field; - const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; - const flags_index = extra_index; - var bit_bag_index: usize = flags_index; - extra_index += bit_bags_count; - var cur_bit_bag: u32 = undefined; - var field_i: u32 = 0; - while (field_i < fields_len) : (field_i += 1) { - if (field_i % fields_per_u32 == 0) { - cur_bit_bag = zir.extra[bit_bag_index]; - bit_bag_index += 1; - } - const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - const has_init = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 2; - const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - - extra_index += 1; // field_name - - fields[field_i] = .{}; - - if (has_type_body) fields[field_i].type_body_len = zir.extra[extra_index]; - extra_index += 1; - - if (has_align) { - fields[field_i].align_body_len = zir.extra[extra_index]; - extra_index += 1; - } - if (has_init) { - fields[field_i].init_body_len = zir.extra[extra_index]; - extra_index += 1; - any_inits = true; - } - } - } - - if (any_inits) { - for (fields, 0..) |zir_field, field_i| { - extra_index += zir_field.type_body_len; - extra_index += zir_field.align_body_len; - const body = zir.bodySlice(extra_index, zir_field.init_body_len); - extra_index += zir_field.init_body_len; - - if (body.len == 0) continue; - - // Pre-populate the type mapping the body expects to be there. - // In init bodies, the zir index of the struct itself is used - // to refer to the current field type. - - const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_i]); - const type_ref = Air.internedToRef(field_ty.toIntern()); - try sema.inst_map.ensureSpaceForInstructions(sema.gpa, &.{zir_index}); - sema.inst_map.putAssumeCapacity(zir_index, type_ref); - - const init_src: LazySrcLoc = .{ - .base_node_inst = struct_type.zir_index, - .offset = .{ .container_field_value = @intCast(field_i) }, - }; - - block_scope.comptime_reason = .{ .reason = .{ - .src = init_src, - .r = .{ .simple = .struct_field_default_value }, - } }; - const init = try sema.resolveInlineBody(&block_scope, body, zir_index); - const coerced = try sema.coerce(&block_scope, field_ty, init, init_src); - const default_val = try sema.resolveConstValue(&block_scope, init_src, coerced, null); - - if (default_val.canMutateComptimeVarState(zcu)) { - return sema.failWithContainsReferenceToComptimeVar( - &block_scope, - init_src, - struct_type.fieldName(ip, field_i), - "field default value", - default_val, - ); - } - struct_type.field_inits.get(ip)[field_i] = default_val.toIntern(); - } - } - - try sema.flushExports(); -} - -fn unionFields( - sema: *Sema, - union_ty: InternPool.Index, - union_type: InternPool.LoadedUnionType, -) CompileError!void { - const tracy = trace(@src()); - defer tracy.end(); - - const pt = sema.pt; - const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = comp.gpa; - const io = comp.io; - const ip = &zcu.intern_pool; - - const zir = zcu.namespacePtr(union_type.namespace).fileScope(zcu).zir.?; - const zir_index = union_type.zir_index.resolve(ip) orelse return error.AnalysisFail; - const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; - assert(extended.opcode == .union_decl); - const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); - const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand); - var extra_index: usize = extra.end; - - const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: { - const ty_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); - extra_index += 1; - break :blk ty_ref; - } else .none; - - const captures_len = if (small.has_captures_len) blk: { - const captures_len = zir.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - - const body_len = if (small.has_body_len) blk: { - const body_len = zir.extra[extra_index]; - extra_index += 1; - break :blk body_len; - } else 0; - - const fields_len = if (small.has_fields_len) blk: { - const fields_len = zir.extra[extra_index]; - extra_index += 1; - break :blk fields_len; - } else 0; - - const decls_len = if (small.has_decls_len) decls_len: { - const decls_len = zir.extra[extra_index]; - extra_index += 1; - break :decls_len decls_len; - } else 0; - - // Skip over captures and decls. - extra_index += captures_len * 2 + decls_len; - - const body = zir.bodySlice(extra_index, body_len); - extra_index += body.len; - - const src: LazySrcLoc = .{ - .base_node_inst = union_type.zir_index, - .offset = .nodeOffset(.zero), - }; - - var block_scope: Block = .{ - .parent = null, - .sema = sema, - .namespace = union_type.namespace, - .instructions = .{}, - .inlining = null, - .comptime_reason = .{ .reason = .{ - .src = src, - .r = .{ .simple = .type }, - } }, - .src_base_inst = union_type.zir_index, - .type_name_ctx = union_type.name, - }; - defer assert(block_scope.instructions.items.len == 0); - - if (body.len != 0) { - _ = try sema.analyzeInlineBody(&block_scope, body, zir_index); - } - - var int_tag_ty: Type = undefined; - var enum_field_names: []InternPool.NullTerminatedString = &.{}; - var enum_field_vals: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty; - var explicit_tags_seen: []bool = &.{}; - if (tag_type_ref != .none) { - const tag_ty_src: LazySrcLoc = .{ - .base_node_inst = union_type.zir_index, - .offset = .{ .node_offset_container_tag = .zero }, - }; - const provided_ty = try sema.resolveType(&block_scope, tag_ty_src, tag_type_ref); - if (small.auto_enum_tag) { - // The provided type is an integer type and we must construct the enum tag type here. - int_tag_ty = provided_ty; - if (int_tag_ty.zigTypeTag(zcu) != .int and int_tag_ty.zigTypeTag(zcu) != .comptime_int) { - return sema.fail(&block_scope, tag_ty_src, "expected integer tag type, found '{f}'", .{int_tag_ty.fmt(pt)}); - } - - if (fields_len > 0) { - const field_count_val = try pt.intValue(.comptime_int, fields_len - 1); - if (!(try sema.intFitsInType(field_count_val, int_tag_ty, null))) { - const msg = msg: { - const msg = try sema.errMsg(tag_ty_src, "specified integer tag type cannot represent every field", .{}); - errdefer msg.destroy(sema.gpa); - try sema.errNote(tag_ty_src, msg, "type '{f}' cannot fit values in range 0...{d}", .{ - int_tag_ty.fmt(pt), - fields_len - 1, - }); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(&block_scope, msg); - } - enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); - try enum_field_vals.ensureTotalCapacity(sema.arena, fields_len); - } - } else { - // The provided type is the enum tag type. - const enum_type = switch (ip.indexToKey(provided_ty.toIntern())) { - .enum_type => ip.loadEnumType(provided_ty.toIntern()), - else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{f}'", .{provided_ty.fmt(pt)}), - }; - union_type.setTagType(ip, io, provided_ty.toIntern()); - // The fields of the union must match the enum exactly. - // A flag per field is used to check for missing and extraneous fields. - explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len); - @memset(explicit_tags_seen, false); - } - } else { - // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis - // purposes, we still auto-generate an enum tag type the same way. That the union is - // untagged is represented by the Type tag (union vs union_tagged). - enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); - } - - var field_types: std.ArrayList(InternPool.Index) = .empty; - var field_aligns: std.ArrayList(InternPool.Alignment) = .empty; - - try field_types.ensureTotalCapacityPrecise(sema.arena, fields_len); - if (small.any_aligned_fields) - try field_aligns.ensureTotalCapacityPrecise(sema.arena, fields_len); - - var max_bits: u64 = 0; - var min_bits: u64 = std.math.maxInt(u64); - var max_bits_src: LazySrcLoc = undefined; - var min_bits_src: LazySrcLoc = undefined; - var max_bits_ty: Type = undefined; - var min_bits_ty: Type = undefined; - const bits_per_field = 4; - const fields_per_u32 = 32 / bits_per_field; - const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; - var bit_bag_index: usize = extra_index; - extra_index += bit_bags_count; - var cur_bit_bag: u32 = undefined; - var field_i: u32 = 0; - var last_tag_val: ?Value = null; - const layout = union_type.flagsUnordered(ip).layout; - while (field_i < fields_len) : (field_i += 1) { - if (field_i % fields_per_u32 == 0) { - cur_bit_bag = zir.extra[bit_bag_index]; - bit_bag_index += 1; - } - const has_type = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - const has_tag = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - const unused = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - _ = unused; - - const field_name_index: Zir.NullTerminatedString = @enumFromInt(zir.extra[extra_index]); - const field_name_zir = zir.nullTerminatedString(field_name_index); - extra_index += 1; - - const field_type_ref: Zir.Inst.Ref = if (has_type) blk: { - const field_type_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); - extra_index += 1; - break :blk field_type_ref; - } else .none; + continue; + }; + if (!coerced_val.eql(existing, .fromInterned(field_ty.*), zcu)) { + comptime_val = null; + break; + } + } - const align_ref: Zir.Inst.Ref = if (has_align) blk: { - const align_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); - extra_index += 1; - break :blk align_ref; - } else .none; + field_val.* = if (comptime_val) |v| v.toIntern() else .none; + } - const tag_ref: Air.Inst.Ref = if (has_tag) blk: { - const tag_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); - extra_index += 1; - break :blk try sema.resolveInst(tag_ref); - } else .none; + const final_ty = try ip.getTupleType(gpa, io, pt.tid, .{ + .types = field_types, + .values = field_vals, + }); - const name_src: LazySrcLoc = .{ - .base_node_inst = union_type.zir_index, - .offset = .{ .container_field_name = field_i }, - }; - const value_src: LazySrcLoc = .{ - .base_node_inst = union_type.zir_index, - .offset = .{ .container_field_value = field_i }, - }; - const align_src: LazySrcLoc = .{ - .base_node_inst = union_type.zir_index, - .offset = .{ .container_field_align = field_i }, - }; - const type_src: LazySrcLoc = .{ - .base_node_inst = union_type.zir_index, - .offset = .{ .container_field_type = field_i }, - }; + return .{ .success = .fromInterned(final_ty) }; + }, - if (enum_field_vals.capacity() > 0) { - const enum_tag_val = if (tag_ref != .none) blk: { - const coerced = try sema.coerce(&block_scope, int_tag_ty, tag_ref, value_src); - const val = try sema.resolveConstDefinedValue(&block_scope, value_src, coerced, .{ .simple = .enum_field_tag_value }); - last_tag_val = val; - - break :blk val; - } else blk: { - if (last_tag_val) |last_tag| { - const result = try arith.incrementDefinedInt(sema, int_tag_ty, last_tag); - if (result.overflow) return sema.fail( - &block_scope, - value_src, - "enumeration value '{f}' too large for type '{f}'", - .{ result.val.fmtValueSema(pt, sema), int_tag_ty.fmt(pt) }, - ); - last_tag_val = result.val; + .exact => { + var expect_ty: ?Type = null; + var first_idx: usize = undefined; + for (peer_tys, 0..) |opt_ty, i| { + const ty = opt_ty orelse continue; + if (expect_ty) |expect| { + if (!ty.eql(expect, zcu)) return .{ .conflict = .{ + .peer_idx_a = first_idx, + .peer_idx_b = i, + } }; } else { - last_tag_val = try pt.intValue(int_tag_ty, 0); + expect_ty = ty; + first_idx = i; } - break :blk last_tag_val.?; - }; - const gop = enum_field_vals.getOrPutAssumeCapacity(enum_tag_val.toIntern()); - if (gop.found_existing) { - const other_value_src: LazySrcLoc = .{ - .base_node_inst = union_type.zir_index, - .offset = .{ .container_field_value = @intCast(gop.index) }, - }; - const msg = msg: { - const msg = try sema.errMsg( - value_src, - "enum tag value {f} already taken", - .{enum_tag_val.fmtValueSema(pt, sema)}, - ); - errdefer msg.destroy(gpa); - try sema.errNote(other_value_src, msg, "other occurrence here", .{}); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(&block_scope, msg); } - } - - // This string needs to outlive the ZIR code. - const field_name = try ip.getOrPutString(gpa, io, pt.tid, field_name_zir, .no_embedded_nulls); - if (enum_field_names.len != 0) { - enum_field_names[field_i] = field_name; - } - - const field_ty: Type = if (!has_type) - .void - else if (field_type_ref == .none) - .noreturn - else - try sema.resolveType(&block_scope, type_src, field_type_ref); - - if (explicit_tags_seen.len > 0) { - const tag_ty = union_type.tagTypeUnordered(ip); - const tag_info = ip.loadEnumType(tag_ty); - const enum_index = tag_info.nameIndex(ip, field_name) orelse { - return sema.fail(&block_scope, name_src, "no field named '{f}' in enum '{f}'", .{ - field_name.fmt(ip), Type.fromInterned(tag_ty).fmt(pt), - }); - }; + return .{ .success = expect_ty.? }; + }, + } +} - // No check for duplicate because the check already happened in order - // to create the enum type in the first place. - assert(!explicit_tags_seen[enum_index]); - explicit_tags_seen[enum_index] = true; +fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type { + // e0 -> e1 + if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e1, e0, src, src)) { + return e1; + } - // Enforce the enum fields and the union fields being in the same order. - if (enum_index != field_i) { - const msg = msg: { - const enum_field_src: LazySrcLoc = .{ - .base_node_inst = Type.fromInterned(tag_ty).typeDeclInstAllowGeneratedTag(zcu).?, - .offset = .{ .container_field_name = enum_index }, - }; - const msg = try sema.errMsg(name_src, "union field '{f}' ordered differently than corresponding enum field", .{ - field_name.fmt(ip), - }); - errdefer msg.destroy(sema.gpa); - try sema.errNote(enum_field_src, msg, "enum field here", .{}); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(&block_scope, msg); - } - } + // e1 -> e0 + if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e0, e1, src, src)) { + return e0; + } - if (field_ty.zigTypeTag(zcu) == .@"opaque") { - const msg = msg: { - const msg = try sema.errMsg(type_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); - errdefer msg.destroy(sema.gpa); + return sema.errorSetMerge(e0, e1); +} - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(&block_scope, msg); - } - switch (layout) { - .@"extern" => if (!try sema.validateExternType(field_ty, .union_field)) { - const msg = msg: { - const msg = try sema.errMsg(type_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); - errdefer msg.destroy(sema.gpa); +fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type { + const target = sema.pt.zcu.getTarget(); - try sema.explainWhyTypeIsNotExtern(msg, type_src, field_ty, .union_field); + // ty_b -> ty_a + if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, false, target, src, src, null)) { + return ty_a; + } - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(&block_scope, msg); - }, - .@"packed" => { - if (!try sema.validatePackedType(field_ty)) { - const msg = msg: { - const msg = try sema.errMsg(type_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); - errdefer msg.destroy(sema.gpa); + // ty_a -> ty_b + if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, false, target, src, src, null)) { + return ty_b; + } - try sema.explainWhyTypeIsNotPacked(msg, type_src, field_ty); + return null; +} - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(&block_scope, msg); - } - const field_bits = try field_ty.bitSizeSema(pt); - if (field_bits >= max_bits) { - max_bits = field_bits; - max_bits_src = type_src; - max_bits_ty = field_ty; - } - if (field_bits <= min_bits) { - min_bits = field_bits; - min_bits_src = type_src; - min_bits_ty = field_ty; +const ArrayLike = struct { + len: u64, + /// `noreturn` indicates that this type is `struct{}` so can coerce to anything + elem_ty: Type, +}; +fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike { + const pt = sema.pt; + const zcu = pt.zcu; + return switch (ty.zigTypeTag(zcu)) { + .array => .{ + .len = ty.arrayLen(zcu), + .elem_ty = ty.childType(zcu), + }, + .@"struct" => { + if (!ty.isTuple(zcu)) return null; + const field_count = ty.structFieldCount(zcu); + if (field_count == 0) return .{ + .len = 0, + .elem_ty = .noreturn, + }; + const elem_ty = ty.fieldType(0, zcu); + for (1..field_count) |i| { + if (!ty.fieldType(i, zcu).eql(elem_ty, zcu)) { + return null; } - }, - .auto => {}, - } - - field_types.appendAssumeCapacity(field_ty.toIntern()); - - if (small.any_aligned_fields) { - field_aligns.appendAssumeCapacity(if (align_ref != .none) - try sema.resolveAlign(&block_scope, align_src, align_ref) - else - .none); - } else { - assert(align_ref == .none); - } - } - - union_type.setFieldTypes(ip, field_types.items); - union_type.setFieldAligns(ip, field_aligns.items); + } + return .{ + .len = field_count, + .elem_ty = elem_ty, + }; + }, + else => null, + }; +} - if (layout == .@"packed" and fields_len != 0 and min_bits != max_bits) { +fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { + const pt = sema.pt; + if (!ty.isIndexable(pt.zcu)) { const msg = msg: { - const msg = try sema.errMsg(src, "packed union has fields with mismatching bit sizes", .{}); + const msg = try sema.errMsg(src, "type '{f}' does not support indexing", .{ty.fmt(pt)}); errdefer msg.destroy(sema.gpa); - try sema.errNote(min_bits_src, msg, "{d} bits here", .{min_bits}); - try sema.addDeclaredHereNote(msg, min_bits_ty); - try sema.errNote(max_bits_src, msg, "{d} bits here", .{max_bits}); - try sema.addDeclaredHereNote(msg, max_bits_ty); + try sema.errNote(src, msg, "operand must be an array, slice, tuple, or vector", .{}); break :msg msg; }; - return sema.failWithOwnedErrorMsg(&block_scope, msg); + return sema.failWithOwnedErrorMsg(block, msg); } +} - if (explicit_tags_seen.len > 0) { - const tag_ty = union_type.tagTypeUnordered(ip); - const tag_info = ip.loadEnumType(tag_ty); - if (tag_info.names.len > fields_len) { - const msg = msg: { - const msg = try sema.errMsg(src, "enum field(s) missing in union", .{}); - errdefer msg.destroy(sema.gpa); - - for (tag_info.names.get(ip), 0..) |field_name, field_index| { - if (explicit_tags_seen[field_index]) continue; - try sema.addFieldErrNote(.fromInterned(tag_ty), field_index, msg, "field '{f}' missing, declared here", .{ - field_name.fmt(ip), - }); - } - try sema.addDeclaredHereNote(msg, .fromInterned(tag_ty)); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(&block_scope, msg); +fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { + const pt = sema.pt; + const zcu = pt.zcu; + if (ty.zigTypeTag(zcu) == .pointer) { + switch (ty.ptrSize(zcu)) { + .slice, .many, .c => return, + .one => { + const elem_ty = ty.childType(zcu); + if (elem_ty.zigTypeTag(zcu) == .array) return; + // TODO https://github.com/ziglang/zig/issues/15479 + // if (elem_ty.isTuple()) return; + }, } - } else if (enum_field_vals.count() > 0) { - const enum_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), union_ty, union_type.name); - union_type.setTagType(ip, io, enum_ty); - } else { - const enum_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_ty, union_type.name); - union_type.setTagType(ip, io, enum_ty); } - - try sema.flushExports(); + const msg = msg: { + const msg = try sema.errMsg(src, "type '{f}' is not an indexable pointer", .{ty.fmt(pt)}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(src, msg, "operand must be a slice, a many pointer or a pointer to an array", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); } -fn generateUnionTagTypeNumbered( +/// Resolves the inferred error set of the given function, so that the corresponding concrete error +/// set is available by calling `InternPool.funcIesResolvedUnordered` on `func_index`. +/// +/// Asserts that `func_index` is a function. Also asserts that it is not a coerced function, because +/// coerced functions do not own inferred error sets. +fn ensureFuncIesResolved( sema: *Sema, block: *Block, - enum_field_names: []const InternPool.NullTerminatedString, - enum_field_vals: []const InternPool.Index, - union_type: InternPool.Index, - union_name: InternPool.NullTerminatedString, -) !InternPool.Index { + src: LazySrcLoc, + func_index: InternPool.Index, +) CompileError!void { const pt = sema.pt; const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = comp.gpa; - const io = comp.io; const ip = &zcu.intern_pool; - const name = try ip.getOrPutStringFmt( - gpa, - io, - pt.tid, - "@typeInfo({f}).@\"union\".tag_type.?", - .{union_name.fmt(ip)}, - .no_embedded_nulls, - ); - - const enum_ty = try ip.getGeneratedTagEnumType(gpa, io, pt.tid, .{ - .name = name, - .owner_union_ty = union_type, - .tag_ty = if (enum_field_vals.len == 0) - (try pt.intType(.unsigned, 0)).toIntern() - else - ip.typeOf(enum_field_vals[0]), - .names = enum_field_names, - .values = enum_field_vals, - .tag_mode = .explicit, - .parent_namespace = block.namespace, - }); - - return enum_ty; -} + assert(ip.unwrapCoercedFunc(func_index) == func_index); -fn generateUnionTagTypeSimple( - sema: *Sema, - block: *Block, - enum_field_names: []const InternPool.NullTerminatedString, - union_type: InternPool.Index, - union_name: InternPool.NullTerminatedString, -) !InternPool.Index { - const pt = sema.pt; - const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = comp.gpa; - const io = comp.io; - const ip = &zcu.intern_pool; + try sema.declareDependency(.{ .func_ies = func_index }); + try sema.addReferenceEntry(block, src, .wrap(.{ .func = func_index })); - const name = try ip.getOrPutStringFmt( - gpa, - io, - pt.tid, - "@typeInfo({f}).@\"union\".tag_type.?", - .{union_name.fmt(ip)}, - .no_embedded_nulls, - ); + const reason: Zcu.DependencyReason = .{ .src = src, .type_layout_reason = undefined }; - const enum_ty = try ip.getGeneratedTagEnumType(gpa, io, pt.tid, .{ - .name = name, - .owner_union_ty = union_type, - .tag_ty = (try pt.smallestUnsignedInt(enum_field_names.len -| 1)).toIntern(), - .names = enum_field_names, - .values = &.{}, - .tag_mode = .auto, - .parent_namespace = block.namespace, - }); + if (zcu.analysis_in_progress.contains(.wrap(.{ .func = func_index }))) { + return sema.failWithDependencyLoop(.wrap(.{ .func = func_index }), &reason); + } - return enum_ty; + try pt.ensureFuncBodyUpToDate(func_index, &reason); } -/// There is another implementation of this in `Type.onePossibleValue`. This one -/// in `Sema` is for calling during semantic analysis, and performs field resolution -/// to get the answer. The one in `Type` is for calling during codegen and asserts -/// that the types are already resolved. -/// TODO assert the return value matches `ty.onePossibleValue` -pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { +pub fn resolveInferredErrorSetPtr( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + ies: *InferredErrorSet, +) CompileError!void { const pt = sema.pt; - const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = comp.gpa; - const io = comp.io; - const ip = &zcu.intern_pool; - - return switch (ty.toIntern()) { - .u0_type, - .i0_type, - => try pt.intValue(ty, 0), - .u1_type, - .u8_type, - .i8_type, - .u16_type, - .i16_type, - .u29_type, - .u32_type, - .i32_type, - .u64_type, - .i64_type, - .u80_type, - .u128_type, - .i128_type, - .u256_type, - .usize_type, - .isize_type, - .c_char_type, - .c_short_type, - .c_ushort_type, - .c_int_type, - .c_uint_type, - .c_long_type, - .c_ulong_type, - .c_longlong_type, - .c_ulonglong_type, - .c_longdouble_type, - .f16_type, - .f32_type, - .f64_type, - .f80_type, - .f128_type, - .anyopaque_type, - .bool_type, - .type_type, - .anyerror_type, - .adhoc_inferred_error_set_type, - .comptime_int_type, - .comptime_float_type, - .enum_literal_type, - .ptr_usize_type, - .ptr_const_comptime_int_type, - .manyptr_u8_type, - .manyptr_const_u8_type, - .manyptr_const_u8_sentinel_0_type, - .manyptr_const_slice_const_u8_type, - .slice_const_u8_type, - .slice_const_u8_sentinel_0_type, - .slice_const_slice_const_u8_type, - .optional_type_type, - .manyptr_const_type_type, - .slice_const_type_type, - .vector_8_i8_type, - .vector_16_i8_type, - .vector_32_i8_type, - .vector_64_i8_type, - .vector_1_u8_type, - .vector_2_u8_type, - .vector_4_u8_type, - .vector_8_u8_type, - .vector_16_u8_type, - .vector_32_u8_type, - .vector_64_u8_type, - .vector_2_i16_type, - .vector_4_i16_type, - .vector_8_i16_type, - .vector_16_i16_type, - .vector_32_i16_type, - .vector_4_u16_type, - .vector_8_u16_type, - .vector_16_u16_type, - .vector_32_u16_type, - .vector_2_i32_type, - .vector_4_i32_type, - .vector_8_i32_type, - .vector_16_i32_type, - .vector_4_u32_type, - .vector_8_u32_type, - .vector_16_u32_type, - .vector_2_i64_type, - .vector_4_i64_type, - .vector_8_i64_type, - .vector_2_u64_type, - .vector_4_u64_type, - .vector_8_u64_type, - .vector_1_u128_type, - .vector_2_u128_type, - .vector_1_u256_type, - .vector_4_f16_type, - .vector_8_f16_type, - .vector_16_f16_type, - .vector_32_f16_type, - .vector_2_f32_type, - .vector_4_f32_type, - .vector_8_f32_type, - .vector_16_f32_type, - .vector_2_f64_type, - .vector_4_f64_type, - .vector_8_f64_type, - .anyerror_void_error_union_type, - => null, - .void_type => Value.void, - .noreturn_type => Value.@"unreachable", - .anyframe_type => unreachable, - .null_type => Value.null, - .undefined_type => Value.undef, - .optional_noreturn_type => try pt.nullValue(ty), - .generic_poison_type => unreachable, - .empty_tuple_type => Value.empty_tuple, - // values, not types - .undef, - .undef_bool, - .undef_usize, - .undef_u1, - .zero, - .zero_usize, - .zero_u1, - .zero_u8, - .one, - .one_usize, - .one_u1, - .one_u8, - .four_u8, - .negative_one, - .void_value, - .unreachable_value, - .null_value, - .bool_true, - .bool_false, - .empty_tuple, - // invalid - .none, - => unreachable, - - _ => switch (ty.toIntern().unwrap(ip).getTag(ip)) { - .removed => unreachable, - - .type_int_signed, // i0 handled above - .type_int_unsigned, // u0 handled above - .type_pointer, - .type_slice, - .type_anyframe, - .type_error_union, - .type_anyerror_union, - .type_error_set, - .type_inferred_error_set, - .type_opaque, - .type_function, - => null, - - .simple_type, // handled above - // values, not types - .undef, - .simple_value, - .ptr_nav, - .ptr_uav, - .ptr_uav_aligned, - .ptr_comptime_alloc, - .ptr_comptime_field, - .ptr_int, - .ptr_eu_payload, - .ptr_opt_payload, - .ptr_elem, - .ptr_field, - .ptr_slice, - .opt_payload, - .opt_null, - .int_u8, - .int_u16, - .int_u32, - .int_i32, - .int_usize, - .int_comptime_int_u32, - .int_comptime_int_i32, - .int_small, - .int_positive, - .int_negative, - .int_lazy_align, - .int_lazy_size, - .error_set_error, - .error_union_error, - .error_union_payload, - .enum_literal, - .enum_tag, - .float_f16, - .float_f32, - .float_f64, - .float_f80, - .float_f128, - .float_c_longdouble_f80, - .float_c_longdouble_f128, - .float_comptime_float, - .variable, - .threadlocal_variable, - .@"extern", - .func_decl, - .func_instance, - .func_coerced, - .only_possible_value, - .union_value, - .bytes, - .aggregate, - .repeated, - // memoized value, not types - .memoized_call, - => unreachable, - - .type_array_big, - .type_array_small, - .type_vector, - .type_enum_auto, - .type_enum_explicit, - .type_enum_nonexhaustive, - .type_struct, - .type_struct_packed, - .type_struct_packed_inits, - .type_tuple, - .type_union, - => switch (ip.indexToKey(ty.toIntern())) { - inline .array_type, .vector_type => |seq_type, seq_tag| { - const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none; - if (seq_type.len + @intFromBool(has_sentinel) == 0) return try pt.aggregateValue(ty, &.{}); - if (try sema.typeHasOnePossibleValue(.fromInterned(seq_type.child))) |opv| { - return try pt.aggregateSplatValue(ty, opv); - } - return null; - }, - - .struct_type => { - // Resolving the layout first helps to avoid loops. - // If the type has a coherent layout, we can recurse through fields safely. - try ty.resolveLayout(pt); - - const struct_type = ip.loadStructType(ty.toIntern()); - - if (struct_type.field_types.len == 0) { - // In this case the struct has no fields at all and - // therefore has one possible value. - return try pt.aggregateValue(ty, &.{}); - } - - const field_vals = try sema.arena.alloc( - InternPool.Index, - struct_type.field_types.len, - ); - for (field_vals, 0..) |*field_val, i| { - if (struct_type.fieldIsComptime(ip, i)) { - try ty.resolveStructFieldInits(pt); - field_val.* = struct_type.field_inits.get(ip)[i]; - continue; - } - const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); - if (try sema.typeHasOnePossibleValue(field_ty)) |field_opv| { - field_val.* = field_opv.toIntern(); - } else return null; - } - - // In this case the struct has no runtime-known fields and - // therefore has one possible value. - return try pt.aggregateValue(ty, field_vals); - }, - - .tuple_type => |tuple| { - try ty.resolveLayout(pt); - - if (tuple.types.len == 0) { - return try pt.aggregateValue(ty, &.{}); - } + const ip = &pt.zcu.intern_pool; - const field_vals = try sema.arena.alloc( - InternPool.Index, - tuple.types.len, - ); - for ( - field_vals, - tuple.types.get(ip), - tuple.values.get(ip), - ) |*field_val, field_ty, field_comptime_val| { - if (field_comptime_val != .none) { - field_val.* = field_comptime_val; - continue; - } - if (try sema.typeHasOnePossibleValue(.fromInterned(field_ty))) |opv| { - field_val.* = opv.toIntern(); - } else return null; - } + if (ies.resolved != .none) return; - return try pt.aggregateValue(ty, field_vals); - }, + const ies_index = ip.errorUnionSet(sema.fn_ret_ty.toIntern()); - .union_type => { - // Resolving the layout first helps to avoid loops. - // If the type has a coherent layout, we can recurse through fields safely. - try ty.resolveLayout(pt); - - const union_obj = ip.loadUnionType(ty.toIntern()); - const tag_val = (try sema.typeHasOnePossibleValue(.fromInterned(union_obj.tagTypeUnordered(ip)))) orelse - return null; - if (union_obj.field_types.len == 0) { - const only = try pt.intern(.{ .empty_enum_value = ty.toIntern() }); - return Value.fromInterned(only); - } - const only_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[0]); - const val_val = (try sema.typeHasOnePossibleValue(only_field_ty)) orelse - return null; - const only = try pt.internUnion(.{ - .ty = ty.toIntern(), - .tag = tag_val.toIntern(), - .val = val_val.toIntern(), - }); - return Value.fromInterned(only); - }, + for (ies.inferred_error_sets.keys()) |other_ies_index| { + if (ies_index == other_ies_index) continue; + const other_func_index = ip.iesFuncIndex(other_ies_index); + try sema.ensureFuncIesResolved(block, src, other_func_index); + switch (ip.funcIesResolvedUnordered(other_func_index)) { + .anyerror_type => { + ies.resolved = .anyerror_type; + return; + }, + else => |error_set_ty_index| { + const names = ip.indexToKey(error_set_ty_index).error_set_type.names; + for (names.get(ip)) |name| { + try ies.errors.put(sema.arena, name, {}); + } + }, + } + } - .enum_type => { - const enum_type = ip.loadEnumType(ty.toIntern()); - switch (enum_type.tag_mode) { - .nonexhaustive => { - if (enum_type.tag_ty == .comptime_int_type) return null; + const resolved_error_set_ty = try pt.errorSetFromUnsortedNames(ies.errors.keys()); + ies.resolved = resolved_error_set_ty.toIntern(); +} - if (try sema.typeHasOnePossibleValue(.fromInterned(enum_type.tag_ty))) |int_opv| { - const only = try pt.intern(.{ .enum_tag = .{ - .ty = ty.toIntern(), - .int = int_opv.toIntern(), - } }); - return Value.fromInterned(only); - } +fn resolveAdHocInferredErrorSet( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + value: InternPool.Index, +) CompileError!InternPool.Index { + const pt = sema.pt; + const zcu = pt.zcu; + const comp = zcu.comp; + const gpa = comp.gpa; + const io = comp.io; + const ip = &zcu.intern_pool; - return null; - }, - .auto, .explicit => { - if (Type.fromInterned(enum_type.tag_ty).hasRuntimeBits(zcu)) return null; - - return Value.fromInterned(switch (enum_type.names.len) { - 0 => try pt.intern(.{ .empty_enum_value = ty.toIntern() }), - 1 => try pt.intern(.{ .enum_tag = .{ - .ty = ty.toIntern(), - .int = if (enum_type.values.len == 0) - (try pt.intValue(.fromInterned(enum_type.tag_ty), 0)).toIntern() - else - try ip.getCoercedInts( - gpa, - io, - pt.tid, - ip.indexToKey(enum_type.values.get(ip)[0]).int, - enum_type.tag_ty, - ), - } }), - else => return null, - }); - }, - } - }, + const new_ty = try resolveAdHocInferredErrorSetTy(sema, block, src, ip.typeOf(value)); + if (new_ty == .none) return value; + return ip.getCoerced(gpa, io, pt.tid, value, new_ty); +} - else => unreachable, - }, +fn resolveAdHocInferredErrorSetTy( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + ty: InternPool.Index, +) CompileError!InternPool.Index { + const ies = sema.fn_ret_ty_ies orelse return .none; + const pt = sema.pt; + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const error_union_info = switch (ip.indexToKey(ty)) { + .error_union_type => |x| x, + else => return .none, + }; + if (error_union_info.error_set_type != .adhoc_inferred_error_set_type) + return .none; - .type_optional => { - const payload_ip = ip.indexToKey(ty.toIntern()).opt_type; - // Although ?noreturn is handled above, the element type - // can be effectively noreturn for example via an empty - // enum or error set. - if (ip.isNoReturn(payload_ip)) return try pt.nullValue(ty); - return null; - }, + try sema.resolveInferredErrorSetPtr(block, src, ies); + const new_ty = try pt.intern(.{ .error_union_type = .{ + .error_set_type = ies.resolved, + .payload_type = error_union_info.payload_type, + } }); + return new_ty; +} + +fn resolveInferredErrorSetTy( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + ty: InternPool.Index, +) CompileError!InternPool.Index { + const pt = sema.pt; + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + if (ty == .anyerror_type) return ty; + switch (ip.indexToKey(ty)) { + .error_set_type => return ty, + .inferred_error_set_type => |func_index| { + try sema.ensureFuncIesResolved(block, src, func_index); + return ip.funcIesResolvedUnordered(func_index); }, - }; + else => unreachable, + } } /// Returns the type of the AIR instruction. @@ -36232,9 +33200,10 @@ fn isComptimeKnown( sema: *Sema, inst: Air.Inst.Ref, ) !bool { - return (try sema.resolveValue(inst)) != null; + return sema.resolveValue(inst) != null; } +/// Asserts that the layout of `var_type` has already been resolved. fn analyzeComptimeAlloc( sema: *Sema, block: *Block, @@ -36245,10 +33214,9 @@ fn analyzeComptimeAlloc( const pt = sema.pt; const zcu = pt.zcu; - // Needed to make an anon decl with type `var_type` (the `finish()` call below). - _ = try sema.typeHasOnePossibleValue(var_type); + var_type.assertHasLayout(zcu); - const ptr_type = try pt.ptrTypeSema(.{ + const ptr_type = try pt.ptrType(.{ .child = var_type.toIntern(), .flags = .{ .alignment = alignment, @@ -36256,13 +33224,23 @@ fn analyzeComptimeAlloc( }, }); - const alloc = try sema.newComptimeAlloc(block, src, var_type, alignment); - - return Air.internedToRef((try pt.intern(.{ .ptr = .{ - .ty = ptr_type.toIntern(), - .base_addr = .{ .comptime_alloc = alloc }, - .byte_offset = 0, - } }))); + if (try var_type.onePossibleValue(pt)) |opv| { + return .fromIntern(try pt.intern(.{ .ptr = .{ + .ty = ptr_type.toIntern(), + .base_addr = .{ .uav = .{ + .val = opv.toIntern(), + .orig_ty = ptr_type.toIntern(), + } }, + .byte_offset = 0, + } })); + } else { + const alloc = try sema.newComptimeAlloc(block, src, var_type, alignment); + return .fromIntern(try pt.intern(.{ .ptr = .{ + .ty = ptr_type.toIntern(), + .base_addr = .{ .comptime_alloc = alloc }, + .byte_offset = 0, + } })); + } } fn resolveAddressSpace( @@ -36272,7 +33250,7 @@ fn resolveAddressSpace( zir_ref: Zir.Inst.Ref, ctx: std.Target.AddressSpaceContext, ) !std.builtin.AddressSpace { - const air_ref = try sema.resolveInst(zir_ref); + const air_ref = sema.resolveInst(zir_ref); return sema.analyzeAsAddressSpace(block, src, air_ref, ctx); } @@ -36363,40 +33341,7 @@ fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError return std.math.cast(usize, int) orelse return sema.fail(block, src, "expression produces integer value '{d}' which is too big for this compiler implementation to handle", .{int}); } -/// For pointer-like optionals, it returns the pointer type. For pointers, -/// the type is returned unmodified. -/// This can return `error.AnalysisFail` because it sometimes requires resolving whether -/// a type has zero bits, which can cause a "foo depends on itself" compile error. -/// This logic must be kept in sync with `Type.isPtrLikeOptional`. -fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type { - const pt = sema.pt; - const zcu = pt.zcu; - return switch (zcu.intern_pool.indexToKey(ty.toIntern())) { - .ptr_type => |ptr_type| switch (ptr_type.flags.size) { - .one, .many, .c => ty, - .slice => null, - }, - .opt_type => |opt_child| switch (zcu.intern_pool.indexToKey(opt_child)) { - .ptr_type => |ptr_type| switch (ptr_type.flags.size) { - .slice, .c => null, - .many, .one => { - if (ptr_type.flags.is_allowzero) return null; - - // optionals of zero sized types behave like bools, not pointers - const payload_ty: Type = .fromInterned(opt_child); - if ((try sema.typeHasOnePossibleValue(payload_ty)) != null) { - return null; - } - - return payload_ty; - }, - }, - else => null, - }, - else => null, - }; -} - +/// Asserts that the layout of `union_ty` is already resolved. fn unionFieldIndex( sema: *Sema, block: *Block, @@ -36407,13 +33352,14 @@ fn unionFieldIndex( const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - try union_ty.resolveFields(pt); const union_obj = zcu.typeToUnion(union_ty).?; - const field_index = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse + const enum_obj = ip.loadEnumType(union_obj.enum_tag_type); + const field_index = enum_obj.nameIndex(ip, field_name) orelse return sema.failWithBadUnionFieldAccess(block, union_ty, union_obj, field_src, field_name); return @intCast(field_index); } +/// Asserts that the layout of `struct_ty` is already resolved. fn structFieldIndex( sema: *Sema, block: *Block, @@ -36424,7 +33370,6 @@ fn structFieldIndex( const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - try struct_ty.resolveFields(pt); const struct_type = zcu.typeToStruct(struct_ty).?; return struct_type.nameIndex(ip, field_name) orelse return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_src, field_name); @@ -36509,102 +33454,25 @@ fn intFromFloatScalar( return pt.getCoerced(cti_result, int_ty); } -/// Asserts the value is an integer, and the destination type is ComptimeInt or Int. -/// Vectors are also accepted. Vector results are reduced with AND. -/// -/// If provided, `vector_index` reports the first element that failed the range check. -fn intFitsInType( - sema: *Sema, - val: Value, - ty: Type, - vector_index: ?*usize, -) CompileError!bool { - const pt = sema.pt; - const zcu = pt.zcu; - if (ty.toIntern() == .comptime_int_type) return true; - const info = ty.intInfo(zcu); - switch (val.toIntern()) { - .zero_usize, .zero_u8 => return true, - else => switch (zcu.intern_pool.indexToKey(val.toIntern())) { - .undef => return true, - .variable, .@"extern", .func, .ptr => { - const target = zcu.getTarget(); - const ptr_bits = target.ptrBitWidth(); - return switch (info.signedness) { - .signed => info.bits > ptr_bits, - .unsigned => info.bits >= ptr_bits, - }; - }, - .int => |int| switch (int.storage) { - .u64, .i64, .big_int => { - var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined; - const big_int = int.storage.toBigInt(&buffer); - return big_int.fitsInTwosComp(info.signedness, info.bits); - }, - .lazy_align => |lazy_ty| { - const max_needed_bits = @as(u16, 16) + @intFromBool(info.signedness == .signed); - // If it is u16 or bigger we know the alignment fits without resolving it. - if (info.bits >= max_needed_bits) return true; - const x = try Type.fromInterned(lazy_ty).abiAlignmentSema(pt); - if (x == .none) return true; - const actual_needed_bits = @as(usize, x.toLog2Units()) + 1 + @intFromBool(info.signedness == .signed); - return info.bits >= actual_needed_bits; - }, - .lazy_size => |lazy_ty| { - const max_needed_bits = @as(u16, 64) + @intFromBool(info.signedness == .signed); - // If it is u64 or bigger we know the size fits without resolving it. - if (info.bits >= max_needed_bits) return true; - const x = try Type.fromInterned(lazy_ty).abiSizeSema(pt); - if (x == 0) return true; - const actual_needed_bits = std.math.log2(x) + 1 + @intFromBool(info.signedness == .signed); - return info.bits >= actual_needed_bits; - }, - }, - .aggregate => |aggregate| { - assert(ty.zigTypeTag(zcu) == .vector); - return switch (aggregate.storage) { - .bytes => |bytes| for (bytes.toSlice(ty.vectorLen(zcu), &zcu.intern_pool), 0..) |byte, i| { - if (byte == 0) continue; - const actual_needed_bits = std.math.log2(byte) + 1 + @intFromBool(info.signedness == .signed); - if (info.bits >= actual_needed_bits) continue; - if (vector_index) |vi| vi.* = i; - break false; - } else true, - .elems, .repeated_elem => for (switch (aggregate.storage) { - .bytes => unreachable, - .elems => |elems| elems, - .repeated_elem => |elem| @as(*const [1]InternPool.Index, &elem), - }, 0..) |elem, i| { - if (try sema.intFitsInType(Value.fromInterned(elem), ty.scalarType(zcu), null)) continue; - if (vector_index) |vi| vi.* = i; - break false; - } else true, - }; - }, - else => unreachable, - }, - } -} - fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool { const pt = sema.pt; - if (!(try int_val.compareAllWithZeroSema(.gte, pt))) return false; + if (!int_val.compareAllWithZero(.gte, pt.zcu)) return false; const end_val = try pt.intValue(tag_ty, end); if (!(try sema.compareAll(int_val, .lt, end_val, tag_ty))) return false; return true; } -/// Asserts the type is an enum. +/// Asserts the type is an exhaustive enum. fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool { const pt = sema.pt; const zcu = pt.zcu; const enum_type = zcu.intern_pool.loadEnumType(ty.toIntern()); - assert(enum_type.tag_mode != .nonexhaustive); + assert(!enum_type.nonexhaustive); // The `tagValueIndex` function call below relies on the type being the integer tag type. // `getCoerced` assumes the value will fit the new type. - if (!(try sema.intFitsInType(int, .fromInterned(enum_type.tag_ty), null))) return false; - const int_coerced = try pt.getCoerced(int, .fromInterned(enum_type.tag_ty)); - + const int_tag_ty: Type = .fromInterned(enum_type.int_tag_type); + if (!int.intFitsInType(int_tag_ty, null, zcu)) return false; + const int_coerced = try pt.getCoerced(int, int_tag_ty); return enum_type.tagValueIndex(&zcu.intern_pool, int_coerced.toIntern()) != null; } @@ -36644,17 +33512,19 @@ fn compareScalar( ty: Type, ) CompileError!bool { const pt = sema.pt; + const zcu = pt.zcu; + const coerced_lhs = try pt.getCoerced(lhs, ty); const coerced_rhs = try pt.getCoerced(rhs, ty); // Equality comparisons of signed zero and NaN need to use floating point semantics - if (coerced_lhs.isFloat(pt.zcu) or coerced_rhs.isFloat(pt.zcu)) - return Value.compareHeteroSema(coerced_lhs, op, coerced_rhs, pt); + if (coerced_lhs.isFloat(zcu) or coerced_rhs.isFloat(zcu)) + return Value.compareHetero(coerced_lhs, op, coerced_rhs, zcu); switch (op) { - .eq => return sema.valuesEqual(coerced_lhs, coerced_rhs, ty), - .neq => return !(try sema.valuesEqual(coerced_lhs, coerced_rhs, ty)), - else => return Value.compareHeteroSema(coerced_lhs, op, coerced_rhs, pt), + .eq => return Value.eql(coerced_lhs, coerced_rhs, ty, zcu), + .neq => return !Value.eql(coerced_lhs, coerced_rhs, ty, zcu), + else => return Value.compareHetero(coerced_lhs, op, coerced_rhs, zcu), } } @@ -36716,25 +33586,6 @@ fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type { return pt.errorSetFromUnsortedNames(names.keys()); } -/// Avoids crashing the compiler when asking if inferred allocations are noreturn. -fn isNoReturn(sema: *Sema, ref: Air.Inst.Ref) bool { - if (ref == .unreachable_value) return true; - if (ref.toIndex()) |inst| switch (sema.air_instructions.items(.tag)[@intFromEnum(inst)]) { - .inferred_alloc, .inferred_alloc_comptime => return false, - else => {}, - }; - return sema.typeOf(ref).isNoReturn(sema.pt.zcu); -} - -/// Avoids crashing the compiler when asking if inferred allocations are known to be a certain zig type. -fn isKnownZigType(sema: *Sema, ref: Air.Inst.Ref, tag: std.builtin.TypeId) bool { - if (ref.toIndex()) |inst| switch (sema.air_instructions.items(.tag)[@intFromEnum(inst)]) { - .inferred_alloc, .inferred_alloc_comptime => return false, - else => {}, - }; - return sema.typeOf(ref).zigTypeTag(sema.pt.zcu) == tag; -} - pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void { const pt = sema.pt; if (!pt.zcu.comp.config.incremental) return; @@ -36742,23 +33593,6 @@ pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void { const gop = try sema.dependencies.getOrPut(sema.gpa, dependee); if (gop.found_existing) return; - // Avoid creating dependencies on ourselves. This situation can arise when we analyze the fields - // of a type and they use `@This()`. This dependency would be unnecessary, and in fact would - // just result in over-analysis since `Zcu.findOutdatedToAnalyze` would never be able to resolve - // the loop. - // Note that this also disallows a `nav_val` - switch (sema.owner.unwrap()) { - .nav_val => |this_nav| switch (dependee) { - .nav_val => |other_nav| if (this_nav == other_nav) return, - else => {}, - }, - .nav_ty => |this_nav| switch (dependee) { - .nav_ty => |other_nav| if (this_nav == other_nav) return, - else => {}, - }, - else => {}, - } - try pt.addDependency(sema.owner, dependee); } @@ -36799,7 +33633,7 @@ fn validateRuntimeValue(sema: *Sema, block: *Block, val_src: LazySrcLoc, val: Ai }); } -fn failWithContainsReferenceToComptimeVar(sema: *Sema, block: *Block, src: LazySrcLoc, value_name: InternPool.NullTerminatedString, kind_of_value: []const u8, val: ?Value) CompileError { +pub fn failWithContainsReferenceToComptimeVar(sema: *Sema, block: *Block, src: LazySrcLoc, value_name: InternPool.NullTerminatedString, kind_of_value: []const u8, val: ?Value) CompileError { return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(src, "{s} contains reference to comptime var", .{kind_of_value}); errdefer msg.destroy(sema.gpa); @@ -36867,11 +33701,7 @@ fn notePathToComptimeAllocPtr( else => {}, // there will be another stage } - const derivation = comptime_ptr.pointerDerivationAdvanced(arena, pt, false, sema) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.Canceled => @panic("TODO"), // pls don't be cancelable mlugg - error.AnalysisFail => unreachable, - }; + const derivation = try comptime_ptr.pointerDerivation(arena, pt, sema); var second_path_aw: std.Io.Writer.Allocating = .init(arena); defer second_path_aw.deinit(); @@ -36983,7 +33813,6 @@ fn anyUndef(sema: *Sema, block: *Block, src: LazySrcLoc, val: Value) !bool { const zcu = pt.zcu; return switch (zcu.intern_pool.indexToKey(val.toIntern())) { .undef => true, - .simple_value => |v| v == .undefined, .slice => { // If the slice contents are runtime-known, reification will fail later on with a // specific error message. @@ -37058,12 +33887,12 @@ fn maybeDerefSliceAsArray( else => unreachable, }; const elem_ty = Type.fromInterned(slice.ty).childType(zcu); - const len = try Value.fromInterned(slice.len).toUnsignedIntSema(pt); + const len = Value.fromInterned(slice.len).toUnsignedInt(zcu); const array_ty = try pt.arrayType(.{ .child = elem_ty.toIntern(), .len = len, }); - const ptr_ty = try pt.ptrTypeSema(p: { + const ptr_ty = try pt.ptrType(p: { var p = Type.fromInterned(slice.ty).ptrInfo(zcu); p.flags.size = .one; p.child = array_ty.toIntern(); @@ -37097,19 +33926,9 @@ pub fn flushExports(sema: *Sema) !void { const zcu = sema.pt.zcu; const gpa = zcu.gpa; - // There may be existing exports. For instance, a struct may export - // things during both field type resolution and field default resolution. - // - // So, pick up and delete any existing exports. This strategy performs - // redundant work, but that's okay, because this case is exceedingly rare. - if (zcu.single_exports.get(sema.owner)) |export_idx| { - try sema.exports.append(gpa, export_idx.ptr(zcu).*); - } else if (zcu.multi_exports.get(sema.owner)) |info| { - try sema.exports.appendSlice(gpa, zcu.all_exports.items[info.index..][0..info.len]); - } - zcu.deleteUnitExports(sema.owner); + assert(!zcu.single_exports.contains(sema.owner)); + assert(!zcu.multi_exports.contains(sema.owner)); - // `sema.exports` is completed; store the data into the `Zcu`. if (sema.exports.items.len == 1) { try zcu.single_exports.ensureUnusedCapacity(gpa, 1); const export_idx: Zcu.Export.Index = zcu.free_exports.pop() orelse idx: { @@ -37129,238 +33948,6 @@ pub fn flushExports(sema: *Sema) !void { } } -/// Called as soon as a `declared` enum type is created. -/// Resolves the tag type and field inits. -/// Marks the `src_inst` dependency on the enum's declaration, so call sites need not do this. -pub fn resolveDeclaredEnum( - pt: Zcu.PerThread, - wip_ty: InternPool.WipEnumType, - inst: Zir.Inst.Index, - tracked_inst: InternPool.TrackedInst.Index, - namespace: InternPool.NamespaceIndex, - type_name: InternPool.NullTerminatedString, - small: Zir.Inst.EnumDecl.Small, - body: []const Zir.Inst.Index, - tag_type_ref: Zir.Inst.Ref, - any_values: bool, - fields_len: u32, - zir: Zir, - body_end: usize, -) Zcu.SemaError!void { - const zcu = pt.zcu; - const gpa = zcu.gpa; - - const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) }; - - var arena: std.heap.ArenaAllocator = .init(gpa); - defer arena.deinit(); - - var comptime_err_ret_trace: std.array_list.Managed(Zcu.LazySrcLoc) = .init(gpa); - defer comptime_err_ret_trace.deinit(); - - var sema: Sema = .{ - .pt = pt, - .gpa = gpa, - .arena = arena.allocator(), - .code = zir, - .owner = .wrap(.{ .type = wip_ty.index }), - .func_index = .none, - .func_is_naked = false, - .fn_ret_ty = .void, - .fn_ret_ty_ies = null, - .comptime_err_ret_trace = &comptime_err_ret_trace, - }; - defer sema.deinit(); - - if (zcu.comp.debugIncremental()) { - const info = try zcu.incremental_debug_state.getUnitInfo(gpa, sema.owner); - info.last_update_gen = zcu.generation; - } - - try sema.declareDependency(.{ .src_hash = tracked_inst }); - - var block: Block = .{ - .parent = null, - .sema = &sema, - .namespace = namespace, - .instructions = .{}, - .inlining = null, - .comptime_reason = .{ .reason = .{ - .src = src, - .r = .{ .simple = .enum_field_values }, - } }, - .src_base_inst = tracked_inst, - .type_name_ctx = type_name, - }; - defer block.instructions.deinit(gpa); - - sema.resolveDeclaredEnumInner( - &block, - wip_ty, - inst, - tracked_inst, - src, - small, - body, - tag_type_ref, - any_values, - fields_len, - zir, - body_end, - ) catch |err| switch (err) { - error.ComptimeBreak => unreachable, - error.ComptimeReturn => unreachable, - error.OutOfMemory, error.Canceled => |e| return e, - error.AnalysisFail => { - if (!zcu.failed_analysis.contains(sema.owner)) { - try zcu.transitive_failed_analysis.put(gpa, sema.owner, {}); - } - return error.AnalysisFail; - }, - }; -} - -fn resolveDeclaredEnumInner( - sema: *Sema, - block: *Block, - wip_ty: InternPool.WipEnumType, - inst: Zir.Inst.Index, - tracked_inst: InternPool.TrackedInst.Index, - src: LazySrcLoc, - small: Zir.Inst.EnumDecl.Small, - body: []const Zir.Inst.Index, - tag_type_ref: Zir.Inst.Ref, - any_values: bool, - fields_len: u32, - zir: Zir, - body_end: usize, -) Zcu.CompileError!void { - const pt = sema.pt; - const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = comp.gpa; - const io = comp.io; - const ip = &zcu.intern_pool; - - const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; - - const tag_ty_src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = .{ .node_offset_container_tag = .zero } }; - - const int_tag_ty = ty: { - if (body.len != 0) { - _ = try sema.analyzeInlineBody(block, body, inst); - } - - if (tag_type_ref != .none) { - const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref); - if (ty.zigTypeTag(zcu) != .int and ty.zigTypeTag(zcu) != .comptime_int) { - return sema.fail(block, tag_ty_src, "expected integer tag type, found '{f}'", .{ty.fmt(pt)}); - } - break :ty ty; - } else if (fields_len == 0) { - break :ty try pt.intType(.unsigned, 0); - } else { - const bits = std.math.log2_int_ceil(usize, fields_len); - break :ty try pt.intType(.unsigned, bits); - } - }; - - wip_ty.setTagTy(ip, int_tag_ty.toIntern()); - - var extra_index = body_end + bit_bags_count; - var bit_bag_index: usize = body_end; - var cur_bit_bag: u32 = undefined; - var last_tag_val: ?Value = null; - for (0..fields_len) |field_i_usize| { - const field_i: u32 = @intCast(field_i_usize); - if (field_i % 32 == 0) { - cur_bit_bag = zir.extra[bit_bag_index]; - bit_bag_index += 1; - } - const has_tag_value = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - - const field_name_index: Zir.NullTerminatedString = @enumFromInt(zir.extra[extra_index]); - const field_name_zir = zir.nullTerminatedString(field_name_index); - extra_index += 1; // field name - - const field_name = try ip.getOrPutString(gpa, io, pt.tid, field_name_zir, .no_embedded_nulls); - - const value_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, - .offset = .{ .container_field_value = field_i }, - }; - - const tag_overflow = if (has_tag_value) overflow: { - const tag_val_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); - extra_index += 1; - const tag_inst = try sema.resolveInst(tag_val_ref); - last_tag_val = try sema.resolveConstDefinedValue(block, .{ - .base_node_inst = tracked_inst, - .offset = .{ .container_field_name = field_i }, - }, tag_inst, .{ .simple = .enum_field_tag_value }); - if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true; - last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty); - if (wip_ty.nextField(ip, field_name, last_tag_val.?.toIntern())) |conflict| { - assert(conflict.kind == .value); // AstGen validated names are unique - const other_field_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, - .offset = .{ .container_field_value = conflict.prev_field_idx }, - }; - const msg = msg: { - const msg = try sema.errMsg(value_src, "enum tag value {f} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)}); - errdefer msg.destroy(gpa); - try sema.errNote(other_field_src, msg, "other occurrence here", .{}); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - break :overflow false; - } else if (any_values) overflow: { - if (last_tag_val) |last_tag| { - const result = try arith.incrementDefinedInt(sema, int_tag_ty, last_tag); - last_tag_val = result.val; - if (result.overflow) break :overflow true; - } else { - last_tag_val = try pt.intValue(int_tag_ty, 0); - } - if (wip_ty.nextField(ip, field_name, last_tag_val.?.toIntern())) |conflict| { - assert(conflict.kind == .value); // AstGen validated names are unique - const other_field_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, - .offset = .{ .container_field_value = conflict.prev_field_idx }, - }; - const msg = msg: { - const msg = try sema.errMsg(value_src, "enum tag value {f} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)}); - errdefer msg.destroy(gpa); - try sema.errNote(other_field_src, msg, "other occurrence here", .{}); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - break :overflow false; - } else overflow: { - assert(wip_ty.nextField(ip, field_name, .none) == null); - last_tag_val = try pt.intValue(.comptime_int, field_i); - if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true; - last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty); - break :overflow false; - }; - - if (tag_overflow) { - const msg = try sema.errMsg(value_src, "enumeration value '{f}' too large for type '{f}'", .{ - last_tag_val.?.fmtValueSema(pt, sema), int_tag_ty.fmt(pt), - }); - return sema.failWithOwnedErrorMsg(block, msg); - } - } - if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) { - if (fields_len >= 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(zcu)) { - return sema.fail(block, src, "non-exhaustive enum specifies every value", .{}); - } - } -} - pub const bitCastVal = @import("Sema/bitcast.zig").bitCast; pub const bitCastSpliceVal = @import("Sema/bitcast.zig").bitCastSplice; @@ -37369,6 +33956,10 @@ const ComptimeLoadResult = @import("Sema/comptime_ptr_access.zig").ComptimeLoadR const storeComptimePtr = @import("Sema/comptime_ptr_access.zig").storeComptimePtr; const ComptimeStoreResult = @import("Sema/comptime_ptr_access.zig").ComptimeStoreResult; +pub const type_resolution = @import("Sema/type_resolution.zig"); +pub const ensureLayoutResolved = type_resolution.ensureLayoutResolved; +pub const ensureStructDefaultsResolved = type_resolution.ensureStructDefaultsResolved; + pub fn getBuiltinType(sema: *Sema, src: LazySrcLoc, decl: Zcu.BuiltinDecl) SemaError!Type { assert(decl.kind() == .type); try sema.ensureMemoizedStateResolved(src, decl.stage()); @@ -37448,62 +34039,95 @@ pub fn resolveNavPtrModifiers( }; } -pub fn analyzeMemoizedState(sema: *Sema, block: *Block, simple_src: LazySrcLoc, builtin_namespace: InternPool.NamespaceIndex, stage: InternPool.MemoizedStateStage) CompileError!bool { - const pt = sema.pt; - const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = comp.gpa; - const io = comp.io; - const ip = &zcu.intern_pool; +pub fn analyzeMemoizedState(sema: *Sema, stage: InternPool.MemoizedStateStage) CompileError!bool { + const pt = sema.pt; + const zcu = pt.zcu; + const comp = zcu.comp; + const gpa = comp.gpa; + const io = comp.io; + const ip = &zcu.intern_pool; + + // This `Block` acts kind of like it's evaluating a `comptime` declaration in the root source + // file of the standard library. In particular, its namespace is the root std namespace. + var block: Block = block: { + // Get the main struct type of the root source file of `std`. No need for a reference entry + // because `std` is always an analysis root. + const std_file_index = zcu.module_roots.get(zcu.std_mod).?.unwrap().?; + try pt.ensureFilePopulated(std_file_index); + const std_type: Type = .fromInterned(zcu.fileRootType(std_file_index)); + break :block .{ + .parent = null, + .sema = sema, + .namespace = std_type.getNamespaceIndex(zcu), + .instructions = .empty, + .inlining = null, + .comptime_reason = null, + .src_base_inst = std_type.typeDeclInst(zcu).?, + .type_name_ctx = .empty, + }; + }; + defer block.instructions.deinit(gpa); + + const std_builtin_ty: Type = ty: { + const std_src = block.nodeOffset(.zero); + const decl_name = try ip.getOrPutString(gpa, io, pt.tid, "builtin", .no_embedded_nulls); + const nav = try sema.namespaceLookup(&block, std_src, block.namespace, decl_name) orelse { + return sema.fail(&block, std_src, "'std' missing 'builtin'", .{}); + }; + const uncoerced_val = try sema.analyzeNavVal(&block, std_src, nav); + const decl_src: LazySrcLoc = .{ + .base_node_inst = ip.getNav(nav).srcInst(ip), + .offset = .nodeOffset(.zero), + }; + break :ty try sema.analyzeAsType(&block, decl_src, .std_builtin_decl, uncoerced_val); + }; var any_changed = false; inline for (comptime std.enums.values(Zcu.BuiltinDecl)) |builtin_decl| { if (stage == comptime builtin_decl.stage()) { - const parent_ns: Zcu.Namespace.Index, const parent_name: []const u8, const name: []const u8 = switch (comptime builtin_decl.access()) { - .direct => |name| .{ builtin_namespace, "std.builtin", name }, + const parent_ns_ty: Type, const parent_name: []const u8, const name: []const u8 = switch (comptime builtin_decl.access()) { + .direct => |name| .{ std_builtin_ty, "std.builtin", name }, .nested => |nested| access: { - const parent_ty: Type = .fromInterned(zcu.builtin_decl_values.get(nested[0])); - const parent_ns = parent_ty.getNamespace(zcu).unwrap() orelse { - return sema.fail(block, simple_src, "std.builtin.{s} is not a container type", .{@tagName(nested[0])}); - }; - break :access .{ parent_ns, "std.builtin." ++ @tagName(nested[0]), nested[1] }; + const parent_decl, const name = nested; + const parent_ty: Type = .fromInterned(zcu.builtin_decl_values.get(parent_decl)); + break :access .{ parent_ty, "std.builtin." ++ @tagName(parent_decl), name }; }, }; + const parent_ns = parent_ns_ty.getNamespace(zcu).unwrap() orelse { + return sema.fail(&block, block.nodeOffset(.zero), "'{s}' is not a container type", .{parent_name}); + }; + const parent_ty_src = parent_ns_ty.srcLoc(zcu); const name_nts = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls); - const nav = try sema.namespaceLookup(block, simple_src, parent_ns, name_nts) orelse - return sema.fail(block, simple_src, "{s} missing {s}", .{ parent_name, name }); + const nav = try sema.namespaceLookup(&block, parent_ty_src, parent_ns, name_nts) orelse { + return sema.fail(&block, parent_ty_src, "'{s}' missing '{s}'", .{ parent_name, name }); + }; + const uncoerced_val = try sema.analyzeNavVal(&block, parent_ty_src, nav); - const src: LazySrcLoc = .{ + const decl_src: LazySrcLoc = .{ .base_node_inst = ip.getNav(nav).srcInst(ip), .offset = .nodeOffset(.zero), }; - const result = try sema.analyzeNavVal(block, src, nav); - - const uncoerced_val = try sema.resolveConstDefinedValue(block, src, result, null); - const maybe_lazy_val: Value = switch (builtin_decl.kind()) { - .type => if (uncoerced_val.typeOf(zcu).zigTypeTag(zcu) != .type) { - return sema.fail(block, src, "{s}.{s} is not a type", .{ parent_name, name }); - } else val: { - try uncoerced_val.toType().resolveFully(pt); - break :val uncoerced_val; + const val: Value = switch (builtin_decl.kind()) { + .type => val: { + const ty = try sema.analyzeAsType(&block, decl_src, .std_builtin_decl, uncoerced_val); + try sema.ensureLayoutResolved(ty, decl_src, .builtin_type); + break :val ty.toValue(); }, .func => val: { const func_ty = try sema.getExpectedBuiltinFnType(builtin_decl); - const coerced = try sema.coerce(block, func_ty, Air.internedToRef(uncoerced_val.toIntern()), src); - break :val .fromInterned(coerced.toInterned().?); + const coerced = try sema.coerce(&block, func_ty, uncoerced_val, decl_src); + break :val try sema.resolveConstDefinedValue(&block, decl_src, coerced, .{ .simple = .std_builtin_decl }); }, .string => val: { - const coerced = try sema.coerce(block, .slice_const_u8, Air.internedToRef(uncoerced_val.toIntern()), src); - break :val .fromInterned(coerced.toInterned().?); + const coerced = try sema.coerce(&block, .slice_const_u8, uncoerced_val, decl_src); + break :val try sema.resolveConstDefinedValue(&block, decl_src, coerced, .{ .simple = .std_builtin_decl }); }, }; - const val = try sema.resolveLazyValue(maybe_lazy_val); - const prev = zcu.builtin_decl_values.get(builtin_decl); - if (val.toIntern() != prev) { + if (zcu.builtin_decl_values.get(builtin_decl) != val.toIntern()) { zcu.builtin_decl_values.set(builtin_decl, val.toIntern()); any_changed = true; } @@ -37539,7 +34163,6 @@ fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Typ => try pt.funcType(.{ .param_types = &.{ .generic_poison_type, .generic_poison_type }, .return_type = .noreturn_type, - .is_generic = true, }), // `fn (anyerror) noreturn` @@ -37590,3 +34213,372 @@ fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Typ else => unreachable, }; } + +pub fn setTypeName( + sema: *Sema, + block: *Block, + wip: *const InternPool.WipContainerType, + name_strategy: Zir.Inst.NameStrategy, + anon_prefix: []const u8, + inst: Zir.Inst.Index, +) CompileError!void { + const pt = sema.pt; + const zcu = pt.zcu; + const comp = zcu.comp; + const gpa = comp.gpa; + const io = comp.io; + const ip = &zcu.intern_pool; + + strat: switch (name_strategy) { + .anon => { + // It would be neat to have "struct:line:column" but this name has + // to survive incremental updates, where it may have been shifted down + // or up to a different line, but unchanged, and thus not unnecessarily + // semantically analyzed. + // TODO: that would be possible, by detecting line number changes and renaming + // types appropriately. However, `@typeName` becomes a problem then. If we remove + // that builtin from the language, we can consider this. + wip.setName(ip, try ip.getOrPutStringFmt( + gpa, + io, + pt.tid, + "{f}__{s}_{d}", + .{ block.type_name_ctx.fmt(ip), anon_prefix, @intFromEnum(wip.index) }, + .no_embedded_nulls, + ), .none); + }, + .parent => wip.setName(ip, block.type_name_ctx, sema.owner.unwrap().nav_val.toOptional()), + .func => { + const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index).resolve(ip) orelse return error.AnalysisFail); + const zir_tags = sema.code.instructions.items(.tag); + + var aw: std.Io.Writer.Allocating = .init(gpa); + defer aw.deinit(); + const w = &aw.writer; + w.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory; + + var arg_i: usize = 0; + for (fn_info.param_body) |zir_inst| switch (zir_tags[@intFromEnum(zir_inst)]) { + .param, .param_comptime, .param_anytype, .param_anytype_comptime => { + const arg = sema.inst_map.get(zir_inst).?; + // If this is being called in a generic function then analyzeCall will + // have already resolved the args and this will work. + // If not then this is a struct type being returned from a non-generic + // function and the name doesn't matter since it will later + // result in a compile error. + const arg_val = sema.resolveValue(arg) orelse { + continue :strat .anon; + }; + + if (arg_i != 0) w.writeByte(',') catch return error.OutOfMemory; + + // Limiting the depth here helps avoid type names getting too long, which + // in turn helps to avoid unreasonably long symbol names for namespaced + // symbols. Such names should ideally be human-readable, and additionally, + // some tooling may not support very long symbol names. + w.print("{f}", .{Value.fmtValueSemaFull(.{ + .val = arg_val, + .pt = pt, + .opt_sema = sema, + .depth = 1, + })}) catch return error.OutOfMemory; + + arg_i += 1; + continue; + }, + else => continue, + }; + + w.writeByte(')') catch return error.OutOfMemory; + const name = try ip.getOrPutString(gpa, io, pt.tid, aw.written(), .no_embedded_nulls); + wip.setName(ip, name, .none); + }, + .dbg_var => { + // TODO: this logic is questionable. We ideally should be traversing the `Block` rather than relying on the order of AstGen instructions. + const ref = inst.toRef(); + const zir_tags = sema.code.instructions.items(.tag); + const zir_data = sema.code.instructions.items(.data); + const var_name = for (@intFromEnum(inst)..zir_tags.len) |i| switch (zir_tags[i]) { + .dbg_var_ptr, .dbg_var_val => if (zir_data[i].str_op.operand == ref) { + break zir_data[i].str_op.getStr(sema.code); + }, + else => {}, + } else { + continue :strat .anon; + }; + const name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.{s}", .{ + block.type_name_ctx.fmt(ip), var_name, + }, .no_embedded_nulls); + wip.setName(ip, name, .none); + }, + } +} + +fn zirStructDecl( + sema: *Sema, + block: *Block, + inst: Zir.Inst.Index, +) CompileError!Air.Inst.Ref { + const pt = sema.pt; + const zcu = pt.zcu; + const comp = zcu.comp; + const gpa = comp.gpa; + const io = comp.io; + const ip = &zcu.intern_pool; + + const tracked_inst = try block.trackZir(inst); + + const src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .nodeOffset(.zero), + }; + + const struct_decl = sema.code.getStructDecl(inst); + + const captures = try sema.getCaptures(block, src, struct_decl.captures, struct_decl.capture_names); + + const ty: Type = switch (try ip.getDeclaredStructType(gpa, io, pt.tid, .{ + .zir_index = tracked_inst, + .captures = captures, + .fields_len = @intCast(struct_decl.field_names.len), + .layout = struct_decl.layout, + .any_comptime_fields = struct_decl.field_comptime_bits != null, + .any_field_defaults = struct_decl.field_default_body_lens != null, + .any_field_aligns = struct_decl.field_align_body_lens != null, + .packed_backing_mode = if (struct_decl.backing_int_type_body != null) .explicit else .auto, + })) { + .existing => |ty| .fromInterned(ty), + .wip => |wip| ty: { + errdefer wip.cancel(ip, pt.tid); + try sema.setTypeName(block, &wip, struct_decl.name_strategy, "struct", inst); + const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ + .parent = block.namespace.toOptional(), + .owner_type = wip.index, + .file_scope = block.getFileScopeIndex(zcu), + .generation = zcu.generation, + }); + errdefer pt.destroyNamespace(new_namespace_index); + try pt.scanNamespace(new_namespace_index, struct_decl.decls); + if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); + break :ty .fromInterned(wip.finish(ip, new_namespace_index)); + }, + }; + + try sema.addTypeReferenceEntry(src, ty); + try pt.ensureNamespaceUpToDate(ty.getNamespaceIndex(zcu)); + + return .fromType(ty); +} +fn zirUnionDecl( + sema: *Sema, + block: *Block, + inst: Zir.Inst.Index, +) CompileError!Air.Inst.Ref { + const pt = sema.pt; + const zcu = pt.zcu; + const comp = zcu.comp; + const gpa = comp.gpa; + const io = comp.io; + const ip = &zcu.intern_pool; + + const tracked_inst = try block.trackZir(inst); + + const src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .nodeOffset(.zero), + }; + + const union_decl = sema.code.getUnionDecl(inst); + + const captures = try sema.getCaptures(block, src, union_decl.captures, union_decl.capture_names); + + const ty: Type = switch (try ip.getDeclaredUnionType(gpa, io, pt.tid, .{ + .zir_index = tracked_inst, + .captures = captures, + .fields_len = @intCast(union_decl.field_names.len), + .layout = union_decl.kind.layout(), + .any_field_aligns = union_decl.field_align_body_lens != null, + .tag_usage = switch (union_decl.kind) { + .auto => if (block.wantSafeTypes()) .safety else .none, + + .tagged_explicit, + .tagged_enum, + .tagged_enum_explicit, + => .tagged, + + .@"extern", + .@"packed", + .packed_explicit, + => .none, + }, + .enum_tag_mode = switch (union_decl.kind) { + .tagged_explicit => .explicit, + else => .auto, + }, + .packed_backing_mode = switch (union_decl.kind) { + .packed_explicit => .explicit, + else => .auto, + }, + })) { + .existing => |ty| .fromInterned(ty), + .wip => |wip| ty: { + errdefer wip.cancel(ip, pt.tid); + try sema.setTypeName(block, &wip, union_decl.name_strategy, "union", inst); + const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ + .parent = block.namespace.toOptional(), + .owner_type = wip.index, + .file_scope = block.getFileScopeIndex(zcu), + .generation = zcu.generation, + }); + errdefer pt.destroyNamespace(new_namespace_index); + try pt.scanNamespace(new_namespace_index, union_decl.decls); + if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); + break :ty .fromInterned(wip.finish(ip, new_namespace_index)); + }, + }; + + try sema.addTypeReferenceEntry(src, ty); + try pt.ensureNamespaceUpToDate(ty.getNamespaceIndex(zcu)); + + return .fromType(ty); +} +fn zirEnumDecl( + sema: *Sema, + block: *Block, + inst: Zir.Inst.Index, +) CompileError!Air.Inst.Ref { + const pt = sema.pt; + const zcu = pt.zcu; + const comp = zcu.comp; + const gpa = comp.gpa; + const io = comp.io; + const ip = &zcu.intern_pool; + + const tracked_inst = try block.trackZir(inst); + + const src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .nodeOffset(.zero), + }; + + const enum_decl = sema.code.getEnumDecl(inst); + + const captures = try sema.getCaptures(block, src, enum_decl.captures, enum_decl.capture_names); + + const ty: Type = switch (try ip.getDeclaredEnumType(gpa, io, pt.tid, .{ + .zir_index = tracked_inst, + .captures = captures, + .fields_len = @intCast(enum_decl.field_names.len), + .nonexhaustive = enum_decl.nonexhaustive, + .int_tag_mode = if (enum_decl.tag_type_body != null) .explicit else .auto, + })) { + .existing => |ty| .fromInterned(ty), + .wip => |wip| ty: { + errdefer wip.cancel(ip, pt.tid); + try sema.setTypeName(block, &wip, enum_decl.name_strategy, "enum", inst); + const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ + .parent = block.namespace.toOptional(), + .owner_type = wip.index, + .file_scope = block.getFileScopeIndex(zcu), + .generation = zcu.generation, + }); + errdefer pt.destroyNamespace(new_namespace_index); + try pt.scanNamespace(new_namespace_index, enum_decl.decls); + if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); + break :ty .fromInterned(wip.finish(ip, new_namespace_index)); + }, + }; + + try sema.addTypeReferenceEntry(src, ty); + try pt.ensureNamespaceUpToDate(ty.getNamespaceIndex(zcu)); + + return .fromType(ty); +} +fn zirOpaqueDecl( + sema: *Sema, + block: *Block, + inst: Zir.Inst.Index, +) CompileError!Air.Inst.Ref { + const pt = sema.pt; + const zcu = pt.zcu; + const comp = zcu.comp; + const gpa = comp.gpa; + const io = comp.io; + const ip = &zcu.intern_pool; + + const tracked_inst = try block.trackZir(inst); + + const src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .nodeOffset(.zero), + }; + + const opaque_decl = sema.code.getOpaqueDecl(inst); + + const captures = try sema.getCaptures(block, src, opaque_decl.captures, opaque_decl.capture_names); + + const ty: Type = switch (try ip.getDeclaredOpaqueType(gpa, io, pt.tid, .{ + .zir_index = tracked_inst, + .captures = captures, + })) { + .existing => |ty| .fromInterned(ty), + .wip => |wip| ty: { + errdefer wip.cancel(ip, pt.tid); + try sema.setTypeName(block, &wip, opaque_decl.name_strategy, "opaque", inst); + const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ + .parent = block.namespace.toOptional(), + .owner_type = wip.index, + .file_scope = block.getFileScopeIndex(zcu), + .generation = zcu.generation, + }); + errdefer pt.destroyNamespace(new_namespace_index); + try pt.scanNamespace(new_namespace_index, opaque_decl.decls); + if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); + break :ty .fromInterned(wip.finish(ip, new_namespace_index)); + }, + }; + + try sema.addTypeReferenceEntry(src, ty); + try pt.ensureNamespaceUpToDate(ty.getNamespaceIndex(zcu)); + + return .fromType(ty); +} + +/// Registers an error indicating a dependency loop: we have introduced a dependency on `want` (with +/// reason `want_reason`) but have learnt that `want` is already in `zcu.analysis_in_progress`. +pub fn failWithDependencyLoop( + sema: *Sema, + want: AnalUnit, + want_reason: *const Zcu.DependencyReason, +) SemaError { + const pt = sema.pt; + const zcu = pt.zcu; + const gpa = zcu.comp.gpa; + + const in_progress_len = zcu.analysis_in_progress.count(); + var index = zcu.analysis_in_progress.getIndex(want).? + 1; + + try zcu.dependency_loops.ensureUnusedCapacity(gpa, 1); + try zcu.dependency_loop_nodes.ensureUnusedCapacity(gpa, in_progress_len - index + 1); + + zcu.dependency_loops.putAssumeCapacityNoClobber(want, {}); + + while (index <= in_progress_len) : (index += 1) { + const parent_unit = zcu.analysis_in_progress.keys()[index - 1]; + const unit, const reason = if (index == in_progress_len) .{ + want, + want_reason, + } else .{ + zcu.analysis_in_progress.keys()[index], + zcu.analysis_in_progress.values()[index], + }; + + zcu.dependency_loop_nodes.putAssumeCapacityNoClobber(parent_unit, .{ + .unit = unit, + .reason = reason.?.*, + }); + } + + // A dependency loop error will be reported. Mark us all as transitive failures. + return error.AnalysisFail; +} diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig @@ -129,84 +129,73 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter for (0..init.names.len) |i| { elems[i] = try self.lowerExprAnonResTy(init.vals.at(@intCast(i))); } - const struct_ty = switch (try ip.getStructType( - gpa, - io, - pt.tid, - .{ - .layout = .auto, - .fields_len = @intCast(init.names.len), - .known_non_opv = false, - .requires_comptime = .no, - .any_comptime_fields = true, - .any_default_inits = true, - .inits_resolved = true, - .any_aligned_fields = false, - .key = .{ .reified = .{ - .zir_index = self.base_node_inst, - .type_hash = hash: { - var hasher: std.hash.Wyhash = .init(0); - hasher.update(std.mem.asBytes(&node)); - hasher.update(std.mem.sliceAsBytes(elems)); - hasher.update(std.mem.sliceAsBytes(init.names)); - break :hash hasher.final(); - }, - } }, + const struct_ty: Type = switch (try ip.getReifiedStructType(gpa, io, pt.tid, .{ + .zir_index = self.base_node_inst, + .type_hash = hash: { + var hasher: std.hash.Wyhash = .init(0); + hasher.update(std.mem.asBytes(&node)); + hasher.update(std.mem.sliceAsBytes(elems)); + hasher.update(std.mem.sliceAsBytes(init.names)); + break :hash hasher.final(); }, - false, - )) { + .fields_len = @intCast(init.names.len), + .layout = .auto, + .any_comptime_fields = true, + .any_field_defaults = true, + .any_field_aligns = false, + .packed_backing_int_type = .none, + })) { + .existing => |ty| .fromInterned(ty), .wip => |wip| ty: { errdefer wip.cancel(ip, pt.tid); - const type_name = try self.sema.createTypeName( - self.block, - .anon, - "struct", - self.base_node_inst.resolve(ip), - wip.index, - ); - wip.setName(ip, type_name.name, type_name.nav); - - const struct_type = ip.loadStructType(wip.index); - - for (init.names, 0..) |name, field_idx| { - const name_interned = try ip.getOrPutString( + const block = self.block; + const zcu = pt.zcu; + try self.sema.setTypeName(block, &wip, .anon, "struct", self.base_node_inst.resolve(ip).?); + + // Reified structs have field information populated immediately. + @memcpy(wip.field_values.get(ip), elems); + if (init.names.len > 0) { + // All fields are comptime, but unused bits remain zeroed. + const unused_bits = switch (init.names.len % 32) { + 0 => 0, + else => |n| 32 - n, + }; + const comptime_bits = wip.field_is_comptime_bits.getAll(ip); + @memset(comptime_bits[0 .. comptime_bits.len - 1], std.math.maxInt(u32)); + comptime_bits[comptime_bits.len - 1] = @as(u32, std.math.maxInt(u32)) >> @intCast(unused_bits); + } + for ( + init.names, + wip.field_names.get(ip), + wip.field_types.get(ip), + wip.field_values.get(ip), + ) |zoir_name, *field_name, *field_ty, field_val| { + field_name.* = try ip.getOrPutString( gpa, io, pt.tid, - name.get(self.file.zoir.?), + zoir_name.get(self.file.zoir.?), .no_embedded_nulls, ); - assert(struct_type.addFieldName(ip, name_interned) == null); - struct_type.setFieldComptime(ip, field_idx); - } - - @memcpy(struct_type.field_inits.get(ip), elems); - const types = struct_type.field_types.get(ip); - for (0..init.names.len) |i| { - types[i] = Value.fromInterned(elems[i]).typeOf(pt.zcu).toIntern(); + field_ty.* = ip.typeOf(field_val); } const new_namespace_index = try pt.createNamespace(.{ - .parent = self.block.namespace.toOptional(), + .parent = block.namespace.toOptional(), .owner_type = wip.index, - .file_scope = self.block.getFileScopeIndex(pt.zcu), - .generation = pt.zcu.generation, + .file_scope = block.getFileScopeIndex(zcu), + .generation = zcu.generation, }); - try pt.zcu.comp.queueJob(.{ .resolve_type_fully = wip.index }); - codegen_type: { - if (pt.zcu.comp.config.use_llvm) break :codegen_type; - if (self.block.ownerModule().strip) break :codegen_type; - pt.zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); - try pt.zcu.comp.queueJob(.{ .link_type = wip.index }); - } - break :ty wip.finish(ip, new_namespace_index); + errdefer pt.destroyNamespace(new_namespace_index); + if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); + break :ty .fromInterned(wip.finish(ip, new_namespace_index)); }, - .existing => |ty| ty, }; - try self.sema.declareDependency(.{ .interned = struct_ty }); try self.sema.addTypeReferenceEntry(self.nodeSrc(node), struct_ty); + // No need for `ensureNamespaceUpToDate` because this type's namespace is always empty. + try self.sema.ensureLayoutResolved(struct_ty, self.nodeSrc(node), .init); - return (try pt.aggregateValue(.fromInterned(struct_ty), elems)).toIntern(); + return (try pt.aggregateValue(struct_ty, elems)).toIntern(); }, } } @@ -299,7 +288,7 @@ fn checkTypeInner( } else { const gop = try visited.getOrPut(sema.arena, ty.toIntern()); if (gop.found_existing) return; - try ty.resolveFields(pt); + try sema.ensureLayoutResolved(ty, self.import_loc, .init); const struct_info = zcu.typeToStruct(ty).?; for (struct_info.field_types.get(ip)) |field_type| { try self.checkTypeInner(.fromInterned(field_type), null, visited); @@ -308,7 +297,7 @@ fn checkTypeInner( .@"union" => { const gop = try visited.getOrPut(sema.arena, ty.toIntern()); if (gop.found_existing) return; - try ty.resolveFields(pt); + try sema.ensureLayoutResolved(ty, self.import_loc, .init); const union_info = zcu.typeToUnion(ty).?; for (union_info.field_types.get(ip)) |field_type| { if (field_type != .void_type) { @@ -645,6 +634,7 @@ fn lowerEnum(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.I const gpa = comp.gpa; const io = comp.io; const ip = &pt.zcu.intern_pool; + try self.sema.ensureLayoutResolved(res_ty, self.import_loc, .init); switch (node.get(self.file.zoir.?)) { .enum_literal => |field_name| { const field_name_interned = try ip.getOrPutString( @@ -767,8 +757,8 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool const io = comp.io; const ip = &pt.zcu.intern_pool; - try res_ty.resolveFields(self.sema.pt); - try res_ty.resolveStructFieldInits(self.sema.pt); + try self.sema.ensureLayoutResolved(res_ty, self.import_loc, .init); + try self.sema.ensureStructDefaultsResolved(res_ty, self.import_loc); const struct_info = self.sema.pt.zcu.typeToStruct(res_ty).?; const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) { @@ -779,7 +769,7 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool const field_values = try self.sema.arena.alloc(InternPool.Index, struct_info.field_names.len); - const field_defaults = struct_info.field_inits.get(ip); + const field_defaults = struct_info.field_defaults.get(ip); if (field_defaults.len > 0) { @memcpy(field_values, field_defaults); } else { @@ -803,7 +793,7 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool const field_type: Type = .fromInterned(struct_info.field_types.get(ip)[name_index]); field_values[name_index] = try self.lowerExprKnownResTy(field_node, field_type); - if (struct_info.comptime_bits.getBit(ip, name_index)) { + if (struct_info.field_is_comptime_bits.get(ip, name_index)) { const val = ip.indexToKey(field_values[name_index]); const default = ip.indexToKey(field_defaults[name_index]); if (!val.eql(default, ip)) { @@ -918,9 +908,9 @@ fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. const gpa = comp.gpa; const io = comp.io; const ip = &pt.zcu.intern_pool; - try res_ty.resolveFields(self.sema.pt); - const union_info = self.sema.pt.zcu.typeToUnion(res_ty).?; - const enum_tag_info = union_info.loadTagType(ip); + try self.sema.ensureLayoutResolved(res_ty, self.import_loc, .init); + const union_info = pt.zcu.typeToUnion(res_ty).?; + const enum_tag_info = ip.loadEnumType(union_info.enum_tag_type); const field_name, const maybe_field_node = switch (node.get(self.file.zoir.?)) { .enum_literal => |name| b: { @@ -956,7 +946,7 @@ fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. const name_index = enum_tag_info.nameIndex(ip, field_name) orelse { return error.WrongType; }; - const tag = try self.sema.pt.enumValueFieldIndex(.fromInterned(union_info.enum_tag_ty), name_index); + const tag = try self.sema.pt.enumValueFieldIndex(.fromInterned(union_info.enum_tag_type), name_index); const field_type: Type = .fromInterned(union_info.field_types.get(ip)[name_index]); const val = if (maybe_field_node) |field_node| b: { if (field_type.toIntern() == .void_type) { diff --git a/src/Sema/arith.zig b/src/Sema/arith.zig @@ -20,6 +20,9 @@ pub fn incrementDefinedInt( const zcu = pt.zcu; assert(prev_val.typeOf(zcu).toIntern() == ty.toIntern()); assert(!prev_val.isUndef(zcu)); + if (ty.intInfo(zcu).bits == 0) { + return .{ .overflow = true, .val = try comptimeIntAdd(sema, prev_val, .one_comptime_int) }; + } const res = try intAdd(sema, prev_val, try pt.intValue(ty, 1), ty); return .{ .overflow = res.overflow, .val = res.val }; } @@ -1053,7 +1056,7 @@ fn shlScalar( if (rhs_val.isUndef(zcu)) return rhs_val; }, } - switch (try rhs_val.orderAgainstZeroSema(pt)) { + switch (Value.order(rhs_val, .zero_comptime_int, zcu)) { .gt => {}, .eq => return lhs_val, .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, vec_idx), @@ -1090,7 +1093,7 @@ fn shlWithOverflowScalar( if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx); if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx); - switch (try rhs_val.orderAgainstZeroSema(pt)) { + switch (Value.order(rhs_val, .zero_comptime_int, zcu)) { .gt => {}, .eq => return .{ .overflow_bit = .zero_u1, .wrapped_result = lhs_val }, .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, vec_idx), @@ -1169,7 +1172,7 @@ fn shrScalar( if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx); if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx); - switch (try rhs_val.orderAgainstZeroSema(pt)) { + switch (Value.order(rhs_val, .zero_comptime_int, zcu)) { .gt => {}, .eq => return lhs_val, .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, vec_idx), @@ -1430,8 +1433,8 @@ fn intAddWithOverflowInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value const info = ty.intInfo(zcu); var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = try lhs.toBigIntSema(&lhs_space, pt); - const rhs_bigint = try rhs.toBigIntSema(&rhs_space, pt); + const lhs_bigint = lhs.toBigInt(&lhs_space, zcu); + const rhs_bigint = rhs.toBigInt(&rhs_space, zcu); const limbs = try sema.arena.alloc( std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(info.bits), @@ -1512,8 +1515,8 @@ fn intSubWithOverflowInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value const info = ty.intInfo(zcu); var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = try lhs.toBigIntSema(&lhs_space, pt); - const rhs_bigint = try rhs.toBigIntSema(&rhs_space, pt); + const lhs_bigint = lhs.toBigInt(&lhs_space, zcu); + const rhs_bigint = rhs.toBigInt(&rhs_space, zcu); const limbs = try sema.arena.alloc( std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(info.bits), @@ -1597,8 +1600,8 @@ fn intMulWithOverflowInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value const info = ty.intInfo(zcu); var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = try lhs.toBigIntSema(&lhs_space, pt); - const rhs_bigint = try rhs.toBigIntSema(&rhs_space, pt); + const lhs_bigint = lhs.toBigInt(&lhs_space, zcu); + const rhs_bigint = rhs.toBigInt(&rhs_space, zcu); const limbs = try sema.arena.alloc( std.math.big.Limb, lhs_bigint.limbs.len + rhs_bigint.limbs.len, @@ -1840,7 +1843,7 @@ fn intShl( var lhs_space: Value.BigIntSpace = undefined; const lhs_bigint = lhs.toBigInt(&lhs_space, zcu); - const shift_amt: usize = @intCast(try rhs.toUnsignedIntSema(pt)); + const shift_amt: usize = @intCast(rhs.toUnsignedInt(zcu)); if (shift_amt >= info.bits) { return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs, rhs_src, vec_idx); } @@ -1862,7 +1865,7 @@ fn intShlSat( const lhs_bigint = lhs.toBigInt(&lhs_space, zcu); const shift_amt: usize = amt: { - if (try rhs.getUnsignedIntSema(pt)) |shift_amt_u64| { + if (rhs.getUnsignedInt(zcu)) |shift_amt_u64| { if (std.math.cast(usize, shift_amt_u64)) |shift_amt| break :amt shift_amt; } // We only support ints with up to 2^16 - 1 bits, so this @@ -1895,9 +1898,9 @@ fn intShlWithOverflow( const info = lhs_ty.intInfo(zcu); var lhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = try lhs.toBigIntSema(&lhs_space, pt); + const lhs_bigint = lhs.toBigInt(&lhs_space, zcu); - const shift_amt: usize = @intCast(try rhs.toUnsignedIntSema(pt)); + const shift_amt: usize = @intCast(rhs.toUnsignedInt(zcu)); if (shift_amt >= info.bits) { return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs, rhs_src, vec_idx); } @@ -1924,9 +1927,10 @@ fn comptimeIntShl( vec_idx: ?usize, ) !Value { const pt = sema.pt; + const zcu = pt.zcu; var lhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = try lhs.toBigIntSema(&lhs_space, pt); - if (try rhs.getUnsignedIntSema(pt)) |shift_amt_u64| { + const lhs_bigint = lhs.toBigInt(&lhs_space, zcu); + if (rhs.getUnsignedInt(zcu)) |shift_amt_u64| { if (std.math.cast(usize, shift_amt_u64)) |shift_amt| { const result_bigint = try intShlInner(sema, lhs_bigint, shift_amt); return pt.intValue_big(.comptime_int, result_bigint.toConst()); @@ -1963,15 +1967,15 @@ fn intShr( const lhs_bigint = lhs.toBigInt(&lhs_space, zcu); const shift_amt: usize = if (rhs_ty.toIntern() == .comptime_int_type) amt: { - if (try rhs.getUnsignedIntSema(pt)) |shift_amt_u64| { + if (rhs.getUnsignedInt(zcu)) |shift_amt_u64| { if (std.math.cast(usize, shift_amt_u64)) |shift_amt| break :amt shift_amt; } - if (try rhs.compareAllWithZeroSema(.lt, pt)) { + if (rhs.compareAllWithZero(.lt, zcu)) { return sema.failWithNegativeShiftAmount(block, rhs_src, rhs, vec_idx); } else { return sema.failWithUnsupportedComptimeShiftAmount(block, rhs_src, vec_idx); } - } else @intCast(try rhs.toUnsignedIntSema(pt)); + } else @intCast(rhs.toUnsignedInt(zcu)); if (lhs_ty.toIntern() != .comptime_int_type and shift_amt >= lhs_ty.intInfo(zcu).bits) { return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs, rhs_src, vec_idx); @@ -2006,7 +2010,7 @@ fn intBitReverse(sema: *Sema, val: Value, ty: Type) !Value { const info = ty.intInfo(zcu); var val_space: Value.BigIntSpace = undefined; - const val_bigint = try val.toBigIntSema(&val_space, pt); + const val_bigint = val.toBigInt(&val_space, zcu); const limbs = try sema.arena.alloc( std.math.big.Limb, diff --git a/src/Sema/bitcast.zig b/src/Sema/bitcast.zig @@ -79,8 +79,8 @@ fn bitCastInner( const val_ty = val.typeOf(zcu); - try val_ty.resolveLayout(pt); - try dest_ty.resolveLayout(pt); + val_ty.assertHasLayout(zcu); + dest_ty.assertHasLayout(zcu); assert(val_ty.hasWellDefinedLayout(zcu)); @@ -138,8 +138,8 @@ fn bitCastSpliceInner( const val_ty = val.typeOf(zcu); const splice_val_ty = splice_val.typeOf(zcu); - try val_ty.resolveLayout(pt); - try splice_val_ty.resolveLayout(pt); + val_ty.assertHasLayout(zcu); + splice_val_ty.assertHasLayout(zcu); const splice_bits = splice_val_ty.bitSize(zcu); @@ -267,12 +267,13 @@ const UnpackValueBits = struct { .int, .enum_tag, .simple_value, - .empty_enum_value, .float, .ptr, .opt, => try unpack.primitive(val), + .bitpack => |bitpack| try unpack.primitive(.fromInterned(bitpack.backing_int_val)), + .aggregate => switch (ty.zigTypeTag(zcu)) { .vector => { const len: usize = @intCast(ty.arrayLen(zcu)); @@ -443,7 +444,7 @@ const UnpackValueBits = struct { // This @intCast is okay because no primitive can exceed the size of a u16. const int_ty = try unpack.pt.intType(.unsigned, @intCast(bit_count)); const buf = try unpack.arena.alloc(u8, @intCast((val_bits + 7) / 8)); - try val.writeToPackedMemory(ty, unpack.pt, buf, 0); + try val.writeToPackedMemory(unpack.pt, buf, 0); const sub_val = try Value.readFromPackedMemory(int_ty, unpack.pt, buf, @intCast(bit_offset), unpack.arena); try unpack.primitive(sub_val); }, @@ -451,7 +452,6 @@ const UnpackValueBits = struct { // The only values here with runtime bits are `true` and `false. // These are both 1 bit, so will never need truncating. .simple_value => unreachable, - .empty_enum_value => unreachable, // zero-bit else => unreachable, // zero-bit or not primitives } } @@ -565,102 +565,103 @@ const PackValueBits = struct { return pt.aggregateValue(ty, elems); }, .@"packed" => { - // All fields are in order with no padding. - // This is identical between LE and BE targets. - const elems = try arena.alloc(InternPool.Index, ty.structFieldCount(zcu)); - for (elems, 0..) |*elem, i| { - const field_ty = ty.fieldType(i, zcu); - elem.* = (try pack.get(field_ty)).toIntern(); - } - return pt.aggregateValue(ty, elems); + const backing_int_val = try pack.primitive(ty.bitpackBackingInt(zcu)); + return pt.bitpackValue(ty, backing_int_val); }, }, - .@"union" => { - // We will attempt to read as the backing representation. If this emits - // `error.ReinterpretDeclRef`, we will try each union field, preferring larger ones. - // We will also attempt smaller fields when we get `undefined`, as if some bits are - // defined we want to include them. - // TODO: this is very very bad. We need a more sophisticated union representation. - - const prev_unpacked = pack.unpacked; - const prev_bit_offset = pack.bit_offset; - - const backing_ty = try ty.unionBackingType(pt); - - backing: { - const backing_val = pack.get(backing_ty) catch |err| switch (err) { - error.ReinterpretDeclRef => { + .@"union" => switch (ty.containerLayout(zcu)) { + .auto => unreachable, // ill-defined layout + .@"extern" => { + // We will attempt to read as the backing representation. If this emits + // `error.ReinterpretDeclRef`, we will try each union field, preferring larger ones. + // We will also attempt smaller fields when we get `undefined`, as if some bits are + // defined we want to include them. + // TODO: this is very very bad. We need a more sophisticated union representation. + + const prev_unpacked = pack.unpacked; + const prev_bit_offset = pack.bit_offset; + + const backing_ty = try ty.externUnionBackingType(pt); + + backing: { + const backing_val = pack.get(backing_ty) catch |err| switch (err) { + error.ReinterpretDeclRef => { + pack.unpacked = prev_unpacked; + pack.bit_offset = prev_bit_offset; + break :backing; + }, + else => |e| return e, + }; + if (backing_val.isUndef(zcu)) { pack.unpacked = prev_unpacked; pack.bit_offset = prev_bit_offset; break :backing; - }, - else => |e| return e, - }; - if (backing_val.isUndef(zcu)) { - pack.unpacked = prev_unpacked; - pack.bit_offset = prev_bit_offset; - break :backing; + } + return Value.fromInterned(try pt.internUnion(.{ + .ty = ty.toIntern(), + .tag = .none, + .val = backing_val.toIntern(), + })); } - return Value.fromInterned(try pt.internUnion(.{ - .ty = ty.toIntern(), - .tag = .none, - .val = backing_val.toIntern(), - })); - } - const field_order = try pack.arena.alloc(u32, ty.unionTagTypeHypothetical(zcu).enumFieldCount(zcu)); - for (field_order, 0..) |*f, i| f.* = @intCast(i); - // Sort `field_order` to put the fields with the largest bit sizes first. - const SizeSortCtx = struct { - zcu: *Zcu, - field_types: []const InternPool.Index, - fn lessThan(ctx: @This(), a_idx: u32, b_idx: u32) bool { - const a_ty = Type.fromInterned(ctx.field_types[a_idx]); - const b_ty = Type.fromInterned(ctx.field_types[b_idx]); - return a_ty.bitSize(ctx.zcu) > b_ty.bitSize(ctx.zcu); - } - }; - std.mem.sortUnstable(u32, field_order, SizeSortCtx{ - .zcu = zcu, - .field_types = zcu.typeToUnion(ty).?.field_types.get(ip), - }, SizeSortCtx.lessThan); - - const padding_after = endian == .little or ty.containerLayout(zcu) == .@"packed"; - - for (field_order) |field_idx| { - const field_ty = Type.fromInterned(zcu.typeToUnion(ty).?.field_types.get(ip)[field_idx]); - const pad_bits = ty.bitSize(zcu) - field_ty.bitSize(zcu); - if (!padding_after) try pack.padding(pad_bits); - const field_val = pack.get(field_ty) catch |err| switch (err) { - error.ReinterpretDeclRef => { + const field_order = try pack.arena.alloc(u32, ty.unionTagTypeHypothetical(zcu).enumFieldCount(zcu)); + for (field_order, 0..) |*f, i| f.* = @intCast(i); + // Sort `field_order` to put the fields with the largest bit sizes first. + const SizeSortCtx = struct { + zcu: *Zcu, + field_types: []const InternPool.Index, + fn lessThan(ctx: @This(), a_idx: u32, b_idx: u32) bool { + const a_ty = Type.fromInterned(ctx.field_types[a_idx]); + const b_ty = Type.fromInterned(ctx.field_types[b_idx]); + return a_ty.bitSize(ctx.zcu) > b_ty.bitSize(ctx.zcu); + } + }; + std.mem.sortUnstable(u32, field_order, SizeSortCtx{ + .zcu = zcu, + .field_types = zcu.typeToUnion(ty).?.field_types.get(ip), + }, SizeSortCtx.lessThan); + + const padding_after = endian == .little or ty.containerLayout(zcu) == .@"packed"; + + for (field_order) |field_idx| { + const field_ty = Type.fromInterned(zcu.typeToUnion(ty).?.field_types.get(ip)[field_idx]); + const pad_bits = ty.bitSize(zcu) - field_ty.bitSize(zcu); + if (!padding_after) try pack.padding(pad_bits); + const field_val = pack.get(field_ty) catch |err| switch (err) { + error.ReinterpretDeclRef => { + pack.unpacked = prev_unpacked; + pack.bit_offset = prev_bit_offset; + continue; + }, + else => |e| return e, + }; + if (padding_after) try pack.padding(pad_bits); + if (field_val.isUndef(zcu)) { pack.unpacked = prev_unpacked; pack.bit_offset = prev_bit_offset; continue; - }, - else => |e| return e, - }; - if (padding_after) try pack.padding(pad_bits); - if (field_val.isUndef(zcu)) { - pack.unpacked = prev_unpacked; - pack.bit_offset = prev_bit_offset; - continue; + } + const tag_val = try pt.enumValueFieldIndex(ty.unionTagTypeHypothetical(zcu), field_idx); + return Value.fromInterned(try pt.internUnion(.{ + .ty = ty.toIntern(), + .tag = tag_val.toIntern(), + .val = field_val.toIntern(), + })); } - const tag_val = try pt.enumValueFieldIndex(ty.unionTagTypeHypothetical(zcu), field_idx); + + // No field could represent the value. Just do whatever happens when we try to read + // the backing type - either `undefined` or `error.ReinterpretDeclRef`. + const backing_val = try pack.get(backing_ty); return Value.fromInterned(try pt.internUnion(.{ .ty = ty.toIntern(), - .tag = tag_val.toIntern(), - .val = field_val.toIntern(), + .tag = .none, + .val = backing_val.toIntern(), })); - } - - // No field could represent the value. Just do whatever happens when we try to read - // the backing type - either `undefined` or `error.ReinterpretDeclRef`. - const backing_val = try pack.get(backing_ty); - return Value.fromInterned(try pt.internUnion(.{ - .ty = ty.toIntern(), - .tag = .none, - .val = backing_val.toIntern(), - })); + }, + .@"packed" => { + const backing_int_val = try pack.primitive(ty.bitpackBackingInt(zcu)); + return pt.bitpackValue(ty, backing_int_val); + }, }, else => return pack.primitive(ty), } @@ -673,6 +674,9 @@ const PackValueBits = struct { fn primitive(pack: *PackValueBits, want_ty: Type) BitCastError!Value { const pt = pack.pt; const zcu = pt.zcu; + + if (try want_ty.onePossibleValue(pt)) |opv| return opv; + const vals, const bit_offset = pack.prepareBits(want_ty.bitSize(zcu)); for (vals) |val| { @@ -719,7 +723,7 @@ const PackValueBits = struct { const val = Value.fromInterned(ip_val); const ty = val.typeOf(zcu); if (!val.isUndef(zcu)) { - try val.writeToPackedMemory(ty, pt, buf, cur_bit_off); + try val.writeToPackedMemory(pt, buf, cur_bit_off); } cur_bit_off += @intCast(ty.bitSize(zcu)); } diff --git a/src/Sema/comptime_ptr_access.zig b/src/Sema/comptime_ptr_access.zig @@ -67,7 +67,7 @@ pub fn storeComptimePtr( { const store_ty: Type = .fromInterned(ptr_info.child); - if (!try store_ty.comptimeOnlySema(pt) and !try store_ty.hasRuntimeBitsIgnoreComptimeSema(pt)) { + if (!store_ty.comptimeOnly(zcu) and !store_ty.hasRuntimeBits(zcu)) { // zero-bit store; nothing to do return .success; } @@ -354,8 +354,8 @@ fn loadComptimePtrInner( const load_one_ty, const load_count = load_ty.arrayBase(zcu); const extra_base_index: u64 = if (ptr.byte_offset == 0) 0 else idx: { - if (try load_one_ty.comptimeOnlySema(pt)) break :restructure_array; - const elem_len = try load_one_ty.abiSizeSema(pt); + if (load_one_ty.comptimeOnly(zcu)) break :restructure_array; + const elem_len = load_one_ty.abiSize(zcu); if (ptr.byte_offset % elem_len != 0) break :restructure_array; break :idx @divExact(ptr.byte_offset, elem_len); }; @@ -401,12 +401,12 @@ fn loadComptimePtrInner( var cur_offset = ptr.byte_offset; if (load_ty.zigTypeTag(zcu) == .array and array_offset > 0) { - cur_offset += try load_ty.childType(zcu).abiSizeSema(pt) * array_offset; + cur_offset += load_ty.childType(zcu).abiSize(zcu) * array_offset; } - const need_bytes = if (host_bits > 0) (host_bits + 7) / 8 else try load_ty.abiSizeSema(pt); + const need_bytes = if (host_bits > 0) (host_bits + 7) / 8 else load_ty.abiSize(zcu); - if (cur_offset + need_bytes > try cur_val.typeOf(zcu).abiSizeSema(pt)) { + if (cur_offset + need_bytes > cur_val.typeOf(zcu).abiSize(zcu)) { return .{ .out_of_bounds = cur_val.typeOf(zcu) }; } @@ -441,7 +441,7 @@ fn loadComptimePtrInner( .optional => break, // this can only be a pointer-like optional so is terminal .array => { const elem_ty = cur_ty.childType(zcu); - const elem_size = try elem_ty.abiSizeSema(pt); + const elem_size = elem_ty.abiSize(zcu); const elem_idx = cur_offset / elem_size; const next_elem_off = elem_size * (elem_idx + 1); if (cur_offset + need_bytes <= next_elem_off) { @@ -457,7 +457,7 @@ fn loadComptimePtrInner( .@"packed" => break, // let the bitcast logic handle this .@"extern" => for (0..cur_ty.structFieldCount(zcu)) |field_idx| { const start_off = cur_ty.structFieldOffset(field_idx, zcu); - const end_off = start_off + try cur_ty.fieldType(field_idx, zcu).abiSizeSema(pt); + const end_off = start_off + cur_ty.fieldType(field_idx, zcu).abiSize(zcu); if (cur_offset >= start_off and cur_offset + need_bytes <= end_off) { cur_val = try cur_val.getElem(sema.pt, field_idx); cur_offset -= start_off; @@ -484,7 +484,7 @@ fn loadComptimePtrInner( }; // The payload always has offset 0. If it's big enough // to represent the whole load type, we can use it. - if (try payload.typeOf(zcu).abiSizeSema(pt) >= need_bytes) { + if (payload.typeOf(zcu).abiSize(zcu) >= need_bytes) { cur_val = payload; } else { break; @@ -753,8 +753,8 @@ fn prepareComptimePtrStore( const store_one_ty, const store_count = store_ty.arrayBase(zcu); const extra_base_index: u64 = if (ptr.byte_offset == 0) 0 else idx: { - if (try store_one_ty.comptimeOnlySema(pt)) break :restructure_array; - const elem_len = try store_one_ty.abiSizeSema(pt); + if (store_one_ty.comptimeOnly(zcu)) break :restructure_array; + const elem_len = store_one_ty.abiSize(zcu); if (ptr.byte_offset % elem_len != 0) break :restructure_array; break :idx @divExact(ptr.byte_offset, elem_len); }; @@ -807,11 +807,11 @@ fn prepareComptimePtrStore( var cur_val: *MutableValue, var cur_offset: u64 = switch (base_strat) { .direct => |direct| .{ direct.val, 0 }, // It's okay to do `abiSize` - the comptime-only case will be caught below. - .index => |index| .{ index.val, index.elem_index * try index.val.typeOf(zcu).childType(zcu).abiSizeSema(pt) }, + .index => |index| .{ index.val, index.elem_index * index.val.typeOf(zcu).childType(zcu).abiSize(zcu) }, .flat_index => |flat_index| .{ flat_index.val, // It's okay to do `abiSize` - the comptime-only case will be caught below. - flat_index.flat_elem_index * try flat_index.val.typeOf(zcu).arrayBase(zcu)[0].abiSizeSema(pt), + flat_index.flat_elem_index * flat_index.val.typeOf(zcu).arrayBase(zcu)[0].abiSize(zcu), }, .reinterpret => |r| .{ r.val, r.byte_offset }, else => unreachable, @@ -823,12 +823,12 @@ fn prepareComptimePtrStore( } if (store_ty.zigTypeTag(zcu) == .array and array_offset > 0) { - cur_offset += try store_ty.childType(zcu).abiSizeSema(pt) * array_offset; + cur_offset += store_ty.childType(zcu).abiSize(zcu) * array_offset; } - const need_bytes = try store_ty.abiSizeSema(pt); + const need_bytes = store_ty.abiSize(zcu); - if (cur_offset + need_bytes > try cur_val.typeOf(zcu).abiSizeSema(pt)) { + if (cur_offset + need_bytes > cur_val.typeOf(zcu).abiSize(zcu)) { return .{ .out_of_bounds = cur_val.typeOf(zcu) }; } @@ -863,7 +863,7 @@ fn prepareComptimePtrStore( .optional => break, // this can only be a pointer-like optional so is terminal .array => { const elem_ty = cur_ty.childType(zcu); - const elem_size = try elem_ty.abiSizeSema(pt); + const elem_size = elem_ty.abiSize(zcu); const elem_idx = cur_offset / elem_size; const next_elem_off = elem_size * (elem_idx + 1); if (cur_offset + need_bytes <= next_elem_off) { @@ -879,7 +879,7 @@ fn prepareComptimePtrStore( .@"packed" => break, // let the bitcast logic handle this .@"extern" => for (0..cur_ty.structFieldCount(zcu)) |field_idx| { const start_off = cur_ty.structFieldOffset(field_idx, zcu); - const end_off = start_off + try cur_ty.fieldType(field_idx, zcu).abiSizeSema(pt); + const end_off = start_off + cur_ty.fieldType(field_idx, zcu).abiSize(zcu); if (cur_offset >= start_off and cur_offset + need_bytes <= end_off) { cur_val = try cur_val.elem(pt, sema.arena, field_idx); cur_offset -= start_off; @@ -902,7 +902,7 @@ fn prepareComptimePtrStore( }; // The payload always has offset 0. If it's big enough // to represent the whole load type, we can use it. - if (try payload.typeOf(zcu).abiSizeSema(pt) >= need_bytes) { + if (payload.typeOf(zcu).abiSize(zcu) >= need_bytes) { cur_val = payload; } else { break; diff --git a/src/Sema/type_resolution.zig b/src/Sema/type_resolution.zig @@ -0,0 +1,1398 @@ +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; + +const Sema = @import("../Sema.zig"); +const Block = Sema.Block; +const Type = @import("../Type.zig"); +const Value = @import("../Value.zig"); +const Zcu = @import("../Zcu.zig"); +const CompileError = Zcu.CompileError; +const SemaError = Zcu.SemaError; +const LazySrcLoc = Zcu.LazySrcLoc; +const InternPool = @import("../InternPool.zig"); +const Alignment = InternPool.Alignment; +const arith = @import("arith.zig"); + +pub const LayoutResolveReason = enum { + variable, + constant, + parameter, + return_type, + field, + backing_enum, + init, + coerce, + ptr_access, + ptr_offset, + field_used, + field_queried, + size_of, + align_of, + type_info, + align_check, + bit_ptr_child, + @"export", + @"extern", + builtin_type, + + /// Written after string: "while resolving type 'T' " + /// e.g. "while resolving type 'MyStruct' for variable declared here" + pub fn msg(r: LayoutResolveReason) []const u8 { + return switch (r) { + // zig fmt: off + .variable => "for variable declared here", + .constant => "for constant declared here", + .parameter => "for function parameter declared here", + .return_type => "for function return type declared here", + .field => "for field declared here", + .backing_enum => "for backing enum type declared here", + .init => "for initialization performed here", + .coerce => "for coercion performed here", + .ptr_access => "for pointer access here", + .ptr_offset => "for pointer offset here", + .field_used => "for field usage here", + .field_queried => "for field query here", + .size_of => "for size query here", + .align_of => "for alignment query here", + .type_info => "for type information query here", + .align_check => "for alignment check here", + .bit_ptr_child => "for bit size check here", + .@"export" => "for export here", + .@"extern" => "for extern declaration here", + .builtin_type => "from 'std.builtin'", + // zig fmt: on + }; + } +}; + +/// Ensures that `ty` has known layout, including alignment, size, and (where relevant) field offsets. +/// `ty` may be any type; its layout is resolved *recursively* if necessary. +/// Adds incremental dependencies tracking any required type resolution. +pub fn ensureLayoutResolved(sema: *Sema, ty: Type, src: LazySrcLoc, reason: LayoutResolveReason) SemaError!void { + return ensureLayoutResolvedInner(sema, ty, ty, &.{ + .src = src, + .type_layout_reason = reason, + }); +} +fn ensureLayoutResolvedInner(sema: *Sema, ty: Type, orig_ty: Type, reason: *const Zcu.DependencyReason) SemaError!void { + const pt = sema.pt; + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + switch (ip.indexToKey(ty.toIntern())) { + .int_type, + .ptr_type, + .anyframe_type, + .simple_type, + .opaque_type, + .error_set_type, + .inferred_error_set_type, + => {}, + + .func_type => |func_type| { + for (func_type.param_types.get(ip)) |param_ty| { + try ensureLayoutResolvedInner(sema, .fromInterned(param_ty), orig_ty, reason); + } + try ensureLayoutResolvedInner(sema, .fromInterned(func_type.return_type), orig_ty, reason); + }, + + .array_type => |arr| return ensureLayoutResolvedInner(sema, .fromInterned(arr.child), orig_ty, reason), + .vector_type => |vec| return ensureLayoutResolvedInner(sema, .fromInterned(vec.child), orig_ty, reason), + .opt_type => |child| return ensureLayoutResolvedInner(sema, .fromInterned(child), orig_ty, reason), + .error_union_type => |eu| return ensureLayoutResolvedInner(sema, .fromInterned(eu.payload_type), orig_ty, reason), + .tuple_type => |tuple| for (tuple.types.get(ip)) |field_ty| { + try ensureLayoutResolvedInner(sema, .fromInterned(field_ty), orig_ty, reason); + }, + .struct_type, .union_type, .enum_type => { + try sema.declareDependency(.{ .type_layout = ty.toIntern() }); + try sema.addReferenceEntry(null, reason.src, .wrap(.{ .type_layout = ty.toIntern() })); + if (zcu.analysis_in_progress.contains(.wrap(.{ .type_layout = ty.toIntern() }))) { + return sema.failWithDependencyLoop(.wrap(.{ .type_layout = ty.toIntern() }), reason); + } + try pt.ensureTypeLayoutUpToDate(ty, reason); + }, + + // values, not types + .undef, + .simple_value, + .variable, + .@"extern", + .func, + .int, + .err, + .error_union, + .enum_literal, + .enum_tag, + .float, + .ptr, + .slice, + .opt, + .aggregate, + .un, + .bitpack, + // memoization, not types + .memoized_call, + => unreachable, + } +} + +/// Asserts that `ty` is a non-tuple `struct` type, and ensures that its fields' default values +/// are resolved. Adds incremental dependencies tracking the required type resolution. +/// +/// It is not necessary to call this function to query the values of comptime fields: those values +/// are available from type *layout* resolution, see `ensureLayoutResolved`. +/// +/// Asserts that the *layout* of `ty` has already been resolved---see `ensureLayoutResolved`. +pub fn ensureStructDefaultsResolved(sema: *Sema, ty: Type, src: LazySrcLoc) SemaError!void { + const pt = sema.pt; + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + + assert(ip.indexToKey(ty.toIntern()) == .struct_type); + ty.assertHasLayout(zcu); + + try sema.declareDependency(.{ .struct_defaults = ty.toIntern() }); + try sema.addReferenceEntry(null, src, .wrap(.{ .struct_defaults = ty.toIntern() })); + + const reason: Zcu.DependencyReason = .{ .src = src, .type_layout_reason = undefined }; + + if (zcu.analysis_in_progress.contains(.wrap(.{ .struct_defaults = ty.toIntern() }))) { + return sema.failWithDependencyLoop(.wrap(.{ .struct_defaults = ty.toIntern() }), &reason); + } + + try pt.ensureStructDefaultsUpToDate(ty, &reason); +} + +/// Asserts that `struct_ty` is a non-packed non-tuple struct, and that `sema.owner` is that type. +/// This function *does* register the `src_hash` dependency on the struct. +pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void { + const pt = sema.pt; + const zcu = pt.zcu; + const comp = zcu.comp; + const io = comp.io; + const gpa = comp.gpa; + const ip = &zcu.intern_pool; + + assert(sema.owner.unwrap().type_layout == struct_ty.toIntern()); + + const struct_obj = ip.loadStructType(struct_ty.toIntern()); + assert(struct_obj.want_layout); + const zir_index = struct_obj.zir_index.resolve(ip) orelse return error.AnalysisFail; + + var block: Block = .{ + .parent = null, + .sema = sema, + .namespace = struct_obj.namespace, + .instructions = .empty, + .inlining = null, + .comptime_reason = undefined, // always set before using `block` + .src_base_inst = struct_obj.zir_index, + .type_name_ctx = struct_obj.name, + }; + defer block.instructions.deinit(gpa); + + // There may be old field names in here from a previous update. + struct_obj.field_name_map.get(ip).clearRetainingCapacity(); + + if (struct_obj.is_reified) { + // The field names are populated, but we haven't checked for duplicates (nor populated the map) yet. + for (0..struct_obj.field_names.len) |field_index| { + const name = struct_obj.field_names.get(ip)[field_index]; + if (ip.addFieldName(struct_obj.field_names, struct_obj.field_name_map, name)) |prev_field_index| { + return sema.failWithOwnedErrorMsg(&block, msg: { + const src = block.builtinCallArgSrc(.zero, 2); + const msg = try sema.errMsg(src, "duplicate struct field '{f}' at index '{d}", .{ name.fmt(ip), field_index }); + errdefer msg.destroy(gpa); + try sema.errNote(src, msg, "previous field at index '{d}'", .{prev_field_index}); + break :msg msg; + }); + } + } + } else { + // Declared structs do not yet have field information populated: + // * field names + // * field comptime-ness + // * field types + // * field aligns + // It's our job to populate these now. + try sema.declareDependency(.{ .src_hash = struct_obj.zir_index }); + + // Likewise, comptime bits may be set. We clear them all first because it avoids needing + // "unset bit with AND" logic below (instead we only need the "set bit with OR" case). + @memset(struct_obj.field_is_comptime_bits.getAll(ip), 0); + + const zir_struct = sema.code.getStructDecl(zir_index); + var field_it = zir_struct.iterateFields(); + var any_comptime_fields = false; + while (field_it.next()) |zir_field| { + { + const name_slice = sema.code.nullTerminatedString(zir_field.name); + const name = try ip.getOrPutString(gpa, io, pt.tid, name_slice, .no_embedded_nulls); + assert(ip.addFieldName(struct_obj.field_names, struct_obj.field_name_map, name) == null); // AstGen validated this for us + } + + if (zir_field.is_comptime) { + const bit_bag_index = zir_field.idx / 32; + const mask = @as(u32, 1) << @intCast(zir_field.idx % 32); + struct_obj.field_is_comptime_bits.getAll(ip)[bit_bag_index] |= mask; + any_comptime_fields = true; + } + + { + const field_ty_src = block.src(.{ .container_field_type = zir_field.idx }); + const field_ty: Type = field_ty: { + block.comptime_reason = .{ .reason = .{ + .src = field_ty_src, + .r = .{ .simple = .struct_field_types }, + } }; + const type_ref = try sema.resolveInlineBody(&block, zir_field.type_body, zir_index); + break :field_ty try sema.analyzeAsType(&block, field_ty_src, .struct_field_types, type_ref); + }; + struct_obj.field_types.get(ip)[zir_field.idx] = field_ty.toIntern(); + } + + if (struct_obj.field_aligns.len == 0) { + assert(zir_field.align_body == null); + } else { + const field_align_src = block.src(.{ .container_field_align = zir_field.idx }); + const field_align: Alignment = a: { + block.comptime_reason = .{ .reason = .{ + .src = field_align_src, + .r = .{ .simple = .struct_field_attrs }, + } }; + const align_body = zir_field.align_body orelse break :a .none; + const align_ref = try sema.resolveInlineBody(&block, align_body, zir_index); + break :a try sema.analyzeAsAlign(&block, field_align_src, align_ref); + }; + struct_obj.field_aligns.get(ip)[zir_field.idx] = field_align; + } + } + + // We also resolve the default values of any `comptime` fields now. This is not necessary in + // the case of a reified struct because the the default values were already poulated and + // validated by `Sema.zirReifyStruct`. + if (any_comptime_fields) { + try resolveStructDefaultsInner(sema, &block, &struct_obj, .comptime_fields); + } + } + + if (struct_obj.layout == .@"packed") { + return resolvePackedStructLayout(sema, &block, struct_ty, &struct_obj); + } + + // Resolve the layout of all fields, and check their types are allowed. + for (struct_obj.field_types.get(ip), 0..) |field_ty_ip, field_index| { + const field_ty: Type = .fromInterned(field_ty_ip); + assert(!field_ty.isGenericPoison()); + const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) }); + try sema.ensureLayoutResolved(field_ty, field_ty_src, .field); + if (field_ty.zigTypeTag(zcu) == .@"opaque") { + return sema.failWithOwnedErrorMsg(&block, msg: { + const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in struct", .{field_ty.fmt(pt)}); + errdefer msg.destroy(gpa); + try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{}); + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + } + if (struct_obj.layout == .@"extern" and !field_ty.validateExtern(.struct_field, zcu)) { + return sema.failWithOwnedErrorMsg(&block, msg: { + const msg = try sema.errMsg(field_ty_src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); + errdefer msg.destroy(gpa); + try sema.explainWhyTypeIsNotExtern(msg, field_ty_src, field_ty, .struct_field); + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + } + } + + // Fields are okay. Now we need to resolve the struct's overall layout (size, field offsets, etc). + + var any_comptime_fields = false; + var struct_align: Alignment = .@"1"; + var has_no_possible_value = false; + var has_runtime_state = false; + var has_comptime_state = false; + // Unlike `struct_obj.field_aligns`, these are not `.none`. + const resolved_field_aligns = try sema.arena.alloc(Alignment, struct_obj.field_names.len); + for (resolved_field_aligns, 0..) |*align_out, field_idx| { + const field_ty: Type = .fromInterned(struct_obj.field_types.get(ip)[field_idx]); + const field_align: Alignment = a: { + if (struct_obj.field_aligns.len != 0) { + const a = struct_obj.field_aligns.get(ip)[field_idx]; + if (a != .none) break :a a; + } + break :a field_ty.defaultStructFieldAlignment(struct_obj.layout, zcu); + }; + align_out.* = field_align; + if (struct_obj.field_is_comptime_bits.get(ip, field_idx)) { + assert(struct_obj.layout == .auto); // comptime fields not allowed in extern or packed structs + struct_obj.field_runtime_order.get(ip)[field_idx] = .omitted; // comptime fields are not in the runtime order + any_comptime_fields = true; + continue; // `comptime` fields do not contribute to the struct layout + } + struct_align = struct_align.maxStrict(field_align); + if (struct_obj.layout == .auto) { + struct_obj.field_runtime_order.get(ip)[field_idx] = @enumFromInt(field_idx); + } + switch (field_ty.classify(zcu)) { + .one_possible_value => {}, + .no_possible_value => has_no_possible_value = true, + .runtime => has_runtime_state = true, + .fully_comptime => has_comptime_state = true, + .partially_comptime => { + has_runtime_state = true; + has_comptime_state = true; + }, + } + } + const class: Type.Class = class: { + if (has_no_possible_value) break :class .no_possible_value; + if (has_comptime_state) { + break :class if (has_runtime_state) .partially_comptime else .fully_comptime; + } else { + break :class if (has_runtime_state) .runtime else .one_possible_value; + } + }; + + switch (struct_obj.layout) { + .auto => {}, + .@"extern" => assert(class != .no_possible_value), // field types are all extern, so are not NPV + .@"packed" => unreachable, + } + + if (struct_obj.layout == .auto) { + const runtime_order = struct_obj.field_runtime_order.get(ip); + // This logic does not reorder fields; it only moves the omitted ones to the end so that logic + // elsewhere does not need to special-case. TODO: support field reordering in all the backends! + if (!zcu.backendSupportsFeature(.field_reordering)) { + var i: usize = 0; + var off: usize = 0; + while (i + off < runtime_order.len) { + if (runtime_order[i + off] == .omitted) { + off += 1; + } else { + runtime_order[i] = runtime_order[i + off]; + i += 1; + } + } + } else { + // Sort by descending alignment to minimize padding. + const RuntimeOrder = InternPool.LoadedStructType.RuntimeOrder; + const AlignSortCtx = struct { + aligns: []const Alignment, + fn lessThan(ctx: @This(), a: RuntimeOrder, b: RuntimeOrder) bool { + assert(a != .unresolved); + assert(b != .unresolved); + if (a == .omitted) return false; + if (b == .omitted) return true; + const a_align = ctx.aligns[@intFromEnum(a)]; + const b_align = ctx.aligns[@intFromEnum(b)]; + return a_align.compare(.gt, b_align); + } + }; + mem.sortUnstable( + RuntimeOrder, + runtime_order, + @as(AlignSortCtx, .{ .aligns = resolved_field_aligns }), + AlignSortCtx.lessThan, + ); + } + } + + var runtime_order_it = struct_obj.iterateRuntimeOrder(ip); + var cur_offset: u64 = 0; + while (runtime_order_it.next()) |field_idx| { + const field_ty: Type = .fromInterned(struct_obj.field_types.get(ip)[field_idx]); + const offset = resolved_field_aligns[field_idx].forward(cur_offset); + struct_obj.field_offsets.get(ip)[field_idx] = @truncate(offset); // truncate because the overflow is handled below + cur_offset = offset + field_ty.abiSize(zcu); + } + const struct_size: u32 = switch (class) { + .no_possible_value => 0, + else => std.math.cast(u32, struct_align.forward(cur_offset)) orelse return sema.fail( + &block, + struct_ty.srcLoc(zcu), + "struct layout requires size {d}, this compiler implementation supports up to {d}", + .{ struct_align.forward(cur_offset), std.math.maxInt(u32) }, + ), + }; + ip.resolveStructLayout( + io, + struct_ty.toIntern(), + struct_size, + struct_align, + class, + ); +} + +/// Asserts that `struct_ty` is a packed struct, and that `sema.owner` is that type. +/// This function *does* register the `src_hash` dependency on the struct. +fn resolvePackedStructLayout( + sema: *Sema, + block: *Block, + struct_ty: Type, + struct_obj: *const InternPool.LoadedStructType, +) CompileError!void { + const pt = sema.pt; + const zcu = pt.zcu; + const comp = zcu.comp; + const io = comp.io; + const gpa = comp.gpa; + const ip = &zcu.intern_pool; + + // Resolve the layout of all fields, and check their types are allowed. + // Also count the number of bits while we're at it. + var field_bits: u64 = 0; + for (struct_obj.field_types.get(ip), 0..) |field_ty_ip, field_index| { + const field_ty: Type = .fromInterned(field_ty_ip); + assert(!field_ty.isGenericPoison()); + const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) }); + try sema.ensureLayoutResolved(field_ty, field_ty_src, .field); + if (field_ty.zigTypeTag(zcu) == .@"opaque") { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in struct", .{field_ty.fmt(pt)}); + errdefer msg.destroy(gpa); + try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{}); + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + } + if (field_ty.unpackable(zcu)) |reason| return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(field_ty_src, "packed structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); + errdefer msg.destroy(gpa); + try sema.explainWhyTypeIsUnpackable(msg, field_ty_src, reason); + break :msg msg; + }); + switch (field_ty.classify(zcu)) { + .one_possible_value, .runtime => {}, + .no_possible_value => unreachable, // packable types are not NPV + .partially_comptime => unreachable, // packable types are not comptime-only + .fully_comptime => unreachable, // packable types are not comptime-only + } + field_bits += field_ty.bitSize(zcu); + } + + const explicit_backing_int_ty: ?Type = if (struct_obj.is_reified) ty: { + break :ty switch (struct_obj.packed_backing_mode) { + .explicit => .fromInterned(struct_obj.packed_backing_int_type), + .auto => null, + }; + } else ty: { + const zir_index = struct_obj.zir_index.resolve(ip).?; + const zir_struct = sema.code.getStructDecl(zir_index); + const backing_int_type_body = zir_struct.backing_int_type_body orelse { + break :ty null; // inferred backing type + }; + // Explicitly specified, so evaluate the backing int type expression. + const backing_int_type_src = block.src(.container_arg); + block.comptime_reason = .{ .reason = .{ + .src = backing_int_type_src, + .r = .{ .simple = .packed_struct_backing_int_type }, + } }; + const type_ref = try sema.resolveInlineBody(block, backing_int_type_body, zir_index); + break :ty try sema.analyzeAsType(block, backing_int_type_src, .packed_struct_backing_int_type, type_ref); + }; + + // Finally, either validate or infer the backing int type. + const backing_int_ty: Type = if (explicit_backing_int_ty) |backing_ty| ty: { + if (backing_ty.zigTypeTag(zcu) != .int) return sema.fail( + block, + block.src(.container_arg), + "expected backing integer type, found '{f}'", + .{backing_ty.fmt(pt)}, + ); + if (field_bits != backing_ty.intInfo(zcu).bits) return sema.failWithOwnedErrorMsg(block, msg: { + const src = struct_ty.srcLoc(zcu); + const msg = try sema.errMsg(src, "backing integer bit width does not match total bit width of fields", .{}); + errdefer msg.destroy(gpa); + try sema.errNote( + block.src(.container_arg), + msg, + "backing integer '{f}' has bit width '{d}'", + .{ backing_ty.fmt(pt), backing_ty.bitSize(zcu) }, + ); + try sema.errNote(src, msg, "struct fields have total bit width '{d}'", .{field_bits}); + break :msg msg; + }); + break :ty backing_ty; + } else ty: { + // We need to generate the inferred tag. + const backing_int_bits = std.math.cast(u16, field_bits) orelse return sema.fail( + block, + struct_ty.srcLoc(zcu), + "packed struct bit width '{d}' exceeds maximum bit width of 65535", + .{field_bits}, + ); + break :ty try pt.intType(.unsigned, backing_int_bits); + }; + ip.resolvePackedStructLayout( + io, + struct_ty.toIntern(), + backing_int_ty.toIntern(), + ); +} + +/// Asserts that `struct_ty` is a non-tuple struct, and that `sema.owner` is that type. +/// +/// Also asserts that the layout of `struct_ty` has *already* been resolved (though it is okay for +/// that resolution to have failed). This requirement exists to ensure better error messages in the +/// event of a dependency loop. +/// +/// This function *does* register the `src_hash` dependency on the struct. +pub fn resolveStructDefaults(sema: *Sema, struct_ty: Type) CompileError!void { + const pt = sema.pt; + const zcu = pt.zcu; + const comp = zcu.comp; + const gpa = comp.gpa; + const ip = &zcu.intern_pool; + + assert(sema.owner.unwrap().struct_defaults == struct_ty.toIntern()); + + // We always depend on the layout of `struct_ty`. However, we don't actually need to resolve it + // now, because the caller has done so for us. Just mark the dependency so that the incremental + // compilation handling understands the dependency graph. + try sema.declareDependency(.{ .type_layout = struct_ty.toIntern() }); + struct_ty.assertHasLayout(zcu); + const layout_unit: InternPool.AnalUnit = .wrap(.{ .type_layout = struct_ty.toIntern() }); + if (zcu.failed_analysis.contains(layout_unit) or zcu.transitive_failed_analysis.contains(layout_unit)) { + return error.AnalysisFail; + } + + const struct_obj = ip.loadStructType(struct_ty.toIntern()); + assert(struct_obj.want_layout); + + if (struct_obj.is_reified) { + // `Sema.zirReifyStruct` has already populated the default field values *and* (by loading + // the default values from pointers) validated their types, so we have nothing to do. + return; + } + + try sema.declareDependency(.{ .src_hash = struct_obj.zir_index }); + + if (struct_obj.field_defaults.len == 0) { + // The struct has no default field values, so the slice has been omitted. + return; + } + + var block: Block = .{ + .parent = null, + .sema = sema, + .namespace = struct_obj.namespace, + .instructions = .empty, + .inlining = null, + .comptime_reason = undefined, // always set before using `block` + .src_base_inst = struct_obj.zir_index, + .type_name_ctx = struct_obj.name, + }; + defer block.instructions.deinit(gpa); + + return resolveStructDefaultsInner(sema, &block, &struct_obj, .normal_fields); +} + +/// Asserts that the struct is not reified, and that `struct_obj.field_defaults.len` is non-zero. +fn resolveStructDefaultsInner( + sema: *Sema, + block: *Block, + struct_obj: *const InternPool.LoadedStructType, + mode: enum { comptime_fields, normal_fields }, +) CompileError!void { + const pt = sema.pt; + const zcu = pt.zcu; + const comp = zcu.comp; + const gpa = comp.gpa; + const ip = &zcu.intern_pool; + + assert(struct_obj.field_defaults.len > 0); + + // We'll need to map the struct decl instruction to provide result types + const zir_index = struct_obj.zir_index.resolve(ip) orelse return error.AnalysisFail; + try sema.inst_map.ensureSpaceForInstructions(gpa, &.{zir_index}); + + const field_types = struct_obj.field_types.get(ip); + + const zir_struct = sema.code.getStructDecl(zir_index); + var field_it = zir_struct.iterateFields(); + while (field_it.next()) |zir_field| { + switch (mode) { + .comptime_fields => if (!zir_field.is_comptime) continue, + .normal_fields => if (zir_field.is_comptime) continue, + } + + const default_val_src = block.src(.{ .container_field_value = zir_field.idx }); + block.comptime_reason = .{ .reason = .{ + .src = default_val_src, + .r = .{ .simple = .struct_field_default_value }, + } }; + const default_body = zir_field.default_body orelse { + struct_obj.field_defaults.get(ip)[zir_field.idx] = .none; + continue; + }; + const field_ty: Type = .fromInterned(field_types[zir_field.idx]); + const uncoerced = ref: { + // Provide the result type + sema.inst_map.putAssumeCapacity(zir_index, .fromIntern(field_ty.toIntern())); + defer assert(sema.inst_map.remove(zir_index)); + break :ref try sema.resolveInlineBody(block, default_body, zir_index); + }; + const coerced = try sema.coerce(block, field_ty, uncoerced, default_val_src); + const default_val = try sema.resolveConstValue(block, default_val_src, coerced, null); + if (default_val.canMutateComptimeVarState(zcu)) { + const field_name = struct_obj.field_names.get(ip)[zir_field.idx]; + return sema.failWithContainsReferenceToComptimeVar(block, default_val_src, field_name, "field default value", default_val); + } + struct_obj.field_defaults.get(ip)[zir_field.idx] = default_val.toIntern(); + } +} + +/// This logic must be kept in sync with `Type.getUnionLayout`. +pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void { + const pt = sema.pt; + const zcu = pt.zcu; + const comp = zcu.comp; + const io = comp.io; + const gpa = comp.gpa; + const ip = &zcu.intern_pool; + + assert(sema.owner.unwrap().type_layout == union_ty.toIntern()); + + const union_obj = ip.loadUnionType(union_ty.toIntern()); + assert(union_obj.want_layout); + const zir_index = union_obj.zir_index.resolve(ip) orelse return error.AnalysisFail; + + var block: Block = .{ + .parent = null, + .sema = sema, + .namespace = union_obj.namespace, + .instructions = .empty, + .inlining = null, + .comptime_reason = undefined, // always set before using `block` + .src_base_inst = union_obj.zir_index, + .type_name_ctx = union_obj.name, + }; + defer block.instructions.deinit(gpa); + + const enum_tag_ty: Type = switch (union_obj.enum_tag_mode) { + .explicit => validated_tag_ty: { + // If the union is reified, its enum tag type is already populated. If the union is + // declared, we need to evaluate the enum tag type expression (the `E` in `union(E)`). + const tag_ty: Type = switch (union_obj.is_reified) { + true => .fromInterned(union_obj.enum_tag_type), + false => tag_ty: { + const zir_union = sema.code.getUnionDecl(zir_index); + assert(zir_union.kind == .tagged_explicit); // `Zcu.mapOldZirToNew` guarantees that the ZIR mapping preserves `kind` + const tag_type_body = zir_union.arg_type_body.?; + const tag_type_src = block.src(.container_arg); + block.comptime_reason = .{ .reason = .{ + .src = tag_type_src, + .r = .{ .simple = .union_enum_tag_type }, + } }; + const type_ref = try sema.resolveInlineBody(&block, tag_type_body, zir_index); + break :tag_ty try sema.analyzeAsType(&block, tag_type_src, .union_enum_tag_type, type_ref); + }, + }; + // Because the type is explicitly specified, we need to validate it. + if (tag_ty.zigTypeTag(zcu) != .@"enum") return sema.fail( + &block, + block.src(.container_arg), + "expected enum tag type, found '{f}'", + .{tag_ty.fmt(pt)}, + ); + break :validated_tag_ty tag_ty; + }, + // If no tag type was specified, we generate one keyed on this union type. + .auto => switch (try ip.getGeneratedEnumTagType(gpa, io, pt.tid, .{ + .union_type = union_ty.toIntern(), + // The int tag for this enum is usually inferred---the exception is `union(enum(T))`. + .int_tag_mode = switch (union_obj.is_reified) { + true => .auto, + false => switch (sema.code.getUnionDecl(zir_index).kind) { + .tagged_enum_explicit => .explicit, + else => .auto, + }, + }, + .fields_len = @intCast(union_obj.field_types.len), + })) { + .existing => |tag_ty| .fromInterned(tag_ty), + .wip => |wip| tag_ty: { + errdefer wip.cancel(ip, pt.tid); + _ = wip.setName(ip, try ip.getOrPutStringFmt( + gpa, + io, + pt.tid, + "@typeInfo({f}).@\"union\".tag_type.?", + .{union_obj.name.fmt(ip)}, + .no_embedded_nulls, + ), .none); + const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ + .parent = union_obj.namespace.toOptional(), + .owner_type = wip.index, + .file_scope = zcu.namespacePtr(union_obj.namespace).file_scope, + .generation = zcu.generation, + }); + if (comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); + break :tag_ty .fromInterned(wip.finish(ip, new_namespace_index)); + }, + }, + }; + + try sema.ensureLayoutResolved(enum_tag_ty, block.src(.container_arg), .backing_enum); + const enum_obj = ip.loadEnumType(enum_tag_ty.toIntern()); + + if (union_obj.is_reified) { + // We have field names in `union_obj.reified_field_names`, but we haven't + // checked them against the backing type yet. + const union_field_names = union_obj.reified_field_names.get(ip); + match_fields: { + // We can efficiently *check* if the fields match... + if (union_field_names.len == enum_obj.field_names.len) { + for (union_field_names, enum_obj.field_names.get(ip)) |union_field_name, enum_field_name| { + if (!std.mem.eql(u8, union_field_name.toSlice(ip), enum_field_name.toSlice(ip))) break; + } else { + break :match_fields; + } + } + // ...but if they don't, reporting a nice error is a little more involved. If some field + // is present in the enum but not the union, or vice versa, we will report that instead + // of a generic "field order mismatch" error. Of course, this error is impossible for a + // generated tag type, because we populated that from the union ZIR! + assert(enum_obj.owner_union != union_ty.toIntern()); + return failUnionFieldMismatch(sema, &block, union_field_names, enum_tag_ty, &enum_obj); + } + } else { + // Declared unions do not have field types or aligns populated yet. + // We also need to check the field names match the backing enum. + try sema.declareDependency(.{ .src_hash = union_obj.zir_index }); + const zir_union = sema.code.getUnionDecl(zir_index); + + // We'll first check the field names against the backing enum, and only analyze the types + // once we know the fields match one-to-one. + match_fields: { + // We can efficiently *check* if the fields match... + if (zir_union.field_names.len == enum_obj.field_names.len) { + for (zir_union.field_names, enum_obj.field_names.get(ip)) |union_field_name_zir, enum_field_name| { + const union_field_name_slice = sema.code.nullTerminatedString(union_field_name_zir); + if (!std.mem.eql(u8, union_field_name_slice, enum_field_name.toSlice(ip))) break; + } else { + break :match_fields; + } + } + // ...but if they don't, reporting a nice error is a little more involved. If some field + // is present in the enum but not the union, or vice versa, we will report that instead + // of a generic "field order mismatch" error. Of course, this error is impossible for a + // generated tag type, because we populated that from the union ZIR! + assert(enum_obj.owner_union != union_ty.toIntern()); + const union_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, zir_union.field_names.len); + for (zir_union.field_names, union_field_names) |name_zir, *name| { + name.* = try ip.getOrPutString(gpa, io, pt.tid, sema.code.nullTerminatedString(name_zir), .no_embedded_nulls); + } + return failUnionFieldMismatch(sema, &block, union_field_names, enum_tag_ty, &enum_obj); + } + + // Field names okay; populate types and aligns. + var field_it = zir_union.iterateFields(); + while (field_it.next()) |zir_field| { + const field_ty_src = block.src(.{ .container_field_type = zir_field.idx }); + const field_ty: Type = field_ty: { + block.comptime_reason = .{ .reason = .{ + .src = field_ty_src, + .r = .{ .simple = .union_field_types }, + } }; + const type_body = zir_field.type_body orelse break :field_ty .void; + const type_ref = try sema.resolveInlineBody(&block, type_body, zir_index); + break :field_ty try sema.analyzeAsType(&block, field_ty_src, .union_field_types, type_ref); + }; + union_obj.field_types.get(ip)[zir_field.idx] = field_ty.toIntern(); + + const field_align_src = block.src(.{ .container_field_align = zir_field.idx }); + const explicit_field_align: Alignment = a: { + block.comptime_reason = .{ .reason = .{ + .src = field_align_src, + .r = .{ .simple = .union_field_attrs }, + } }; + const align_body = zir_field.align_body orelse break :a .none; + const align_ref = try sema.resolveInlineBody(&block, align_body, zir_index); + break :a try sema.analyzeAsAlign(&block, field_align_src, align_ref); + }; + if (union_obj.field_aligns.len != 0) { + union_obj.field_aligns.get(ip)[zir_field.idx] = explicit_field_align; + } else { + assert(explicit_field_align == .none); + } + } + } + + if (union_obj.layout == .@"packed") { + return resolvePackedUnionLayout(sema, &block, union_ty, &union_obj, enum_tag_ty); + } + + // Resolve the layout of all fields, and check their types are allowed. + for (union_obj.field_types.get(ip), 0..) |field_ty_ip, field_index| { + const field_ty: Type = .fromInterned(field_ty_ip); + assert(!field_ty.isGenericPoison()); + const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) }); + try sema.ensureLayoutResolved(field_ty, field_ty_src, .field); + if (field_ty.zigTypeTag(zcu) == .@"opaque") { + return sema.failWithOwnedErrorMsg(&block, msg: { + const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in union", .{field_ty.fmt(pt)}); + errdefer msg.destroy(gpa); + try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{}); + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + } + if (union_obj.layout == .@"extern" and !field_ty.validateExtern(.union_field, zcu)) { + return sema.failWithOwnedErrorMsg(&block, msg: { + const msg = try sema.errMsg(field_ty_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); + errdefer msg.destroy(gpa); + try sema.explainWhyTypeIsNotExtern(msg, field_ty_src, field_ty, .union_field); + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + } + } + + // Fields are okay. Now we need to resolve the union's overall layout (size, alignment, etc). + var payload_align: Alignment = .@"1"; + var payload_size: u64 = 0; + var possible_tags: u32 = 0; + var payload_has_comptime_state = false; + for (0..union_obj.field_types.len) |field_idx| { + const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); + const field_align: Alignment = a: { + if (union_obj.field_aligns.len != 0) { + const a = union_obj.field_aligns.get(ip)[field_idx]; + if (a != .none) break :a a; + } + break :a field_ty.abiAlignment(zcu); + }; + payload_align = payload_align.maxStrict(field_align); + payload_size = @max(payload_size, field_ty.abiSize(zcu)); + + switch (field_ty.classify(zcu)) { + .no_possible_value => {}, // uninstantiable field has no effect + .one_possible_value, .runtime => { + possible_tags += 1; + }, + .partially_comptime, .fully_comptime => { + possible_tags += 1; + payload_has_comptime_state = true; + }, + } + } + + // Uninstantiable `extern union`s don't make sense; disallow them. + if (possible_tags == 0 and union_obj.layout != .auto) { + // Field types are all extern, so not NPV; thus zero possible tags means no tags at all. + assert(union_obj.field_types.len == 0); + return sema.fail(&block, union_ty.srcLoc(zcu), "extern union has no fields", .{}); + } + + // We only need a runtime tag if there are multiple possible active fields *and* the union is + // not going to be comptime-only. Even if there are still runtime bits in the payload, the tag + // does not require runtime bits in a comptime-only union, because it is impossible to get a + // pointer to a union's tag. + const has_runtime_tag = switch (possible_tags) { + 0, 1 => false, + else => union_obj.tag_usage != .none and !payload_has_comptime_state, + }; + + const class: Type.Class = class: { + if (possible_tags == 0) { + break :class .no_possible_value; + } + if (payload_has_comptime_state) { + break :class if (payload_size > 0) .partially_comptime else .fully_comptime; + } + const have_runtime_bits = has_runtime_tag or payload_size > 0; + break :class if (have_runtime_bits) .runtime else .one_possible_value; + }; + + const size: u64, const padding: u64, const alignment: Alignment = layout: { + if (!has_runtime_tag) { + break :layout .{ payload_align.forward(payload_size), 0, payload_align }; + } + const tag_align = enum_tag_ty.abiAlignment(zcu); + const tag_size = enum_tag_ty.abiSize(zcu); + // The layout will either be (tag, payload, padding) or (payload, tag, padding) depending on + // which has larger alignment. So the overall size is just the tag and payload sizes, added, + // and padded to the larger alignment. + const alignment = tag_align.maxStrict(payload_align); + const unpadded_size = tag_size + payload_size; + const size = alignment.forward(unpadded_size); + break :layout .{ size, size - unpadded_size, alignment }; + }; + + if (class == .no_possible_value or class == .one_possible_value) { + assert(size == 0); + assert(padding == 0); + } + + const casted_size = std.math.cast(u32, size) orelse return sema.fail( + &block, + union_ty.srcLoc(zcu), + "union layout requires size {d}, this compiler implementation supports up to {d}", + .{ size, std.math.maxInt(u32) }, + ); + ip.resolveUnionLayout( + io, + union_ty.toIntern(), + enum_tag_ty.toIntern(), + class, + has_runtime_tag, + casted_size, + @intCast(padding), // okay because padding is no greater than size + alignment, + ); +} +fn failUnionFieldMismatch(sema: *Sema, block: *Block, union_field_names: []const InternPool.NullTerminatedString, enum_tag_ty: Type, enum_obj: *const InternPool.LoadedEnumType) CompileError { + const pt = sema.pt; + const zcu = pt.zcu; + const comp = zcu.comp; + const gpa = comp.gpa; + const ip = &zcu.intern_pool; + const enum_to_union_map = try sema.arena.alloc(?u32, enum_obj.field_names.len); + @memset(enum_to_union_map, null); + for (union_field_names, 0..) |field_name, union_field_index| { + if (enum_obj.nameIndex(ip, field_name)) |enum_field_index| { + enum_to_union_map[enum_field_index] = @intCast(union_field_index); + continue; + } + const union_field_src = block.src(.{ .container_field_name = @intCast(union_field_index) }); + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(union_field_src, "no field named '{f}' in enum '{f}'", .{ field_name.fmt(ip), enum_tag_ty.fmt(pt) }); + errdefer msg.destroy(gpa); + try sema.addDeclaredHereNote(msg, enum_tag_ty); + break :msg msg; + }); + } + for (enum_to_union_map, 0..) |union_field_index, enum_field_index| { + if (union_field_index != null) continue; + const field_name_ip = enum_obj.field_names.get(ip)[enum_field_index]; + const enum_field_src: LazySrcLoc = .{ + .base_node_inst = enum_tag_ty.typeDeclInstAllowGeneratedTag(zcu).?, + .offset = .{ .container_field_name = @intCast(enum_field_index) }, + }; + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(block.nodeOffset(.zero), "enum field '{f}' missing from union", .{field_name_ip.fmt(ip)}); + errdefer msg.destroy(gpa); + try sema.errNote(enum_field_src, msg, "enum field here", .{}); + break :msg msg; + }); + } + // The only problem is the field ordering. + for (enum_to_union_map, 0..) |union_field_index, enum_field_index| { + if (union_field_index.? == enum_field_index) continue; + const field_name = enum_obj.field_names.get(ip)[enum_field_index]; + const union_field_src = block.src(.{ .container_field_name = union_field_index.? }); + const enum_field_src: LazySrcLoc = .{ + .base_node_inst = enum_tag_ty.typeDeclInstAllowGeneratedTag(zcu).?, + .offset = .{ .container_field_name = @intCast(enum_field_index) }, + }; + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(block.nodeOffset(.zero), "union field order does not match tag enum field order", .{}); + errdefer msg.destroy(gpa); + try sema.errNote(union_field_src, msg, "union field '{f}' is index {d}", .{ field_name.fmt(ip), union_field_index.? }); + try sema.errNote(enum_field_src, msg, "enum field '{f}' is index {d}", .{ field_name.fmt(ip), enum_field_index }); + break :msg msg; + }); + } + unreachable; // we already determined that *something* is wrong +} +fn resolvePackedUnionLayout( + sema: *Sema, + block: *Block, + union_ty: Type, + union_obj: *const InternPool.LoadedUnionType, + enum_tag_ty: Type, +) CompileError!void { + const pt = sema.pt; + const zcu = pt.zcu; + const comp = zcu.comp; + const io = comp.io; + const gpa = comp.gpa; + const ip = &zcu.intern_pool; + + // Uninstantiable `packed union`s don't make sense; disallow them. + if (union_obj.field_types.len == 0) { + return sema.fail(block, union_ty.srcLoc(zcu), "packed union has no fields", .{}); + } + + // Resolve the layout of all fields, and check their types are allowed. + for (union_obj.field_types.get(ip), 0..) |field_ty_ip, field_index| { + const field_ty: Type = .fromInterned(field_ty_ip); + assert(!field_ty.isGenericPoison()); + const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) }); + try sema.ensureLayoutResolved(field_ty, field_ty_src, .field); + if (field_ty.zigTypeTag(zcu) == .@"opaque") { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in union", .{field_ty.fmt(pt)}); + errdefer msg.destroy(gpa); + try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{}); + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + } + if (field_ty.unpackable(zcu)) |reason| return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(field_ty_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); + errdefer msg.destroy(gpa); + try sema.explainWhyTypeIsUnpackable(msg, field_ty_src, reason); + break :msg msg; + }); + assert(!field_ty.comptimeOnly(zcu)); // packable types are not comptime-only + } + + const explicit_backing_int_ty: ?Type = if (union_obj.is_reified) ty: { + switch (union_obj.packed_backing_mode) { + .explicit => break :ty .fromInterned(union_obj.packed_backing_int_type), + .auto => break :ty null, + } + } else ty: { + const zir_index = union_obj.zir_index.resolve(ip).?; + const zir_union = sema.code.getUnionDecl(zir_index); + const backing_int_type_body = zir_union.arg_type_body orelse { + break :ty null; // inferred backing type + }; + // Explicitly specified, so evaluate the backing int type expression. + const backing_int_type_src = block.src(.container_arg); + block.comptime_reason = .{ .reason = .{ + .src = backing_int_type_src, + .r = .{ .simple = .packed_union_backing_int_type }, + } }; + const type_ref = try sema.resolveInlineBody(block, backing_int_type_body, zir_index); + break :ty try sema.analyzeAsType(block, backing_int_type_src, .packed_union_backing_int_type, type_ref); + }; + + // Finally, either validate or infer the backing int type. + const backing_int_ty: Type = if (explicit_backing_int_ty) |backing_ty| ty: { + if (backing_ty.zigTypeTag(zcu) != .int) return sema.fail( + block, + block.src(.container_arg), + "expected backing integer type, found '{f}'", + .{backing_ty.fmt(pt)}, + ); + const backing_int_bits = backing_ty.intInfo(zcu).bits; + for (union_obj.field_types.get(ip), 0..) |field_type_ip, field_idx| { + const field_type: Type = .fromInterned(field_type_ip); + const field_bits = field_type.bitSize(zcu); + if (field_bits != backing_int_bits) return sema.failWithOwnedErrorMsg(block, msg: { + const field_ty_src = block.src(.{ .container_field_type = @intCast(field_idx) }); + const msg = try sema.errMsg(field_ty_src, "field bit width does not match backing integer", .{}); + errdefer msg.destroy(gpa); + try sema.errNote(field_ty_src, msg, "field type '{f}' has bit width '{d}'", .{ field_type.fmt(pt), field_bits }); + try sema.errNote( + block.src(.container_arg), + msg, + "backing integer '{f}' has bit width '{d}'", + .{ backing_ty.fmt(pt), backing_int_bits }, + ); + try sema.errNote(field_ty_src, msg, "all fields in a packed union must have the same bit width", .{}); + break :msg msg; + }); + } + break :ty backing_ty; + } else ty: { + const field_types = union_obj.field_types.get(ip); + const first_field_type: Type = .fromInterned(field_types[0]); + const first_field_bits = first_field_type.bitSize(zcu); + for (field_types[1..], 1..) |field_type_ip, field_idx| { + const field_type: Type = .fromInterned(field_type_ip); + const field_bits = field_type.bitSize(zcu); + if (field_bits != first_field_bits) return sema.failWithOwnedErrorMsg(block, msg: { + const first_field_ty_src = block.src(.{ .container_field_type = 0 }); + const field_ty_src = block.src(.{ .container_field_type = @intCast(field_idx) }); + const msg = try sema.errMsg(field_ty_src, "field bit width does not match earlier field", .{}); + errdefer msg.destroy(gpa); + try sema.errNote(field_ty_src, msg, "field type '{f}' has bit width '{d}'", .{ field_type.fmt(pt), field_bits }); + try sema.errNote(first_field_ty_src, msg, "other field type '{f}' has bit width '{d}'", .{ first_field_type.fmt(pt), first_field_bits }); + try sema.errNote(field_ty_src, msg, "all fields in a packed union must have the same bit width", .{}); + break :msg msg; + }); + } + const backing_int_bits = std.math.cast(u16, first_field_bits) orelse return sema.fail( + block, + union_ty.srcLoc(zcu), + "packed union bit width '{d}' exceeds maximum bit width of 65535", + .{first_field_bits}, + ); + break :ty try pt.intType(.unsigned, backing_int_bits); + }; + ip.resolvePackedUnionLayout( + io, + union_ty.toIntern(), + enum_tag_ty.toIntern(), + backing_int_ty.toIntern(), + ); +} + +pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { + const pt = sema.pt; + const zcu = pt.zcu; + const comp = zcu.comp; + const io = comp.io; + const gpa = comp.gpa; + const ip = &zcu.intern_pool; + + assert(sema.owner.unwrap().type_layout == enum_ty.toIntern()); + + const enum_obj = ip.loadEnumType(enum_ty.toIntern()); + assert(enum_obj.want_layout); + + const maybe_parent_union_obj: ?InternPool.LoadedUnionType = un: { + if (enum_obj.owner_union == .none) break :un null; + break :un ip.loadUnionType(enum_obj.owner_union); + }; + + const tracked_inst = enum_obj.zir_index.unwrap() orelse maybe_parent_union_obj.?.zir_index; + const zir_index = tracked_inst.resolve(ip) orelse return error.AnalysisFail; + + var block: Block = .{ + .parent = null, + .sema = sema, + .namespace = enum_obj.namespace, + .instructions = .empty, + .inlining = null, + .comptime_reason = undefined, // always set before using `block` + .src_base_inst = tracked_inst, + .type_name_ctx = enum_obj.name, + }; + defer block.instructions.deinit(gpa); + + // There may be old field names in the map from a previous update. + enum_obj.field_name_map.get(ip).clearRetainingCapacity(); + + if (maybe_parent_union_obj) |*union_obj| { + if (union_obj.is_reified) { + // In the case of reification, the union stores the field names, just for us to copy. + @memcpy(enum_obj.field_names.get(ip), union_obj.reified_field_names.get(ip)); + // The list of field names is now populated, but we haven't checked for duplicates yet, + // nor have we populated the hash map. + for (0..enum_obj.field_names.len) |field_index| { + const name = enum_obj.field_names.get(ip)[field_index]; + if (ip.addFieldName(enum_obj.field_names, enum_obj.field_name_map, name)) |prev_field_index| { + return sema.failWithOwnedErrorMsg(&block, msg: { + const src = block.builtinCallArgSrc(.zero, 2); + const msg = try sema.errMsg(src, "duplicate union field '{f}' at index '{d}", .{ name.fmt(ip), field_index }); + errdefer msg.destroy(gpa); + try sema.errNote(src, msg, "previous field at index '{d}'", .{prev_field_index}); + break :msg msg; + }); + } + } + } else { + // Generated tag enums for declared unions do not yet have field names populated. It is + // our job to populate them now. + try sema.declareDependency(.{ .src_hash = union_obj.zir_index }); + const zir_union = sema.code.getUnionDecl(zir_index); + for (zir_union.field_names) |zir_field_name| { + const name_slice = sema.code.nullTerminatedString(zir_field_name); + const name = try ip.getOrPutString(gpa, io, pt.tid, name_slice, .no_embedded_nulls); + assert(ip.addFieldName(enum_obj.field_names, enum_obj.field_name_map, name) == null); // AstGen validated this for us + } + } + } else { + if (enum_obj.is_reified) { + // The field names are populated, but we haven't checked for duplicates (nor populated the map) yet. + for (0..enum_obj.field_names.len) |field_index| { + const name = enum_obj.field_names.get(ip)[field_index]; + if (ip.addFieldName(enum_obj.field_names, enum_obj.field_name_map, name)) |prev_field_index| { + return sema.failWithOwnedErrorMsg(&block, msg: { + const src = block.builtinCallArgSrc(.zero, 2); + const msg = try sema.errMsg(src, "duplicate enum field '{f}' at index '{d}'", .{ name.fmt(ip), field_index }); + errdefer msg.destroy(gpa); + try sema.errNote(src, msg, "previous field at index '{d}'", .{prev_field_index}); + break :msg msg; + }); + } + } + } else { + // Declared enums do not yet have field names populated. It is our job to populate them now. + try sema.declareDependency(.{ .src_hash = enum_obj.zir_index.unwrap().? }); + const zir_enum = sema.code.getEnumDecl(zir_index); + for (zir_enum.field_names) |zir_field_name| { + const name_slice = sema.code.nullTerminatedString(zir_field_name); + const name = try ip.getOrPutString(gpa, io, pt.tid, name_slice, .no_embedded_nulls); + assert(ip.addFieldName(enum_obj.field_names, enum_obj.field_name_map, name) == null); // AstGen validated this for us + } + } + } + + // Field names populated; now deal with the backing integer type. If explicitly provided, + // validate it; otherwise, infer it. + + const explicit_int_tag_ty: ?Type = if (enum_obj.is_reified) ty: { + break :ty switch (enum_obj.int_tag_mode) { + .explicit => .fromInterned(enum_obj.int_tag_type), + .auto => null, + }; + } else if (maybe_parent_union_obj) |*union_obj| ty: { + if (union_obj.is_reified) { + // Reification has no equivalent of 'union(enum(T))'. + break :ty null; + } + const zir_union = sema.code.getUnionDecl(zir_index); + if (zir_union.kind != .tagged_enum_explicit) { + break :ty null; // int tag type will be inferred + } + // Explicitly specified, so evaluate the int tag type expression. + const tag_type_body = zir_union.arg_type_body.?; + const tag_type_src = block.src(.container_arg); + block.comptime_reason = .{ .reason = .{ + .src = tag_type_src, + .r = .{ .simple = .enum_int_tag_type }, + } }; + const type_ref = try sema.resolveInlineBody(&block, tag_type_body, zir_index); + break :ty try sema.analyzeAsType(&block, tag_type_src, .enum_int_tag_type, type_ref); + } else ty: { + const zir_enum = sema.code.getEnumDecl(zir_index); + const tag_type_body = zir_enum.tag_type_body orelse { + break :ty null; // int tag type will be inferred + }; + // Explicitly specified, so evaluate the int tag type expression. + const tag_type_src = block.src(.container_arg); + block.comptime_reason = .{ .reason = .{ + .src = tag_type_src, + .r = .{ .simple = .enum_int_tag_type }, + } }; + const type_ref = try sema.resolveInlineBody(&block, tag_type_body, zir_index); + break :ty try sema.analyzeAsType(&block, tag_type_src, .enum_int_tag_type, type_ref); + }; + const int_tag_ty: Type = if (explicit_int_tag_ty) |int_tag_ty| ty: { + if (int_tag_ty.zigTypeTag(zcu) != .int) return sema.fail( + &block, + block.src(.container_arg), + "expected integer tag type, found '{f}'", + .{int_tag_ty.fmt(pt)}, + ); + break :ty int_tag_ty; + } else ty: { + // Infer the int tag type from the field count + const bits = Type.smallestUnsignedBits(enum_obj.field_names.len -| 1); + break :ty try pt.intType(.unsigned, bits); + }; + + ip.resolveEnumLayout(io, enum_ty.toIntern(), int_tag_ty.toIntern()); + + // Finally, deal with field values. For declared types we need to analyze the expressions, while + // reified types already have them populated; but either way, we need to populate the hash map + // (and validate the values along the way). + + // We'll populate this map. + const field_value_map = enum_obj.field_value_map.unwrap() orelse { + // The enum is auto-numbered with an inferred tag type. We know that the tag type generated + // earlier is sufficient for the number of fields, so we have nothing more to do. + assert(enum_obj.int_tag_mode == .auto); + return; + }; + + // There may be old field values in here from a previous update. + field_value_map.get(ip).clearRetainingCapacity(); + + // Map the enum (or union) decl instruction to provide the tag type as the result type + try sema.inst_map.ensureSpaceForInstructions(gpa, &.{zir_index}); + sema.inst_map.putAssumeCapacity(zir_index, .fromIntern(int_tag_ty.toIntern())); + defer assert(sema.inst_map.remove(zir_index)); + + // First, populate any explicitly provided values. This is the part that actually depends on + // the ZIR, and hence depends on whether this is a declared or generated enum. If any explicit + // value is straight-up invalid, we'll emit an error here. + if (maybe_parent_union_obj) |union_obj| { + if (union_obj.is_reified) { + // Generated tag type for reified union; values already populated. + } else { + // Generated tag type for declared union; evaluate the expressions given in the union declaration. + const zir_union = sema.code.getUnionDecl(zir_index); + var field_it = zir_union.iterateFields(); + while (field_it.next()) |zir_field| { + const field_val_src = block.src(.{ .container_field_value = zir_field.idx }); + block.comptime_reason = .{ .reason = .{ + .src = field_val_src, + .r = .{ .simple = .enum_field_values }, + } }; + const value_body = zir_field.value_body orelse { + enum_obj.field_values.get(ip)[zir_field.idx] = .none; + continue; + }; + const uncoerced = try sema.resolveInlineBody(&block, value_body, zir_index); + const coerced = try sema.coerce(&block, int_tag_ty, uncoerced, field_val_src); + const val = try sema.resolveConstValue(&block, field_val_src, coerced, null); + enum_obj.field_values.get(ip)[zir_field.idx] = val.toIntern(); + } + } + } else if (enum_obj.is_reified) { + // Reified enum; values already populated. + } else { + // Declared enum; evaluate the expressions given in the enum declaration. + const zir_enum = sema.code.getEnumDecl(zir_index); + var field_it = zir_enum.iterateFields(); + while (field_it.next()) |zir_field| { + const field_val_src = block.src(.{ .container_field_value = zir_field.idx }); + block.comptime_reason = .{ .reason = .{ + .src = field_val_src, + .r = .{ .simple = .enum_field_values }, + } }; + const value_body = zir_field.value_body orelse { + enum_obj.field_values.get(ip)[zir_field.idx] = .none; + continue; + }; + const uncoerced = try sema.resolveInlineBody(&block, value_body, zir_index); + const coerced = try sema.coerce(&block, int_tag_ty, uncoerced, field_val_src); + const val = try sema.resolveConstDefinedValue(&block, field_val_src, coerced, null); + enum_obj.field_values.get(ip)[zir_field.idx] = val.toIntern(); + } + } + + // Explicit values are set. Now we'll go through the whole array and figure out the final + // field values. This is also where we'll detect duplicates. + + for (0..enum_obj.field_names.len) |field_idx| { + const field_val_src = block.src(.{ .container_field_value = @intCast(field_idx) }); + // If the field value was not specified, compute the implicit value. + const field_val = val: { + const explicit_val = enum_obj.field_values.get(ip)[field_idx]; + if (explicit_val != .none) { + assert(ip.typeOf(explicit_val) == int_tag_ty.toIntern()); + break :val explicit_val; + } + if (field_idx == 0) { + // Implicit value is 0, which is valid for every integer type. + const val = (try pt.intValue(int_tag_ty, 0)).toIntern(); + enum_obj.field_values.get(ip)[field_idx] = val; + break :val val; + } + // Implicit non-initial value: take the previous field value and add one. + const prev_field_val: Value = .fromInterned(enum_obj.field_values.get(ip)[field_idx - 1]); + const result = try arith.incrementDefinedInt(sema, int_tag_ty, prev_field_val); + if (result.overflow) return sema.fail( + &block, + field_val_src, + "enum tag value '{f}' too large for type '{f}'", + .{ result.val.fmtValueSema(pt, sema), int_tag_ty.fmt(pt) }, + ); + const val = result.val.toIntern(); + enum_obj.field_values.get(ip)[field_idx] = val; + break :val val; + }; + if (ip.addFieldTagValue(enum_obj.field_values, field_value_map, field_val)) |prev_field_index| { + return sema.failWithOwnedErrorMsg(&block, msg: { + const prev_field_val_src = block.src(.{ .container_field_value = prev_field_index }); + const msg = try sema.errMsg(field_val_src, "enum tag value '{f}' for field '{f}' already taken", .{ + Value.fromInterned(field_val).fmtValueSema(pt, sema), + enum_obj.field_names.get(ip)[field_idx].fmt(ip), + }); + errdefer msg.destroy(gpa); + try sema.errNote(prev_field_val_src, msg, "previous occurrence in field '{f}'", .{ + enum_obj.field_names.get(ip)[prev_field_index].fmt(ip), + }); + break :msg msg; + }); + } + } + + if (enum_obj.nonexhaustive) { + const fields_len = enum_obj.field_names.len; + if (fields_len >= 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(zcu)) { + return sema.fail(&block, block.nodeOffset(.zero), "non-exhaustive enum specifies every value", .{}); + } + } +} diff --git a/src/Type.zig b/src/Type.zig @@ -12,12 +12,10 @@ const Target = std.Target; const Zcu = @import("Zcu.zig"); const log = std.log.scoped(.Type); const target_util = @import("target.zig"); -const Sema = @import("Sema.zig"); const InternPool = @import("InternPool.zig"); const Alignment = InternPool.Alignment; const Zir = std.zig.Zir; const Type = @This(); -const SemaError = Zcu.SemaError; ip_index: InternPool.Index, @@ -25,14 +23,288 @@ pub fn zigTypeTag(ty: Type, zcu: *const Zcu) std.builtin.TypeId { return zcu.intern_pool.zigTypeTag(ty.toIntern()); } -pub fn baseZigTypeTag(self: Type, mod: *Zcu) std.builtin.TypeId { - return switch (self.zigTypeTag(mod)) { - .error_union => self.errorUnionPayload(mod).baseZigTypeTag(mod), - .optional => { - return self.optionalChild(mod).baseZigTypeTag(mod); +/// Every type is a member of exactly one "class" which determines: +/// * whether values of the type can exist at all +/// * whether values of the type can be runtime-knwon +/// * whether the type is considered comptime-only +/// * whether the type has runtime bits (nonzero ABI size) +pub const Class = enum(u3) { + /// Values of this type cannot exist because the type semantically has no values. Attempting to + /// create a value of this type (such as by coercing `undefined`) always emits a compile error. + /// + /// Not comptime-only. No runtime bits, i.e. ABI size is 0. + /// + /// Exhaustive list of no-possible-value ("NPV") types: + /// * `noreturn` + /// * `anyopaque`, and any `opaque` type + /// * `[n]T` where `n` is non-zero and `T` is NPV + /// * Any tuple where at least one non-`comptime` field has an NPV type + /// * Any enum whose backing type is `noreturn` + /// * Any struct where at least one non-`comptime` field has an NPV type + /// * Any union where every field has an NPV type (including unions with no fields) + /// * If the union would typically have a runtime tag, even if that tag would have runtime + /// bits, the union type is still NPV; the runtime tag is effectively omitted. + no_possible_value, + + /// Values of this type are always comptime-known because there is only one value inhabiting the + /// type. This matches the colloquial understanding of a "zero-bit type". + /// + /// Not comptime-only (although always comptime-known). No runtime bits, i.e. ABI size is 0. + /// + /// Exhaustive list of one-possible-value ("OPV") types: + /// * `void` + /// * `u0`, `i0` + /// * `[0]T` for any `T` + /// * `[n]T` where `T` is OPV + /// * `[n:s]T` where `T` is OPV + /// * `@Vector(0, T)` for any `T` + /// * `@Vector(n, T)` where `T` is OPV + /// * Any tuple where every non-`comptime` field has an OPV type (including tuples with no fields) + /// * Any enum whose backing type is OPV + /// * Any struct where every non-`comptime` field has an OPV type (including structs with no fields) + /// * Any union with no runtime tag where all fields have OPV + /// * Any union where one field has an OPV type, and either: + /// * All other fields have NPV types (in this case, if there would be a runtime tag, it is omitted) + /// * All other fields have NPV or OPV types, and the union has no runtime tag + one_possible_value, + + /// The type holds state (so it is neither NPV nor OPV), but contains no comptime-only state, so + /// values may be runtime-known. + /// + /// Not comptime-only. Has runtime bits, i.e. ABI size is non-zero. + /// + /// Most types which are typically used in Zig inhabit this class. For instance, all pointer + /// types, all integer types other than `u0` and `i0`, and most user-defined aggregates fall + /// into this category. + runtime, + + /// The type holds state (so it is neither NPV nor OPV). Some, but not all, of the contained + /// state is comptime-only. + /// + /// Comptime-only. Has runtime bits, i.e. ABI size is non-zero. + /// + /// Partially-comptime types arise from aggregates (`struct`s, `union`s, or tuples) which have + /// some fields with fully-comptime types (such as `comptime_int`) and some fields with runtime + /// types (such as `u8`). Because the user may acquire pointers to these fields, pointers to the + /// embedded runtime state must be valid, so backends are required to lower the runtime state + /// within the type. + /// + /// Note that logically-runtime state which cannot be directly referenced by the user (such as + /// the enum tag of a tagged union type, or the "populated" bit of an optional type) does not + /// cause a type to be partially-comptime. + partially_comptime, + + /// The type contains exclusively comptime-only state. + /// + /// Comptime-only. No runtime bits, i.e. ABI size is 0. + /// + /// Fully-comptime types arise from a handful of primitive fully-comptime types: + /// * `type` + /// * `comptime_int` + /// * `comptime_float` + /// * `@EnumLiteral()` + /// * `@TypeOf(null)` + /// * `@TypeOf(undefined)` + /// + /// Then, aggregates containing fully-comptime types may themselves be either fully-comptime or + /// partially-comptime; see the doc comment on `.partially_comptime` for details. + fully_comptime, +}; + +/// Returns the `Class` for the type `ty`. Asserts that the layout of `ty` is resolved. +pub fn classify(start_ty: Type, zcu: *const Zcu) Class { + const ip = &zcu.intern_pool; + + // We avoid recursion in most cases to make us more optimizer-friendly because this can be a + // very hot code path. The only case where recursion is necessary is tuples, so that case is + // outlined into a separate function; see `classifyTuple`. + + var extra_states: enum { none, one, many } = .none; + + var cur_ty = start_ty; + const base: Class = while (true) break switch (ip.indexToKey(cur_ty.toIntern())) { + .simple_type => |t| switch (t) { + .f16, + .f32, + .f64, + .f80, + .f128, + .usize, + .isize, + .c_char, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .c_longdouble, + .bool, + .anyerror, + .adhoc_inferred_error_set, + => .runtime, + + .anyopaque => .no_possible_value, + + .type, + .comptime_int, + .comptime_float, + .enum_literal, + .null, + .undefined, + => .fully_comptime, + + .void => .one_possible_value, + .noreturn => .no_possible_value, + + .generic_poison => unreachable, + }, + + .error_set_type, + .inferred_error_set_type, + .ptr_type, + .anyframe_type, + => .runtime, + + .func_type => .fully_comptime, + + .opaque_type => .no_possible_value, + + .error_union_type => |eu| { + extra_states = .many; + cur_ty = .fromInterned(eu.payload_type); + continue; + }, + + .int_type => |int| switch (int.bits) { + 0 => .one_possible_value, + else => .runtime, + }, + .array_type => |arr| { + if (arr.len == 0 and arr.sentinel == .none) break .one_possible_value; + cur_ty = .fromInterned(arr.child); + continue; + }, + .vector_type => |vec| { + if (vec.len == 0) break .one_possible_value; + cur_ty = .fromInterned(vec.child); + continue; + }, + .opt_type => |child_ty_ip| { + extra_states = switch (extra_states) { + .none => .one, + .one, .many => .many, + }; + cur_ty = .fromInterned(child_ty_ip); + continue; + }, + .tuple_type => |tuple| { + @branchHint(.unlikely); + break classifyTuple(tuple.types.get(ip), tuple.values.get(ip), zcu); + }, + .struct_type => { + const struct_obj = ip.loadStructType(cur_ty.toIntern()); + switch (struct_obj.layout) { + .auto, .@"extern" => { + zcu.assertUpToDate(.wrap(.{ .type_layout = cur_ty.toIntern() })); + break struct_obj.class; + }, + .@"packed" => { + cur_ty = .fromInterned(struct_obj.packed_backing_int_type); + continue; + }, + } + }, + .union_type => { + const union_obj = ip.loadUnionType(cur_ty.toIntern()); + switch (union_obj.layout) { + .auto, .@"extern" => { + zcu.assertUpToDate(.wrap(.{ .type_layout = cur_ty.toIntern() })); + break union_obj.class; + }, + .@"packed" => { + cur_ty = .fromInterned(union_obj.packed_backing_int_type); + continue; + }, + } + }, + .enum_type => { + zcu.assertUpToDate(.wrap(.{ .type_layout = cur_ty.toIntern() })); + cur_ty = .fromInterned(ip.loadEnumType(cur_ty.toIntern()).int_tag_type); + continue; }, - else => |t| t, + + // values, not types + .undef, + .simple_value, + .variable, + .@"extern", + .func, + .int, + .err, + .error_union, + .enum_literal, + .enum_tag, + .float, + .ptr, + .slice, + .opt, + .aggregate, + .un, + .bitpack, + // memoization, not types + .memoized_call, + => unreachable, }; + + return switch (base) { + .runtime => .runtime, // extra states are irrelevant, we already have many! + .partially_comptime => .partially_comptime, // likewise + .fully_comptime => { + // We do not need to change to `.partially_comptime` here because the extra states do + // not necessarily require runtime bits. This is because Zig does not provide a way to + // take the address of the "is null" bit of an optional or the error set "inside" of an + // error union. + return .fully_comptime; + }, + + .no_possible_value => switch (extra_states) { + .none => .no_possible_value, + .one => .one_possible_value, + .many => .runtime, + }, + + .one_possible_value => switch (extra_states) { + .none => .one_possible_value, + .one, .many => .runtime, + }, + }; +} +/// This is a separate function to `classify` to avoid recursion in the main `classify` function, +/// which can encourage the optimizer to e.g. inline `classify` where it would be beneficial. +fn classifyTuple(types: []const InternPool.Index, values: []const InternPool.Index, zcu: *const Zcu) Class { + var has_runtime_state = false; + var has_comptime_state = false; + for (types, values) |field_ty, field_comptime_val| { + if (field_comptime_val != .none) continue; + switch (Type.fromInterned(field_ty).classify(zcu)) { + .no_possible_value => return .no_possible_value, + .one_possible_value => {}, + .runtime => has_runtime_state = true, + .fully_comptime => has_comptime_state = true, + .partially_comptime => { + has_runtime_state = true; + has_comptime_state = true; + }, + } + } + if (has_comptime_state) { + return if (has_runtime_state) .partially_comptime else .fully_comptime; + } else { + return if (has_runtime_state) .runtime else .one_possible_value; + } } /// Asserts the type is resolved. @@ -44,7 +316,7 @@ pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool { .comptime_int, => true, - .vector => ty.elemType2(zcu).isSelfComparable(zcu, is_equality_cmp), + .vector => ty.childType(zcu).isSelfComparable(zcu, is_equality_cmp), .bool, .type, @@ -121,11 +393,7 @@ pub fn eql(a: Type, b: Type, zcu: *const Zcu) bool { return a.toIntern() == b.toIntern(); } -pub fn format(ty: Type, writer: *std.Io.Writer) !void { - _ = ty; - _ = writer; - @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()"); -} +pub const format = @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()"); pub const Formatter = std.fmt.Alt(Format, Format.default); @@ -416,13 +684,13 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread, ctx: ?*Compari .error_union, .enum_literal, .enum_tag, - .empty_enum_value, .float, .ptr, .slice, .opt, .aggregate, .un, + .bitpack, // memoization, not types .memoized_call, => unreachable, @@ -440,247 +708,41 @@ pub fn toIntern(ty: Type) InternPool.Index { } pub fn toValue(self: Type) Value { - return Value.fromInterned(self.toIntern()); + return .fromInterned(self.toIntern()); } -const RuntimeBitsError = SemaError || error{NeedLazy}; - +/// Returns `true` if and only if the type takes up space in memory at runtime. This is also exactly +/// whether or not the backend/linker needs to be sent values of this type to emit to the binary. +/// +/// Types without runtime bits have an ABI size of 0; all other types have a non-zero ABI size. All +/// types, regardless of whether they have runtime bits, have a non-zero ABI alignment. +/// +/// Comptime-only types may still have runtime bits. For instance, `struct { a: u32, b: type }` is a +/// comptime-only type, but it nonetheless has runtime bits and a runtime memory layout (where the +/// field `b: type` is omitted). This is because a user may take a pointer to the field `a`, which +/// must then be valid to use at runtime. +/// +/// This function is a trivial wrapper around `classify`: +/// +/// * Types with one possible value, such as `void`, or no possible value, such as `noreturn`, do +/// not have runtime bits and have an ABI size of 0 because they simply contain no state. +/// +/// * Types which are fully comptime, such as `type` and `comptime_int`, do not have runtime bits +/// because they contain only comptime state. (This compiler implementation also currently makes +/// types like `struct { x: comptime_int }` fully comptime, but that could change in the future if +/// we start inserting hidden safety fields into them.) +/// +/// * All other types contain some runtime state, so have runtime bits and a non-zero ABI size. pub fn hasRuntimeBits(ty: Type, zcu: *const Zcu) bool { - return hasRuntimeBitsInner(ty, false, .eager, zcu, {}) catch unreachable; -} - -pub fn hasRuntimeBitsSema(ty: Type, pt: Zcu.PerThread) SemaError!bool { - return hasRuntimeBitsInner(ty, false, .sema, pt.zcu, pt.tid) catch |err| switch (err) { - error.NeedLazy => unreachable, // this would require a resolve strat of lazy - else => |e| return e, - }; -} - -pub fn hasRuntimeBitsIgnoreComptime(ty: Type, zcu: *const Zcu) bool { - return hasRuntimeBitsInner(ty, true, .eager, zcu, {}) catch unreachable; -} - -pub fn hasRuntimeBitsIgnoreComptimeSema(ty: Type, pt: Zcu.PerThread) SemaError!bool { - return hasRuntimeBitsInner(ty, true, .sema, pt.zcu, pt.tid) catch |err| switch (err) { - error.NeedLazy => unreachable, // this would require a resolve strat of lazy - else => |e| return e, - }; -} - -/// true if and only if the type takes up space in memory at runtime. -/// There are two reasons a type will return false: -/// * the type is a comptime-only type. For example, the type `type` itself. -/// - note, however, that a struct can have mixed fields and only the non-comptime-only -/// fields will count towards the ABI size. For example, `struct {T: type, x: i32}` -/// hasRuntimeBits()=true and abiSize()=4 -/// * the type has only one possible value, making its ABI size 0. -/// - an enum with an explicit tag type has the ABI size of the integer tag type, -/// making it one-possible-value only if the integer tag type has 0 bits. -/// When `ignore_comptime_only` is true, then types that are comptime-only -/// may return false positives. -pub fn hasRuntimeBitsInner( - ty: Type, - ignore_comptime_only: bool, - comptime strat: ResolveStratLazy, - zcu: strat.ZcuPtr(), - tid: strat.Tid(), -) RuntimeBitsError!bool { - const ip = &zcu.intern_pool; - const io = zcu.comp.io; - return switch (ty.toIntern()) { - .empty_tuple_type => false, - else => switch (ip.indexToKey(ty.toIntern())) { - .int_type => |int_type| int_type.bits != 0, - .ptr_type => { - // Pointers to zero-bit types still have a runtime address; however, pointers - // to comptime-only types do not, with the exception of function pointers. - if (ignore_comptime_only) return true; - return switch (strat) { - .sema => { - const pt = strat.pt(zcu, tid); - return !try ty.comptimeOnlySema(pt); - }, - .eager => !ty.comptimeOnly(zcu), - .lazy => error.NeedLazy, - }; - }, - .anyframe_type => true, - .array_type => |array_type| return array_type.lenIncludingSentinel() > 0 and - try Type.fromInterned(array_type.child).hasRuntimeBitsInner(ignore_comptime_only, strat, zcu, tid), - .vector_type => |vector_type| return vector_type.len > 0 and - try Type.fromInterned(vector_type.child).hasRuntimeBitsInner(ignore_comptime_only, strat, zcu, tid), - .opt_type => |child| { - const child_ty = Type.fromInterned(child); - if (child_ty.isNoReturn(zcu)) { - // Then the optional is comptime-known to be null. - return false; - } - if (ignore_comptime_only) return true; - return switch (strat) { - .sema => !try child_ty.comptimeOnlyInner(.sema, zcu, tid), - .eager => !child_ty.comptimeOnly(zcu), - .lazy => error.NeedLazy, - }; - }, - .error_union_type, - .error_set_type, - .inferred_error_set_type, - => true, - - // These are function *bodies*, not pointers. - // They return false here because they are comptime-only types. - // Special exceptions have to be made when emitting functions due to - // this returning false. - .func_type => false, - - .simple_type => |t| switch (t) { - .f16, - .f32, - .f64, - .f80, - .f128, - .usize, - .isize, - .c_char, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .c_longdouble, - .bool, - .anyerror, - .adhoc_inferred_error_set, - .anyopaque, - => true, - - // These are false because they are comptime-only types. - .void, - .type, - .comptime_int, - .comptime_float, - .noreturn, - .null, - .undefined, - .enum_literal, - => false, - - .generic_poison => unreachable, - }, - .struct_type => { - const struct_type = ip.loadStructType(ty.toIntern()); - if (strat != .eager and struct_type.assumeRuntimeBitsIfFieldTypesWip(ip, io)) { - // In this case, we guess that hasRuntimeBits() for this type is true, - // and then later if our guess was incorrect, we emit a compile error. - return true; - } - switch (strat) { - .sema => try ty.resolveFields(strat.pt(zcu, tid)), - .eager => assert(struct_type.haveFieldTypes(ip)), - .lazy => if (!struct_type.haveFieldTypes(ip)) return error.NeedLazy, - } - for (0..struct_type.field_types.len) |i| { - if (struct_type.comptime_bits.getBit(ip, i)) continue; - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); - if (try field_ty.hasRuntimeBitsInner(ignore_comptime_only, strat, zcu, tid)) - return true; - } else { - return false; - } - }, - .tuple_type => |tuple| { - for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| { - if (val != .none) continue; // comptime field - if (try Type.fromInterned(field_ty).hasRuntimeBitsInner( - ignore_comptime_only, - strat, - zcu, - tid, - )) return true; - } - return false; - }, - - .union_type => { - const union_type = ip.loadUnionType(ty.toIntern()); - const union_flags = union_type.flagsUnordered(ip); - switch (union_flags.runtime_tag) { - .none => if (strat != .eager) { - // In this case, we guess that hasRuntimeBits() for this type is true, - // and then later if our guess was incorrect, we emit a compile error. - if (union_type.assumeRuntimeBitsIfFieldTypesWip(ip, io)) return true; - }, - .safety, .tagged => {}, - } - switch (strat) { - .sema => try ty.resolveFields(strat.pt(zcu, tid)), - .eager => assert(union_flags.status.haveFieldTypes()), - .lazy => if (!union_flags.status.haveFieldTypes()) - return error.NeedLazy, - } - switch (union_flags.runtime_tag) { - .none => {}, - .safety, .tagged => { - const tag_ty = union_type.tagTypeUnordered(ip); - assert(tag_ty != .none); // tag_ty should have been resolved above - if (try Type.fromInterned(tag_ty).hasRuntimeBitsInner( - ignore_comptime_only, - strat, - zcu, - tid, - )) { - return true; - } - }, - } - for (0..union_type.field_types.len) |field_index| { - const field_ty = Type.fromInterned(union_type.field_types.get(ip)[field_index]); - if (try field_ty.hasRuntimeBitsInner(ignore_comptime_only, strat, zcu, tid)) - return true; - } else { - return false; - } - }, - - .opaque_type => true, - .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty).hasRuntimeBitsInner( - ignore_comptime_only, - strat, - zcu, - tid, - ), - - // values, not types - .undef, - .simple_value, - .variable, - .@"extern", - .func, - .int, - .err, - .error_union, - .enum_literal, - .enum_tag, - .empty_enum_value, - .float, - .ptr, - .slice, - .opt, - .aggregate, - .un, - // memoization, not types - .memoized_call, - => unreachable, - }, + return switch (ty.classify(zcu)) { + .no_possible_value, .one_possible_value, .fully_comptime => false, + .runtime, .partially_comptime => true, }; } -/// true if and only if the type has a well-defined memory layout -/// readFrom/writeToMemory are supported only for types with a well- -/// defined memory layout +/// Returns `true` iff the memory layout of `ty` is defined by the Zig language specification. +/// +/// Does not require `ty` to be resolved. pub fn hasWellDefinedLayout(ty: Type, zcu: *const Zcu) bool { const ip = &zcu.intern_pool; return switch (ip.indexToKey(ty.toIntern())) { @@ -737,17 +799,17 @@ pub fn hasWellDefinedLayout(ty: Type, zcu: *const Zcu) bool { .generic_poison, => false, }, - .struct_type => ip.loadStructType(ty.toIntern()).layout != .auto, - .union_type => { - const union_type = ip.loadUnionType(ty.toIntern()); - return switch (union_type.flagsUnordered(ip).runtime_tag) { - .none, .safety => union_type.flagsUnordered(ip).layout != .auto, - .tagged => false, - }; + .struct_type => switch (ip.loadStructType(ty.toIntern()).layout) { + .auto => false, + .@"extern", .@"packed" => true, + }, + .union_type => switch (ip.loadUnionType(ty.toIntern()).layout) { + .auto => false, + .@"extern", .@"packed" => true, }, - .enum_type => switch (ip.loadEnumType(ty.toIntern()).tag_mode) { + .enum_type => switch (ip.loadEnumType(ty.toIntern()).int_tag_mode) { + .explicit => true, .auto => false, - .explicit, .nonexhaustive => true, }, // values, not types @@ -761,86 +823,88 @@ pub fn hasWellDefinedLayout(ty: Type, zcu: *const Zcu) bool { .error_union, .enum_literal, .enum_tag, - .empty_enum_value, .float, .ptr, .slice, .opt, .aggregate, .un, + .bitpack, // memoization, not types .memoized_call, => unreachable, }; } -pub fn fnHasRuntimeBits(ty: Type, zcu: *Zcu) bool { - return ty.fnHasRuntimeBitsInner(.normal, zcu, {}) catch unreachable; -} - -pub fn fnHasRuntimeBitsSema(ty: Type, pt: Zcu.PerThread) SemaError!bool { - return try ty.fnHasRuntimeBitsInner(.sema, pt.zcu, pt.tid); -} - /// Determines whether a function type has runtime bits, i.e. whether a /// function with this type can exist at runtime. /// Asserts that `ty` is a function type. -pub fn fnHasRuntimeBitsInner( - ty: Type, - comptime strat: ResolveStrat, - zcu: strat.ZcuPtr(), - tid: strat.Tid(), -) SemaError!bool { - const fn_info = zcu.typeToFunc(ty).?; - if (fn_info.is_generic) return false; - if (fn_info.is_var_args) return true; +pub fn fnHasRuntimeBits(fn_ty: Type, zcu: *const Zcu) bool { + assertHasLayout(fn_ty, zcu); + const fn_info = zcu.typeToFunc(fn_ty).?; + if (fn_info.comptime_bits != 0) return false; + for (fn_info.param_types.get(&zcu.intern_pool)) |param_ty| { + if (param_ty == .generic_poison_type) return false; + switch (Type.fromInterned(param_ty).classify(zcu)) { + .fully_comptime, + .partially_comptime, + .no_possible_value, + => return false, + + .one_possible_value, + .runtime, + => {}, + } + } + const ret_ty: Type = .fromInterned(fn_info.return_type); + if (ret_ty.toIntern() == .generic_poison_type) { + return false; + } + if (ret_ty.zigTypeTag(zcu) == .error_union and + ret_ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type) + { + return false; + } + switch (ret_ty.classify(zcu)) { + .fully_comptime, + .partially_comptime, + => return false, + + .no_possible_value, + .one_possible_value, + .runtime, + => {}, + } if (fn_info.cc == .@"inline") return false; - return !try Type.fromInterned(fn_info.return_type).comptimeOnlyInner(strat, zcu, tid); + return true; } -pub fn isFnOrHasRuntimeBits(ty: Type, zcu: *Zcu) bool { +/// Like `hasRuntimeBits`, but also returns `true` for runtime functions. +pub fn isRuntimeFnOrHasRuntimeBits(ty: Type, zcu: *const Zcu) bool { switch (ty.zigTypeTag(zcu)) { .@"fn" => return ty.fnHasRuntimeBits(zcu), else => return ty.hasRuntimeBits(zcu), } } -/// Same as `isFnOrHasRuntimeBits` but comptime-only types may return a false positive. -pub fn isFnOrHasRuntimeBitsIgnoreComptime(ty: Type, zcu: *Zcu) bool { - return switch (ty.zigTypeTag(zcu)) { - .@"fn" => true, - else => return ty.hasRuntimeBitsIgnoreComptime(zcu), - }; -} - +/// Returns whether `ty` is NPV, meaning it is "like `noreturn`" in a sense. See doc comments on +/// `Class` for more details. +/// +/// Exactly equivalent to `ty.classify(zcu) == .no_possible_value`. pub fn isNoReturn(ty: Type, zcu: *const Zcu) bool { - return zcu.intern_pool.isNoReturn(ty.toIntern()); + return ty.classify(zcu) == .no_possible_value; } /// Never returns `none`. Asserts that all necessary type resolution is already done. -pub fn ptrAlignment(ty: Type, zcu: *Zcu) Alignment { - return ptrAlignmentInner(ty, .normal, zcu, {}) catch unreachable; -} - -pub fn ptrAlignmentSema(ty: Type, pt: Zcu.PerThread) SemaError!Alignment { - return try ty.ptrAlignmentInner(.sema, pt.zcu, pt.tid); -} - -pub fn ptrAlignmentInner( - ty: Type, - comptime strat: ResolveStrat, - zcu: strat.ZcuPtr(), - tid: strat.Tid(), -) !Alignment { - return switch (zcu.intern_pool.indexToKey(ty.toIntern())) { - .ptr_type => |ptr_type| { - if (ptr_type.flags.alignment != .none) return ptr_type.flags.alignment; - const res = try Type.fromInterned(ptr_type.child).abiAlignmentInner(strat.toLazy(), zcu, tid); - return res.scalar; - }, - .opt_type => |child| Type.fromInterned(child).ptrAlignmentInner(strat, zcu, tid), +pub fn ptrAlignment(ptr_ty: Type, zcu: *Zcu) Alignment { + const ip = &zcu.intern_pool; + const ptr_key: InternPool.Key.PtrType = switch (ip.indexToKey(ptr_ty.toIntern())) { + .ptr_type => |key| key, + .opt_type => |child| ip.indexToKey(child).ptr_type, else => unreachable, }; + if (ptr_key.flags.alignment != .none) return ptr_key.flags.alignment; + return Type.fromInterned(ptr_key.child).abiAlignment(zcu); } pub fn ptrAddressSpace(ty: Type, zcu: *const Zcu) std.builtin.AddressSpace { @@ -851,861 +915,364 @@ pub fn ptrAddressSpace(ty: Type, zcu: *const Zcu) std.builtin.AddressSpace { }; } -/// May capture a reference to `ty`. -/// Returned value has type `comptime_int`. -pub fn lazyAbiAlignment(ty: Type, pt: Zcu.PerThread) !Value { - switch (try ty.abiAlignmentInner(.lazy, pt.zcu, pt.tid)) { - .val => |val| return val, - .scalar => |x| return pt.intValue(Type.comptime_int, x.toByteUnits() orelse 0), - } -} - -pub const AbiAlignmentInner = union(enum) { - scalar: Alignment, - val: Value, -}; - -pub const ResolveStratLazy = enum { - /// Return a `lazy_size` or `lazy_align` value if necessary. - /// This value can be resolved later using `Value.resolveLazy`. - lazy, - /// Return a scalar result, expecting all necessary type resolution to be completed. - /// Backends should typically use this, since they must not perform type resolution. - eager, - /// Return a scalar result, performing type resolution as necessary. - /// This should typically be used from semantic analysis. - sema, - - pub fn Tid(strat: ResolveStratLazy) type { - return switch (strat) { - .lazy, .sema => Zcu.PerThread.Id, - .eager => void, - }; - } - - pub fn ZcuPtr(strat: ResolveStratLazy) type { - return switch (strat) { - .eager => *const Zcu, - .sema, .lazy => *Zcu, - }; - } - - pub fn pt( - comptime strat: ResolveStratLazy, - zcu: strat.ZcuPtr(), - tid: strat.Tid(), - ) switch (strat) { - .lazy, .sema => Zcu.PerThread, - .eager => void, - } { - return switch (strat) { - .lazy, .sema => .{ .tid = tid, .zcu = zcu }, - else => {}, - }; - } -}; - -/// The chosen strategy can be easily optimized away in release builds. -/// However, in debug builds, it helps to avoid accidentally resolving types in backends. -pub const ResolveStrat = enum { - /// Assert that all necessary resolution is completed. - /// Backends should typically use this, since they must not perform type resolution. - normal, - /// Perform type resolution as necessary using `Zcu`. - /// This should typically be used from semantic analysis. - sema, - - pub fn Tid(strat: ResolveStrat) type { - return switch (strat) { - .sema => Zcu.PerThread.Id, - .normal => void, - }; - } - - pub fn ZcuPtr(strat: ResolveStrat) type { - return switch (strat) { - .normal => *const Zcu, - .sema => *Zcu, - }; - } - - pub fn pt(comptime strat: ResolveStrat, zcu: strat.ZcuPtr(), tid: strat.Tid()) switch (strat) { - .sema => Zcu.PerThread, - .normal => void, - } { - return switch (strat) { - .sema => .{ .tid = tid, .zcu = zcu }, - .normal => {}, - }; - } - - pub inline fn toLazy(strat: ResolveStrat) ResolveStratLazy { - return switch (strat) { - .normal => .eager, - .sema => .sema, - }; - } -}; - -/// Never returns `none`. Asserts that all necessary type resolution is already done. +/// Never returns `.none`. Asserts that the layout of `ty` is resolved. +/// +/// Unlike ABI size, a type's ABI alignment is not affected by its `Class`. In other words, any +/// alignment is possible regardless of the result of `ty.classify(zcu)`. pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment { - return (ty.abiAlignmentInner(.eager, zcu, {}) catch unreachable).scalar; -} - -pub fn abiAlignmentSema(ty: Type, pt: Zcu.PerThread) SemaError!Alignment { - return (try ty.abiAlignmentInner(.sema, pt.zcu, pt.tid)).scalar; -} - -/// If you pass `eager` you will get back `scalar` and assert the type is resolved. -/// In this case there will be no error, guaranteed. -/// If you pass `lazy` you may get back `scalar` or `val`. -/// If `val` is returned, a reference to `ty` has been captured. -/// If you pass `sema` you will get back `scalar` and resolve the type if -/// necessary, possibly returning a CompileError. -pub fn abiAlignmentInner( - ty: Type, - comptime strat: ResolveStratLazy, - zcu: strat.ZcuPtr(), - tid: strat.Tid(), -) SemaError!AbiAlignmentInner { - const pt = strat.pt(zcu, tid); - const target = zcu.getTarget(); const ip = &zcu.intern_pool; + const target = zcu.getTarget(); + assertHasLayout(ty, zcu); + return switch (ip.indexToKey(ty.toIntern())) { + .int_type => |int_type| { + if (int_type.bits == 0) return .@"1"; + return .fromByteUnits(std.zig.target.intAlignment(target, int_type.bits)); + }, + .ptr_type, .anyframe_type => ptrAbiAlignment(target), + .array_type => |array_type| Type.fromInterned(array_type.child).abiAlignment(zcu), + .vector_type => |vector_type| { + if (vector_type.len == 0) return .@"1"; + switch (zcu.comp.getZigBackend()) { + else => { + const elem_bits: u32 = @intCast(Type.fromInterned(vector_type.child).bitSize(zcu)); + if (elem_bits == 0) return .@"1"; + const bytes = ((elem_bits * vector_type.len) + 7) / 8; + return .fromByteUnits(std.math.ceilPowerOfTwoAssert(u32, bytes)); + }, + .stage2_c => return Type.fromInterned(vector_type.child).abiAlignment(zcu), + .stage2_x86_64 => { + if (vector_type.child == .bool_type) { + if (vector_type.len > 256 and target.cpu.has(.x86, .avx512f)) return .@"64"; + if (vector_type.len > 128 and target.cpu.has(.x86, .avx)) return .@"32"; + if (vector_type.len > 64) return .@"16"; + const bytes = std.math.divCeil(u32, vector_type.len, 8) catch unreachable; + return .fromByteUnits(std.math.ceilPowerOfTwoAssert(u32, bytes)); + } + const elem_bytes: u32 = @intCast(Type.fromInterned(vector_type.child).abiSize(zcu)); + if (elem_bytes == 0) return .@"1"; + const bytes = elem_bytes * vector_type.len; + if (bytes > 32 and target.cpu.has(.x86, .avx512f)) return .@"64"; + if (bytes > 16 and target.cpu.has(.x86, .avx)) return .@"32"; + return .@"16"; + }, + } + }, - switch (ty.toIntern()) { - .empty_tuple_type => return .{ .scalar = .@"1" }, - else => switch (ip.indexToKey(ty.toIntern())) { - .int_type => |int_type| { - if (int_type.bits == 0) return .{ .scalar = .@"1" }; - return .{ .scalar = .fromByteUnits(std.zig.target.intAlignment(target, int_type.bits)) }; - }, - .ptr_type, .anyframe_type => { - return .{ .scalar = ptrAbiAlignment(target) }; - }, - .array_type => |array_type| { - return Type.fromInterned(array_type.child).abiAlignmentInner(strat, zcu, tid); - }, - .vector_type => |vector_type| { - if (vector_type.len == 0) return .{ .scalar = .@"1" }; - switch (zcu.comp.getZigBackend()) { - else => { - // This is fine because the child type of a vector always has a bit-size known - // without needing any type resolution. - const elem_bits: u32 = @intCast(Type.fromInterned(vector_type.child).bitSize(zcu)); - if (elem_bits == 0) return .{ .scalar = .@"1" }; - const bytes = ((elem_bits * vector_type.len) + 7) / 8; - const alignment = std.math.ceilPowerOfTwoAssert(u32, bytes); - return .{ .scalar = Alignment.fromByteUnits(alignment) }; - }, - .stage2_c => { - return Type.fromInterned(vector_type.child).abiAlignmentInner(strat, zcu, tid); - }, - .stage2_x86_64 => { - if (vector_type.child == .bool_type) { - if (vector_type.len > 256 and target.cpu.has(.x86, .avx512f)) return .{ .scalar = .@"64" }; - if (vector_type.len > 128 and target.cpu.has(.x86, .avx)) return .{ .scalar = .@"32" }; - if (vector_type.len > 64) return .{ .scalar = .@"16" }; - const bytes = std.math.divCeil(u32, vector_type.len, 8) catch unreachable; - const alignment = std.math.ceilPowerOfTwoAssert(u32, bytes); - return .{ .scalar = Alignment.fromByteUnits(alignment) }; - } - const elem_bytes: u32 = @intCast((try Type.fromInterned(vector_type.child).abiSizeInner(strat, zcu, tid)).scalar); - if (elem_bytes == 0) return .{ .scalar = .@"1" }; - const bytes = elem_bytes * vector_type.len; - if (bytes > 32 and target.cpu.has(.x86, .avx512f)) return .{ .scalar = .@"64" }; - if (bytes > 16 and target.cpu.has(.x86, .avx)) return .{ .scalar = .@"32" }; - return .{ .scalar = .@"16" }; - }, - } - }, + .opt_type => |child| Type.fromInterned(child).abiAlignment(zcu), + .error_union_type => |eu| Alignment.maxStrict( + Type.fromInterned(eu.payload_type).abiAlignment(zcu), + errorAbiAlignment(zcu), + ), - .opt_type => return ty.abiAlignmentInnerOptional(strat, zcu, tid), - .error_union_type => |info| return ty.abiAlignmentInnerErrorUnion( - strat, - zcu, - tid, - Type.fromInterned(info.payload_type), - ), + .error_set_type, .inferred_error_set_type => errorAbiAlignment(zcu), - .error_set_type, .inferred_error_set_type => { - const bits = zcu.errorSetBits(); - if (bits == 0) return .{ .scalar = .@"1" }; - return .{ .scalar = .fromByteUnits(std.zig.target.intAlignment(target, bits)) }; - }, + .func_type => target_util.minFunctionAlignment(target), - // represents machine code; not a pointer - .func_type => return .{ .scalar = target_util.minFunctionAlignment(target) }, - - .simple_type => |t| switch (t) { - .bool, - .anyopaque, - => return .{ .scalar = .@"1" }, - - .usize, - .isize, - => return .{ .scalar = .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())) }, - - .c_char => return .{ .scalar = cTypeAlign(target, .char) }, - .c_short => return .{ .scalar = cTypeAlign(target, .short) }, - .c_ushort => return .{ .scalar = cTypeAlign(target, .ushort) }, - .c_int => return .{ .scalar = cTypeAlign(target, .int) }, - .c_uint => return .{ .scalar = cTypeAlign(target, .uint) }, - .c_long => return .{ .scalar = cTypeAlign(target, .long) }, - .c_ulong => return .{ .scalar = cTypeAlign(target, .ulong) }, - .c_longlong => return .{ .scalar = cTypeAlign(target, .longlong) }, - .c_ulonglong => return .{ .scalar = cTypeAlign(target, .ulonglong) }, - .c_longdouble => return .{ .scalar = cTypeAlign(target, .longdouble) }, - - .f16 => return .{ .scalar = .@"2" }, - .f32 => return .{ .scalar = cTypeAlign(target, .float) }, - .f64 => switch (target.cTypeBitSize(.double)) { - 64 => return .{ .scalar = cTypeAlign(target, .double) }, - else => return .{ .scalar = .@"8" }, - }, - .f80 => switch (target.cTypeBitSize(.longdouble)) { - 80 => return .{ .scalar = cTypeAlign(target, .longdouble) }, - else => return .{ .scalar = Type.u80.abiAlignment(zcu) }, - }, - .f128 => switch (target.cTypeBitSize(.longdouble)) { - 128 => return .{ .scalar = cTypeAlign(target, .longdouble) }, - else => return .{ .scalar = .@"16" }, - }, - - .anyerror, .adhoc_inferred_error_set => { - const bits = zcu.errorSetBits(); - if (bits == 0) return .{ .scalar = .@"1" }; - return .{ .scalar = .fromByteUnits(std.zig.target.intAlignment(target, bits)) }; - }, - - .void, - .type, - .comptime_int, - .comptime_float, - .null, - .undefined, - .enum_literal, - => return .{ .scalar = .@"1" }, - - .noreturn => unreachable, - .generic_poison => unreachable, - }, - .struct_type => { - const struct_type = ip.loadStructType(ty.toIntern()); - if (struct_type.layout == .@"packed") { - switch (strat) { - .sema => try ty.resolveLayout(pt), - .lazy => if (struct_type.backingIntTypeUnordered(ip) == .none) return .{ - .val = Value.fromInterned(try pt.intern(.{ .int = .{ - .ty = .comptime_int_type, - .storage = .{ .lazy_align = ty.toIntern() }, - } })), - }, - .eager => {}, - } - return .{ .scalar = Type.fromInterned(struct_type.backingIntTypeUnordered(ip)).abiAlignment(zcu) }; - } - - if (struct_type.flagsUnordered(ip).alignment == .none) switch (strat) { - .eager => unreachable, // struct alignment not resolved - .sema => try ty.resolveStructAlignment(pt), - .lazy => return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{ - .ty = .comptime_int_type, - .storage = .{ .lazy_align = ty.toIntern() }, - } })) }, - }; - - return .{ .scalar = struct_type.flagsUnordered(ip).alignment }; - }, - .tuple_type => |tuple| { - var big_align: Alignment = .@"1"; - for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| { - if (val != .none) continue; // comptime field - switch (try Type.fromInterned(field_ty).abiAlignmentInner(strat, zcu, tid)) { - .scalar => |field_align| big_align = big_align.max(field_align), - .val => switch (strat) { - .eager => unreachable, // field type alignment not resolved - .sema => unreachable, // passed to abiAlignmentInner above - .lazy => return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{ - .ty = .comptime_int_type, - .storage = .{ .lazy_align = ty.toIntern() }, - } })) }, - }, - } - } - return .{ .scalar = big_align }; + .simple_type => |t| switch (t) { + .bool, + .void, + .noreturn, + .anyopaque, + .type, + .comptime_int, + .comptime_float, + .null, + .undefined, + .enum_literal, + => .@"1", + + .anyerror, .adhoc_inferred_error_set => errorAbiAlignment(zcu), + .usize, .isize => .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())), + + .c_char => cTypeAlign(target, .char), + .c_short => cTypeAlign(target, .short), + .c_ushort => cTypeAlign(target, .ushort), + .c_int => cTypeAlign(target, .int), + .c_uint => cTypeAlign(target, .uint), + .c_long => cTypeAlign(target, .long), + .c_ulong => cTypeAlign(target, .ulong), + .c_longlong => cTypeAlign(target, .longlong), + .c_ulonglong => cTypeAlign(target, .ulonglong), + .c_longdouble => cTypeAlign(target, .longdouble), + + .f16 => .@"2", + .f32 => cTypeAlign(target, .float), + .f64 => switch (target.cTypeBitSize(.double)) { + 64 => cTypeAlign(target, .double), + else => .@"8", }, - .union_type => { - const union_type = ip.loadUnionType(ty.toIntern()); - - if (union_type.flagsUnordered(ip).alignment == .none) switch (strat) { - .eager => unreachable, // union layout not resolved - .sema => try ty.resolveUnionAlignment(pt), - .lazy => return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{ - .ty = .comptime_int_type, - .storage = .{ .lazy_align = ty.toIntern() }, - } })) }, - }; - - return .{ .scalar = union_type.flagsUnordered(ip).alignment }; + .f80 => switch (target.cTypeBitSize(.longdouble)) { + 80 => cTypeAlign(target, .longdouble), + else => Type.u80.abiAlignment(zcu), }, - .opaque_type => return .{ .scalar = .@"1" }, - .enum_type => return .{ - .scalar = Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty).abiAlignment(zcu), + .f128 => switch (target.cTypeBitSize(.longdouble)) { + 128 => cTypeAlign(target, .longdouble), + else => .@"16", }, - // values, not types - .undef, - .simple_value, - .variable, - .@"extern", - .func, - .int, - .err, - .error_union, - .enum_literal, - .enum_tag, - .empty_enum_value, - .float, - .ptr, - .slice, - .opt, - .aggregate, - .un, - // memoization, not types - .memoized_call, - => unreachable, + .generic_poison => unreachable, }, - } -} - -fn abiAlignmentInnerErrorUnion( - ty: Type, - comptime strat: ResolveStratLazy, - zcu: strat.ZcuPtr(), - tid: strat.Tid(), - payload_ty: Type, -) SemaError!AbiAlignmentInner { - // This code needs to be kept in sync with the equivalent switch prong - // in abiSizeInner. - const code_align = Type.anyerror.abiAlignment(zcu); - switch (strat) { - .eager, .sema => { - if (!(payload_ty.hasRuntimeBitsInner(false, strat, zcu, tid) catch |err| switch (err) { - error.NeedLazy => if (strat == .lazy) { - const pt = strat.pt(zcu, tid); - return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{ - .ty = .comptime_int_type, - .storage = .{ .lazy_align = ty.toIntern() }, - } })) }; - } else unreachable, - else => |e| return e, - })) { - return .{ .scalar = code_align }; - } - return .{ .scalar = code_align.max( - (try payload_ty.abiAlignmentInner(strat, zcu, tid)).scalar, - ) }; - }, - .lazy => { - const pt = strat.pt(zcu, tid); - switch (try payload_ty.abiAlignmentInner(strat, zcu, tid)) { - .scalar => |payload_align| return .{ .scalar = code_align.max(payload_align) }, - .val => {}, + .tuple_type => |tuple| { + var big_align: Alignment = .@"1"; + for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| { + if (val != .none) continue; // comptime field + const field_align = Type.fromInterned(field_ty).abiAlignment(zcu); + big_align = big_align.maxStrict(field_align); } - return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{ - .ty = .comptime_int_type, - .storage = .{ .lazy_align = ty.toIntern() }, - } })) }; + return big_align; }, - } -} - -fn abiAlignmentInnerOptional( - ty: Type, - comptime strat: ResolveStratLazy, - zcu: strat.ZcuPtr(), - tid: strat.Tid(), -) SemaError!AbiAlignmentInner { - const pt = strat.pt(zcu, tid); - const target = zcu.getTarget(); - const child_type = ty.optionalChild(zcu); - - switch (child_type.zigTypeTag(zcu)) { - .pointer => return .{ .scalar = ptrAbiAlignment(target) }, - .error_set => return Type.anyerror.abiAlignmentInner(strat, zcu, tid), - .noreturn => return .{ .scalar = .@"1" }, - else => {}, - } - - switch (strat) { - .eager, .sema => { - if (!(child_type.hasRuntimeBitsInner(false, strat, zcu, tid) catch |err| switch (err) { - error.NeedLazy => if (strat == .lazy) { - return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{ - .ty = .comptime_int_type, - .storage = .{ .lazy_align = ty.toIntern() }, - } })) }; - } else unreachable, - else => |e| return e, - })) { - return .{ .scalar = .@"1" }; + .struct_type => { + const struct_obj = ip.loadStructType(ty.toIntern()); + switch (struct_obj.layout) { + .@"packed" => return Type.fromInterned(struct_obj.packed_backing_int_type).abiAlignment(zcu), + .auto, .@"extern" => { + assert(struct_obj.alignment != .none); + return struct_obj.alignment; + }, } - return child_type.abiAlignmentInner(strat, zcu, tid); }, - .lazy => switch (try child_type.abiAlignmentInner(strat, zcu, tid)) { - .scalar => |x| return .{ .scalar = x.max(.@"1") }, - .val => return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{ - .ty = .comptime_int_type, - .storage = .{ .lazy_align = ty.toIntern() }, - } })) }, + .union_type => { + const union_obj = ip.loadUnionType(ty.toIntern()); + switch (union_obj.layout) { + .@"packed" => return Type.fromInterned(union_obj.packed_backing_int_type).abiAlignment(zcu), + .auto, .@"extern" => { + assert(union_obj.alignment != .none); + return union_obj.alignment; + }, + } }, - } -} - -const AbiSizeInner = union(enum) { - scalar: u64, - val: Value, -}; - -/// Asserts the type has the ABI size already resolved. -/// Types that return false for hasRuntimeBits() return 0. -pub fn abiSize(ty: Type, zcu: *const Zcu) u64 { - return (abiSizeInner(ty, .eager, zcu, {}) catch unreachable).scalar; -} + .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).abiAlignment(zcu), + .opaque_type => .@"1", -/// May capture a reference to `ty`. -pub fn abiSizeLazy(ty: Type, pt: Zcu.PerThread) !Value { - switch (try ty.abiSizeInner(.lazy, pt.zcu, pt.tid)) { - .val => |val| return val, - .scalar => |x| return pt.intValue(Type.comptime_int, x), - } -} - -pub fn abiSizeSema(ty: Type, pt: Zcu.PerThread) SemaError!u64 { - return (try abiSizeInner(ty, .sema, pt.zcu, pt.tid)).scalar; + // values, not types + .undef, + .simple_value, + .variable, + .@"extern", + .func, + .int, + .err, + .error_union, + .enum_literal, + .enum_tag, + .float, + .ptr, + .slice, + .opt, + .aggregate, + .un, + .bitpack, + // memoization, not types + .memoized_call, + => unreachable, + }; } -/// If you pass `eager` you will get back `scalar` and assert the type is resolved. -/// In this case there will be no error, guaranteed. -/// If you pass `lazy` you may get back `scalar` or `val`. -/// If `val` is returned, a reference to `ty` has been captured. -/// If you pass `sema` you will get back `scalar` and resolve the type if -/// necessary, possibly returning a CompileError. -pub fn abiSizeInner( - ty: Type, - comptime strat: ResolveStratLazy, - zcu: strat.ZcuPtr(), - tid: strat.Tid(), -) SemaError!AbiSizeInner { - const target = zcu.getTarget(); +/// Asserts that `ty` is not an opaque type, and that the layout of `ty` is resolved. +/// +/// If the type is NPV, OPV, or fully-comptime (see `Class`), the return value of this function is +/// guaranteed to be zero. Otherwise (if the type is runtime or partially-comptime) the return value +/// is guaranteed to be non-zero. +pub fn abiSize(ty: Type, zcu: *const Zcu) u64 { const ip = &zcu.intern_pool; - - switch (ty.toIntern()) { - .empty_tuple_type => return .{ .scalar = 0 }, - - else => switch (ip.indexToKey(ty.toIntern())) { - .int_type => |int_type| { - if (int_type.bits == 0) return .{ .scalar = 0 }; - return .{ .scalar = std.zig.target.intByteSize(target, int_type.bits) }; - }, - .ptr_type => |ptr_type| switch (ptr_type.flags.size) { - .slice => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) * 2 }, - else => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) }, - }, - .anyframe_type => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) }, - - .array_type => |array_type| { - const len = array_type.lenIncludingSentinel(); - if (len == 0) return .{ .scalar = 0 }; - switch (try Type.fromInterned(array_type.child).abiSizeInner(strat, zcu, tid)) { - .scalar => |elem_size| return .{ .scalar = len * elem_size }, - .val => switch (strat) { - .sema, .eager => unreachable, - .lazy => { - const pt = strat.pt(zcu, tid); - return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{ - .ty = .comptime_int_type, - .storage = .{ .lazy_size = ty.toIntern() }, - } })) }; - }, - }, - } - }, - .vector_type => |vector_type| { - const sub_strat: ResolveStrat = switch (strat) { - .sema => .sema, - .eager => .normal, - .lazy => { - const pt = strat.pt(zcu, tid); - return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{ - .ty = .comptime_int_type, - .storage = .{ .lazy_size = ty.toIntern() }, - } })) }; - }, - }; - const alignment = (try ty.abiAlignmentInner(strat, zcu, tid)).scalar; - const total_bytes = switch (zcu.comp.getZigBackend()) { - else => total_bytes: { - const elem_bits = try Type.fromInterned(vector_type.child).bitSizeInner(sub_strat, zcu, tid); - const total_bits = elem_bits * vector_type.len; - break :total_bytes (total_bits + 7) / 8; - }, - .stage2_c => total_bytes: { - const elem_bytes: u32 = @intCast((try Type.fromInterned(vector_type.child).abiSizeInner(strat, zcu, tid)).scalar); - break :total_bytes elem_bytes * vector_type.len; - }, - .stage2_x86_64 => total_bytes: { - if (vector_type.child == .bool_type) break :total_bytes std.math.divCeil(u32, vector_type.len, 8) catch unreachable; - const elem_bytes: u32 = @intCast((try Type.fromInterned(vector_type.child).abiSizeInner(strat, zcu, tid)).scalar); - break :total_bytes elem_bytes * vector_type.len; - }, - }; - return .{ .scalar = alignment.forward(total_bytes) }; - }, - - .opt_type => return ty.abiSizeInnerOptional(strat, zcu, tid), - - .error_set_type, .inferred_error_set_type => { - const bits = zcu.errorSetBits(); - if (bits == 0) return .{ .scalar = 0 }; - return .{ .scalar = std.zig.target.intByteSize(target, bits) }; - }, - - .error_union_type => |error_union_type| { - const payload_ty = Type.fromInterned(error_union_type.payload_type); - // This code needs to be kept in sync with the equivalent switch prong - // in abiAlignmentInner. - const code_size = Type.anyerror.abiSize(zcu); - if (!(payload_ty.hasRuntimeBitsInner(false, strat, zcu, tid) catch |err| switch (err) { - error.NeedLazy => if (strat == .lazy) { - const pt = strat.pt(zcu, tid); - return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{ - .ty = .comptime_int_type, - .storage = .{ .lazy_size = ty.toIntern() }, - } })) }; - } else unreachable, - else => |e| return e, - })) { - // Same as anyerror. - return .{ .scalar = code_size }; - } - const code_align = Type.anyerror.abiAlignment(zcu); - const payload_align = (try payload_ty.abiAlignmentInner(strat, zcu, tid)).scalar; - const payload_size = switch (try payload_ty.abiSizeInner(strat, zcu, tid)) { - .scalar => |elem_size| elem_size, - .val => switch (strat) { - .sema => unreachable, - .eager => unreachable, - .lazy => { - const pt = strat.pt(zcu, tid); - return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{ - .ty = .comptime_int_type, - .storage = .{ .lazy_size = ty.toIntern() }, - } })) }; - }, - }, - }; - - var size: u64 = 0; - if (code_align.compare(.gt, payload_align)) { - size += code_size; - size = payload_align.forward(size); - size += payload_size; - size = code_align.forward(size); - } else { - size += payload_size; - size = code_align.forward(size); - size += code_size; - size = payload_align.forward(size); - } - return .{ .scalar = size }; - }, - .func_type => unreachable, // represents machine code; not a pointer - .simple_type => |t| switch (t) { - .bool => return .{ .scalar = 1 }, - - .f16 => return .{ .scalar = 2 }, - .f32 => return .{ .scalar = 4 }, - .f64 => return .{ .scalar = 8 }, - .f128 => return .{ .scalar = 16 }, - .f80 => switch (target.cTypeBitSize(.longdouble)) { - 80 => return .{ .scalar = target.cTypeByteSize(.longdouble) }, - else => return .{ .scalar = Type.u80.abiSize(zcu) }, - }, - - .usize, - .isize, - => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) }, - - .c_char => return .{ .scalar = target.cTypeByteSize(.char) }, - .c_short => return .{ .scalar = target.cTypeByteSize(.short) }, - .c_ushort => return .{ .scalar = target.cTypeByteSize(.ushort) }, - .c_int => return .{ .scalar = target.cTypeByteSize(.int) }, - .c_uint => return .{ .scalar = target.cTypeByteSize(.uint) }, - .c_long => return .{ .scalar = target.cTypeByteSize(.long) }, - .c_ulong => return .{ .scalar = target.cTypeByteSize(.ulong) }, - .c_longlong => return .{ .scalar = target.cTypeByteSize(.longlong) }, - .c_ulonglong => return .{ .scalar = target.cTypeByteSize(.ulonglong) }, - .c_longdouble => return .{ .scalar = target.cTypeByteSize(.longdouble) }, - - .anyopaque, - .void, - .type, - .comptime_int, - .comptime_float, - .null, - .undefined, - .enum_literal, - => return .{ .scalar = 0 }, - - .anyerror, .adhoc_inferred_error_set => { - const bits = zcu.errorSetBits(); - if (bits == 0) return .{ .scalar = 0 }; - return .{ .scalar = std.zig.target.intByteSize(target, bits) }; + const target = zcu.getTarget(); + assertHasLayout(ty, zcu); + return switch (ip.indexToKey(ty.toIntern())) { + .int_type => |int_type| std.zig.target.intByteSize(target, int_type.bits), + .ptr_type => |ptr_type| switch (ptr_type.flags.size) { + .slice => ptrAbiSize(target) * 2, + .one, .many, .c => ptrAbiSize(target), + }, + .anyframe_type => ptrAbiSize(target), + .array_type => |arr| arr.lenIncludingSentinel() * Type.fromInterned(arr.child).abiSize(zcu), + .vector_type => |vec| { + const elem_ty: Type = .fromInterned(vec.child); + const bytes = switch (zcu.comp.getZigBackend()) { + else => std.math.divCeil(u64, vec.len * elem_ty.bitSize(zcu), 8) catch unreachable, + .stage2_c => vec.len * elem_ty.abiSize(zcu), + .stage2_x86_64 => switch (elem_ty.toIntern()) { + .bool_type => std.math.divCeil(u64, vec.len, 8) catch unreachable, + else => vec.len * elem_ty.abiSize(zcu), }, - - .noreturn => unreachable, - .generic_poison => unreachable, - }, - .struct_type => { - const struct_type = ip.loadStructType(ty.toIntern()); - switch (strat) { - .sema => try ty.resolveLayout(strat.pt(zcu, tid)), - .lazy => { - const pt = strat.pt(zcu, tid); - switch (struct_type.layout) { - .@"packed" => { - if (struct_type.backingIntTypeUnordered(ip) == .none) return .{ - .val = Value.fromInterned(try pt.intern(.{ .int = .{ - .ty = .comptime_int_type, - .storage = .{ .lazy_size = ty.toIntern() }, - } })), - }; - }, - .auto, .@"extern" => { - if (!struct_type.haveLayout(ip)) return .{ - .val = Value.fromInterned(try pt.intern(.{ .int = .{ - .ty = .comptime_int_type, - .storage = .{ .lazy_size = ty.toIntern() }, - } })), - }; - }, - } - }, - .eager => {}, - } - switch (struct_type.layout) { - .@"packed" => return .{ - .scalar = Type.fromInterned(struct_type.backingIntTypeUnordered(ip)).abiSize(zcu), - }, - .auto, .@"extern" => { - assert(struct_type.haveLayout(ip)); - return .{ .scalar = struct_type.sizeUnordered(ip) }; - }, - } - }, - .tuple_type => |tuple| { - switch (strat) { - .sema => try ty.resolveLayout(strat.pt(zcu, tid)), - .lazy, .eager => {}, - } - const field_count = tuple.types.len; - if (field_count == 0) { - return .{ .scalar = 0 }; - } - return .{ .scalar = ty.structFieldOffset(field_count, zcu) }; - }, - - .union_type => { - const union_type = ip.loadUnionType(ty.toIntern()); - switch (strat) { - .sema => try ty.resolveLayout(strat.pt(zcu, tid)), - .lazy => { - const pt = strat.pt(zcu, tid); - if (!union_type.flagsUnordered(ip).status.haveLayout()) return .{ - .val = Value.fromInterned(try pt.intern(.{ .int = .{ - .ty = .comptime_int_type, - .storage = .{ .lazy_size = ty.toIntern() }, - } })), - }; - }, - .eager => {}, - } - - assert(union_type.haveLayout(ip)); - return .{ .scalar = union_type.sizeUnordered(ip) }; + }; + return ty.abiAlignment(zcu).forward(bytes); + }, + .opt_type => |child_ty_ip| { + const child_ty: Type = .fromInterned(child_ty_ip); + if (child_ty.classify(zcu) == .no_possible_value) return 0; + if (ty.optionalReprIsPayload(zcu)) return child_ty.abiSize(zcu); + // Optional types are represented as a struct with the child type as the first + // field and a boolean as the second. Since the child type's abi alignment is + // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal + // to the child type's ABI alignment. + return child_ty.abiSize(zcu) + child_ty.abiAlignment(zcu).toByteUnits().?; + }, + .error_set_type, .inferred_error_set_type => errorAbiSize(zcu), + .error_union_type => |error_union| { + const payload_ty: Type = .fromInterned(error_union.payload_type); + switch (payload_ty.classify(zcu)) { + // Zig has no way to take the address of the error set "in" an error union (giving + // implementations more freedom in terms of data layout), so if the payload type is + // fully comptime, we don't need to dedicate runtime bits to the error set. + .fully_comptime => return 0, + else => {}, + } + // The layout will either be (code, payload, padding) or (payload, code, padding) + // depending on which has larger alignment. So the overall size is just the code + // and payload sizes added and padded to the larger alignment. + const big_align: Alignment = .maxStrict(errorAbiAlignment(zcu), payload_ty.abiAlignment(zcu)); + return big_align.forward(errorAbiSize(zcu) + payload_ty.abiSize(zcu)); + }, + .func_type => 0, + .simple_type => |t| switch (t) { + .void, + .noreturn, + .type, + .comptime_int, + .comptime_float, + .null, + .undefined, + .enum_literal, + => 0, + + .bool => 1, + .anyerror, .adhoc_inferred_error_set => errorAbiSize(zcu), + .usize, .isize => ptrAbiSize(target), + + .c_char => target.cTypeByteSize(.char), + .c_short => target.cTypeByteSize(.short), + .c_ushort => target.cTypeByteSize(.ushort), + .c_int => target.cTypeByteSize(.int), + .c_uint => target.cTypeByteSize(.uint), + .c_long => target.cTypeByteSize(.long), + .c_ulong => target.cTypeByteSize(.ulong), + .c_longlong => target.cTypeByteSize(.longlong), + .c_ulonglong => target.cTypeByteSize(.ulonglong), + .c_longdouble => target.cTypeByteSize(.longdouble), + + .f16 => 2, + .f32 => 4, + .f64 => 8, + .f80 => switch (target.cTypeBitSize(.longdouble)) { + 80 => target.cTypeByteSize(.longdouble), + else => Type.u80.abiSize(zcu), }, - .opaque_type => unreachable, // no size available - .enum_type => return .{ .scalar = Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty).abiSize(zcu) }, + .f128 => 16, - // values, not types - .undef, - .simple_value, - .variable, - .@"extern", - .func, - .int, - .err, - .error_union, - .enum_literal, - .enum_tag, - .empty_enum_value, - .float, - .ptr, - .slice, - .opt, - .aggregate, - .un, - // memoization, not types - .memoized_call, - => unreachable, + .anyopaque => unreachable, + .generic_poison => unreachable, }, - } -} - -fn abiSizeInnerOptional( - ty: Type, - comptime strat: ResolveStratLazy, - zcu: strat.ZcuPtr(), - tid: strat.Tid(), -) SemaError!AbiSizeInner { - const child_ty = ty.optionalChild(zcu); - - if (child_ty.isNoReturn(zcu)) { - return .{ .scalar = 0 }; - } - - if (!(child_ty.hasRuntimeBitsInner(false, strat, zcu, tid) catch |err| switch (err) { - error.NeedLazy => if (strat == .lazy) { - const pt = strat.pt(zcu, tid); - return .{ .val = Value.fromInterned(try pt.intern(.{ .int = .{ - .ty = .comptime_int_type, - .storage = .{ .lazy_size = ty.toIntern() }, - } })) }; - } else unreachable, - else => |e| return e, - })) return .{ .scalar = 1 }; - - if (ty.optionalReprIsPayload(zcu)) { - return child_ty.abiSizeInner(strat, zcu, tid); - } - - const payload_size = switch (try child_ty.abiSizeInner(strat, zcu, tid)) { - .scalar => |elem_size| elem_size, - .val => switch (strat) { - .sema => unreachable, - .eager => unreachable, - .lazy => return .{ .val = Value.fromInterned(try strat.pt(zcu, tid).intern(.{ .int = .{ - .ty = .comptime_int_type, - .storage = .{ .lazy_size = ty.toIntern() }, - } })) }, + .tuple_type => |tuple| switch (ty.classify(zcu)) { + // `structFieldOffset` is bogus on NPV tuples, because there may be some fields with + // non-zero size. + .no_possible_value => 0, + else => ty.structFieldOffset(tuple.types.len, zcu), }, - }; + .struct_type => { + const struct_obj = ip.loadStructType(ty.toIntern()); + switch (struct_obj.layout) { + .@"packed" => return Type.fromInterned(struct_obj.packed_backing_int_type).abiSize(zcu), + .auto, .@"extern" => return struct_obj.size, + } + }, + .union_type => { + const union_obj = ip.loadUnionType(ty.toIntern()); + switch (union_obj.layout) { + .@"packed" => return Type.fromInterned(union_obj.packed_backing_int_type).abiSize(zcu), + .auto, .@"extern" => return union_obj.size, + } + }, + .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).abiSize(zcu), + .opaque_type => unreachable, - // Optional types are represented as a struct with the child type as the first - // field and a boolean as the second. Since the child type's abi alignment is - // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal - // to the child type's ABI alignment. - return .{ - .scalar = (child_ty.abiAlignment(zcu).toByteUnits() orelse 0) + payload_size, + // values, not types + .undef, + .simple_value, + .variable, + .@"extern", + .func, + .int, + .err, + .error_union, + .enum_literal, + .enum_tag, + .float, + .ptr, + .slice, + .opt, + .aggregate, + .un, + .bitpack, + // memoization, not types + .memoized_call, + => unreachable, }; } pub fn ptrAbiAlignment(target: *const Target) Alignment { - return Alignment.fromNonzeroByteUnits(@divExact(target.ptrBitWidth(), 8)); + return .fromNonzeroByteUnits(@divExact(target.ptrBitWidth(), 8)); } - -pub fn bitSize(ty: Type, zcu: *const Zcu) u64 { - return bitSizeInner(ty, .normal, zcu, {}) catch unreachable; +pub fn ptrAbiSize(target: *const Target) u64 { + return @divExact(target.ptrBitWidth(), 8); } - -pub fn bitSizeSema(ty: Type, pt: Zcu.PerThread) SemaError!u64 { - return bitSizeInner(ty, .sema, pt.zcu, pt.tid); +pub fn errorAbiAlignment(zcu: *const Zcu) Alignment { + return .fromNonzeroByteUnits(std.zig.target.intAlignment(zcu.getTarget(), zcu.errorSetBits())); +} +pub fn errorAbiSize(zcu: *const Zcu) u64 { + return std.zig.target.intByteSize(zcu.getTarget(), zcu.errorSetBits()); } -pub fn bitSizeInner( - ty: Type, - comptime strat: ResolveStrat, - zcu: strat.ZcuPtr(), - tid: strat.Tid(), -) SemaError!u64 { +/// Asserts that `ty` is not an opaque or comptime-only type. +/// Once #19755 is implemented, this query will only work on types with a defined bit-level representation. +pub fn bitSize(ty: Type, zcu: *const Zcu) u64 { const target = zcu.getTarget(); const ip = &zcu.intern_pool; - - const strat_lazy: ResolveStratLazy = strat.toLazy(); - - switch (ip.indexToKey(ty.toIntern())) { - .int_type => |int_type| return int_type.bits, + assertHasLayout(ty, zcu); + return switch (ip.indexToKey(ty.toIntern())) { + .int_type => |int_type| int_type.bits, .ptr_type => |ptr_type| switch (ptr_type.flags.size) { - .slice => return target.ptrBitWidth() * 2, - else => return target.ptrBitWidth(), + .slice => target.ptrBitWidth() * 2, + else => target.ptrBitWidth(), }, - .anyframe_type => return target.ptrBitWidth(), - + .anyframe_type => target.ptrBitWidth(), .array_type => |array_type| { - const len = array_type.lenIncludingSentinel(); - if (len == 0) return 0; const elem_ty: Type = .fromInterned(array_type.child); - switch (zcu.comp.getZigBackend()) { - else => { - const elem_size = (try elem_ty.abiSizeInner(strat_lazy, zcu, tid)).scalar; - if (elem_size == 0) return 0; - const elem_bit_size = try elem_ty.bitSizeInner(strat, zcu, tid); - return (len - 1) * 8 * elem_size + elem_bit_size; - }, - .stage2_x86_64 => { - const elem_bit_size = try elem_ty.bitSizeInner(strat, zcu, tid); - return elem_bit_size * len; + const len = array_type.lenIncludingSentinel(); + return switch (zcu.comp.getZigBackend()) { + .stage2_x86_64 => len * elem_ty.bitSize(zcu), + // this case will be removed under #19755 + else => switch (len) { + 0 => 0, + else => (len - 1) * 8 * elem_ty.abiSize(zcu) + elem_ty.bitSize(zcu), }, - } - }, - .vector_type => |vector_type| { - const child_ty: Type = .fromInterned(vector_type.child); - const elem_bit_size = try child_ty.bitSizeInner(strat, zcu, tid); - return elem_bit_size * vector_type.len; - }, - .opt_type => { - // Optionals and error unions are not packed so their bitsize - // includes padding bits. - return (try ty.abiSizeInner(strat_lazy, zcu, tid)).scalar * 8; + }; }, + .vector_type => |vec| vec.len * Type.fromInterned(vec.child).bitSize(zcu), + .error_set_type, .inferred_error_set_type => zcu.errorSetBits(), + .func_type => unreachable, - .error_set_type, .inferred_error_set_type => return zcu.errorSetBits(), - - .error_union_type => { - // Optionals and error unions are not packed so their bitsize - // includes padding bits. - return (try ty.abiSizeInner(strat_lazy, zcu, tid)).scalar * 8; - }, - .func_type => unreachable, // represents machine code; not a pointer .simple_type => |t| switch (t) { - .f16 => return 16, - .f32 => return 32, - .f64 => return 64, - .f80 => return 80, - .f128 => return 128, - - .usize, - .isize, - => return target.ptrBitWidth(), - - .c_char => return target.cTypeBitSize(.char), - .c_short => return target.cTypeBitSize(.short), - .c_ushort => return target.cTypeBitSize(.ushort), - .c_int => return target.cTypeBitSize(.int), - .c_uint => return target.cTypeBitSize(.uint), - .c_long => return target.cTypeBitSize(.long), - .c_ulong => return target.cTypeBitSize(.ulong), - .c_longlong => return target.cTypeBitSize(.longlong), - .c_ulonglong => return target.cTypeBitSize(.ulonglong), - .c_longdouble => return target.cTypeBitSize(.longdouble), - - .bool => return 1, - .void => return 0, - - .anyerror, - .adhoc_inferred_error_set, - => return zcu.errorSetBits(), + .void => 0, + .bool => 1, + .anyerror, .adhoc_inferred_error_set => zcu.errorSetBits(), + .usize, .isize => target.ptrBitWidth(), + + .c_char => target.cTypeBitSize(.char), + .c_short => target.cTypeBitSize(.short), + .c_ushort => target.cTypeBitSize(.ushort), + .c_int => target.cTypeBitSize(.int), + .c_uint => target.cTypeBitSize(.uint), + .c_long => target.cTypeBitSize(.long), + .c_ulong => target.cTypeBitSize(.ulong), + .c_longlong => target.cTypeBitSize(.longlong), + .c_ulonglong => target.cTypeBitSize(.ulonglong), + .c_longdouble => target.cTypeBitSize(.longdouble), + + .f16 => 16, + .f32 => 32, + .f64 => 64, + .f80 => 80, + .f128 => 128, .anyopaque => unreachable, .type => unreachable, @@ -1717,49 +1284,30 @@ pub fn bitSizeInner( .enum_literal => unreachable, .generic_poison => unreachable, }, + .struct_type => { - const struct_type = ip.loadStructType(ty.toIntern()); - const is_packed = struct_type.layout == .@"packed"; - if (strat == .sema) { - const pt = strat.pt(zcu, tid); - try ty.resolveFields(pt); - if (is_packed) try ty.resolveLayout(pt); - } - if (is_packed) { - return try Type.fromInterned(struct_type.backingIntTypeUnordered(ip)) - .bitSizeInner(strat, zcu, tid); + const struct_obj = ip.loadStructType(ty.toIntern()); + switch (struct_obj.layout) { + .@"packed" => return Type.fromInterned(struct_obj.packed_backing_int_type).bitSize(zcu), + .auto, .@"extern" => return struct_obj.size * 8, // will be `unreachable` under #19755 } - return (try ty.abiSizeInner(strat_lazy, zcu, tid)).scalar * 8; }, - - .tuple_type => { - return (try ty.abiSizeInner(strat_lazy, zcu, tid)).scalar * 8; - }, - .union_type => { - const union_type = ip.loadUnionType(ty.toIntern()); - const is_packed = ty.containerLayout(zcu) == .@"packed"; - if (strat == .sema) { - const pt = strat.pt(zcu, tid); - try ty.resolveFields(pt); - if (is_packed) try ty.resolveLayout(pt); - } - if (!is_packed) { - return (try ty.abiSizeInner(strat_lazy, zcu, tid)).scalar * 8; + const union_obj = ip.loadUnionType(ty.toIntern()); + switch (union_obj.layout) { + .@"packed" => return Type.fromInterned(union_obj.packed_backing_int_type).bitSize(zcu), + .auto, .@"extern" => return union_obj.size * 8, // will be `unreachable` under #19755 } - assert(union_type.flagsUnordered(ip).status.haveFieldTypes()); + }, + .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type).bitSize(zcu), - var size: u64 = 0; - for (0..union_type.field_types.len) |field_index| { - const field_ty = union_type.field_types.get(ip)[field_index]; - size = @max(size, try Type.fromInterned(field_ty).bitSizeInner(strat, zcu, tid)); - } + // will be `unreachable` under #19755 + .opt_type, + .error_union_type, + .tuple_type, + => ty.abiSize(zcu) * 8, - return size; - }, .opaque_type => unreachable, - .enum_type => return Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty) - .bitSizeInner(strat, zcu, tid), // values, not types .undef, @@ -1772,33 +1320,16 @@ pub fn bitSizeInner( .error_union, .enum_literal, .enum_tag, - .empty_enum_value, .float, .ptr, - .slice, - .opt, - .aggregate, - .un, - // memoization, not types - .memoized_call, - => unreachable, - } -} - -/// Returns true if the type's layout is already resolved and it is safe -/// to use `abiSize`, `abiAlignment` and `bitSize` on it. -pub fn layoutIsResolved(ty: Type, zcu: *const Zcu) bool { - const ip = &zcu.intern_pool; - return switch (ip.indexToKey(ty.toIntern())) { - .struct_type => ip.loadStructType(ty.toIntern()).haveLayout(ip), - .union_type => ip.loadUnionType(ty.toIntern()).haveLayout(ip), - .array_type => |array_type| { - if (array_type.lenIncludingSentinel() == 0) return true; - return Type.fromInterned(array_type.child).layoutIsResolved(zcu); - }, - .opt_type => |child| Type.fromInterned(child).layoutIsResolved(zcu), - .error_union_type => |k| Type.fromInterned(k.payload_type).layoutIsResolved(zcu), - else => true, + .slice, + .opt, + .aggregate, + .un, + .bitpack, + // memoization, not types + .memoized_call, + => unreachable, }; } @@ -1841,7 +1372,7 @@ pub fn isSliceAtRuntime(ty: Type, zcu: *const Zcu) bool { } pub fn slicePtrFieldType(ty: Type, zcu: *const Zcu) Type { - return Type.fromInterned(zcu.intern_pool.slicePtrType(ty.toIntern())); + return .fromInterned(zcu.intern_pool.slicePtrType(ty.toIntern())); } pub fn isConstPtr(ty: Type, zcu: *const Zcu) bool { @@ -1897,10 +1428,7 @@ pub fn isPtrAtRuntime(ty: Type, zcu: *const Zcu) bool { /// For pointer-like optionals, returns true, otherwise returns the allowzero property /// of pointers. pub fn ptrAllowsZero(ty: Type, zcu: *const Zcu) bool { - if (ty.isPtrLikeOptional(zcu)) { - return true; - } - return ty.ptrInfo(zcu).flags.is_allowzero; + return ty.isPtrLikeOptional(zcu) or ty.ptrInfo(zcu).flags.is_allowzero; } /// See also `isPtrLikeOptional`. @@ -1918,7 +1446,6 @@ pub fn optionalReprIsPayload(ty: Type, zcu: *const Zcu) bool { /// Returns true if the type is optional and would be lowered to a single pointer /// address value, using 0 for null. Note that this returns true for C pointers. -/// This function must be kept in sync with `Sema.typePtrOrOptionalPtrTy`. pub fn isPtrLikeOptional(ty: Type, zcu: *const Zcu) bool { return switch (zcu.intern_pool.indexToKey(ty.toIntern())) { .ptr_type => |ptr_type| ptr_type.flags.size == .c, @@ -1947,52 +1474,54 @@ pub fn childTypeIp(ty: Type, ip: *const InternPool) Type { return Type.fromInterned(ip.childType(ty.toIntern())); } -/// For `*[N]T`, returns `T`. -/// For `?*T`, returns `T`. -/// For `?*[N]T`, returns `T`. -/// For `?[*]T`, returns `T`. -/// For `*T`, returns `T`. -/// For `[*]T`, returns `T`. -/// For `[N]T`, returns `T`. -/// For `[]T`, returns `T`. -/// For `anyframe->T`, returns `T`. -pub fn elemType2(ty: Type, zcu: *const Zcu) Type { - return switch (zcu.intern_pool.indexToKey(ty.toIntern())) { - .ptr_type => |ptr_type| switch (ptr_type.flags.size) { - .one => Type.fromInterned(ptr_type.child).shallowElemType(zcu), - .many, .c, .slice => Type.fromInterned(ptr_type.child), - }, - .anyframe_type => |child| { - assert(child != .none); - return Type.fromInterned(child); +/// Similar to `childType`, but for pointer-like (or slice-like) optionals, gets the child type +/// of the *pointer* type. Asserts that `ty` is either a pointer or a pointer-like optional. +/// +/// Essentially, unwraps any one of the following into `T`: +/// ``` +/// *T ?*T *allowzero T +/// [*]T ?[*]T [*]allowzero T +/// []T ?[]T []allowzero T +/// [*c]T +/// ``` +/// This is primarily useful in Sema to implement operations which can act on optional pointers. +pub fn nullablePtrElem(ty: Type, zcu: *const Zcu) Type { + switch (ty.zigTypeTag(zcu)) { + .pointer => return ty.childType(zcu), + .optional => { + const ptr_ty = ty.childType(zcu); + const ptr_info = zcu.intern_pool.indexToKey(ptr_ty.toIntern()).ptr_type; + assert(ptr_info.flags.size != .c); + assert(!ptr_info.flags.is_allowzero); + return .fromInterned(ptr_info.child); }, - .vector_type => |vector_type| Type.fromInterned(vector_type.child), - .array_type => |array_type| Type.fromInterned(array_type.child), - .opt_type => |child| Type.fromInterned(zcu.intern_pool.childType(child)), else => unreachable, - }; -} - -/// Given that `ty` is an indexable pointer, returns its element type. Specifically: -/// * for `*[n]T`, returns `T` -/// * for `[]T`, returns `T` -/// * for `[*]T`, returns `T` -/// * for `[*c]T`, returns `T` -pub fn indexablePtrElem(ty: Type, zcu: *const Zcu) Type { - const ip = &zcu.intern_pool; - const ptr_type = ip.indexToKey(ty.toIntern()).ptr_type; - switch (ptr_type.flags.size) { - .many, .slice, .c => return .fromInterned(ptr_type.child), - .one => {}, } - const array_type = ip.indexToKey(ptr_type.child).array_type; - return .fromInterned(array_type.child); } -fn shallowElemType(child_ty: Type, zcu: *const Zcu) Type { - return switch (child_ty.zigTypeTag(zcu)) { - .array, .vector => child_ty.childType(zcu), - else => child_ty, +/// Asserts that `ty` is an indexable type, and returns its element type. Tuples (and pointers to +/// tuples) are not supported because they do not have a single element type. +/// +/// Returns `T` for each of the following types: +/// * `[n]T` +/// * `@Vector(n, T)` +/// * `*[n]T` +/// * `*@Vector(n, T)` +/// * `[]T` +/// * `[*]T` +/// * `[*c]T` +pub fn indexableElem(ty: Type, zcu: *const Zcu) Type { + const ip = &zcu.intern_pool; + return switch (ip.indexToKey(ty.toIntern())) { + inline .array_type, .vector_type => |arr| .fromInterned(arr.child), + .ptr_type => |ptr_type| switch (ptr_type.flags.size) { + .many, .slice, .c => .fromInterned(ptr_type.child), + .one => switch (ip.indexToKey(ptr_type.child)) { + inline .array_type, .vector_type => |arr| .fromInterned(arr.child), + else => unreachable, + }, + }, + else => unreachable, }; } @@ -2004,61 +1533,54 @@ pub fn scalarType(ty: Type, zcu: *const Zcu) Type { }; } -/// Asserts that the type is an optional. -/// Note that for C pointers this returns the type unmodified. +/// Asserts that the type is an optional, or a C pointer. +/// For C pointers this returns the type unmodified. pub fn optionalChild(ty: Type, zcu: *const Zcu) Type { - return switch (zcu.intern_pool.indexToKey(ty.toIntern())) { - .opt_type => |child| Type.fromInterned(child), - .ptr_type => |ptr_type| b: { + switch (zcu.intern_pool.indexToKey(ty.toIntern())) { + .opt_type => |child| return .fromInterned(child), + .ptr_type => |ptr_type| { assert(ptr_type.flags.size == .c); - break :b ty; + return ty; }, else => unreachable, - }; + } } -/// Returns the tag type of a union, if the type is a union and it has a tag type. -/// Otherwise, returns `null`. +/// If `ty` is a tagged union, returns its tag type. Otherwise, returns `null`. pub fn unionTagType(ty: Type, zcu: *const Zcu) ?Type { + assertHasLayout(ty, zcu); const ip = &zcu.intern_pool; switch (ip.indexToKey(ty.toIntern())) { .union_type => {}, else => return null, } - const union_type = ip.loadUnionType(ty.toIntern()); - const union_flags = union_type.flagsUnordered(ip); - switch (union_flags.runtime_tag) { - .tagged => { - assert(union_flags.status.haveFieldTypes()); - return Type.fromInterned(union_type.enum_tag_ty); - }, - else => return null, - } + const union_obj = ip.loadUnionType(ty.toIntern()); + return switch (union_obj.tag_usage) { + .tagged => .fromInterned(union_obj.enum_tag_type), + .none, .safety => null, + }; } -/// Same as `unionTagType` but includes safety tag. -/// Codegen should use this version. -pub fn unionTagTypeSafety(ty: Type, zcu: *const Zcu) ?Type { - const ip = &zcu.intern_pool; - return switch (ip.indexToKey(ty.toIntern())) { - .union_type => { - const union_type = ip.loadUnionType(ty.toIntern()); - if (!union_type.hasTag(ip)) return null; - assert(union_type.haveFieldTypes(ip)); - return Type.fromInterned(union_type.enum_tag_ty); - }, - else => null, - }; +/// If the given union type contains a tag (including a safety tag) in its runtime layout, returns +/// its enum tag type. Otherwise, returns null. Asserts that `ty` is a union type. +/// +/// In general, codegen logic should call this function instead of `unionTagType`. +pub fn unionTagTypeRuntime(ty: Type, zcu: *const Zcu) ?Type { + assertHasLayout(ty, zcu); + const union_type = zcu.intern_pool.loadUnionType(ty.toIntern()); + if (!union_type.has_runtime_tag) return null; + return .fromInterned(union_type.enum_tag_type); } -/// Asserts the type is a union; returns the tag type, even if the tag will -/// not be stored at runtime. +/// Asserts that `ty` is a union type, and returns its tag type, even if the tag will not be stored at runtime. pub fn unionTagTypeHypothetical(ty: Type, zcu: *const Zcu) Type { - const union_obj = zcu.typeToUnion(ty).?; - return Type.fromInterned(union_obj.enum_tag_ty); + assertHasLayout(ty, zcu); + const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern()); + return .fromInterned(union_obj.enum_tag_type); } pub fn unionFieldType(ty: Type, enum_tag: Value, zcu: *const Zcu) ?Type { + assertHasLayout(ty, zcu); const ip = &zcu.intern_pool; const union_obj = zcu.typeToUnion(ty).?; const union_fields = union_obj.field_types.get(ip); @@ -2067,17 +1589,20 @@ pub fn unionFieldType(ty: Type, enum_tag: Value, zcu: *const Zcu) ?Type { } pub fn unionFieldTypeByIndex(ty: Type, index: usize, zcu: *const Zcu) Type { + assertHasLayout(ty, zcu); const ip = &zcu.intern_pool; const union_obj = zcu.typeToUnion(ty).?; return Type.fromInterned(union_obj.field_types.get(ip)[index]); } pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, zcu: *const Zcu) ?u32 { + assertHasLayout(ty, zcu); const union_obj = zcu.typeToUnion(ty).?; return zcu.unionTagFieldIndex(union_obj, enum_tag); } pub fn unionHasAllZeroBitFieldTypes(ty: Type, zcu: *Zcu) bool { + assertHasLayout(ty, zcu); const ip = &zcu.intern_pool; const union_obj = zcu.typeToUnion(ty).?; for (union_obj.field_types.get(ip)) |field_ty| { @@ -2087,17 +1612,21 @@ pub fn unionHasAllZeroBitFieldTypes(ty: Type, zcu: *Zcu) bool { } /// Returns the type used for backing storage of this union during comptime operations. -/// Asserts the type is either an extern or packed union. -pub fn unionBackingType(ty: Type, pt: Zcu.PerThread) !Type { +/// Asserts the type is an extern union. +pub fn externUnionBackingType(ty: Type, pt: Zcu.PerThread) !Type { const zcu = pt.zcu; - return switch (ty.containerLayout(zcu)) { - .@"extern" => try pt.arrayType(.{ .len = ty.abiSize(zcu), .child = .u8_type }), - .@"packed" => try pt.intType(.unsigned, @intCast(ty.bitSize(zcu))), + assertHasLayout(ty, zcu); + const loaded_union = zcu.intern_pool.loadUnionType(ty.toIntern()); + switch (loaded_union.layout) { + .@"extern" => return pt.arrayType(.{ .len = ty.abiSize(zcu), .child = .u8_type }), + .@"packed" => unreachable, .auto => unreachable, - }; + } } +/// Asserts that `ty` is a non-packed union type. pub fn unionGetLayout(ty: Type, zcu: *const Zcu) Zcu.UnionLayout { + assertHasLayout(ty, zcu); const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern()); return Type.getUnionLayout(union_obj, zcu); } @@ -2105,9 +1634,18 @@ pub fn unionGetLayout(ty: Type, zcu: *const Zcu) Zcu.UnionLayout { pub fn containerLayout(ty: Type, zcu: *const Zcu) std.builtin.Type.ContainerLayout { const ip = &zcu.intern_pool; return switch (ip.indexToKey(ty.toIntern())) { - .struct_type => ip.loadStructType(ty.toIntern()).layout, .tuple_type => .auto, - .union_type => ip.loadUnionType(ty.toIntern()).flagsUnordered(ip).layout, + .struct_type => ip.loadStructType(ty.toIntern()).layout, + .union_type => ip.loadUnionType(ty.toIntern()).layout, + else => unreachable, + }; +} + +pub fn bitpackBackingInt(ty: Type, zcu: *const Zcu) Type { + const ip = &zcu.intern_pool; + return switch (ip.indexToKey(ty.toIntern())) { + .struct_type => .fromInterned(ip.loadStructType(ty.toIntern()).packed_backing_int_type), + .union_type => .fromInterned(ip.loadUnionType(ty.toIntern()).packed_backing_int_type), else => unreachable, }; } @@ -2123,6 +1661,11 @@ pub fn errorUnionSet(ty: Type, zcu: *const Zcu) Type { } /// Returns false for unresolved inferred error sets. +/// +/// TODO: this function will behave incorrectly under incremental compilation, because in that case +/// it may see an outdated resolved error set. This function must be either deleted, or its contract +/// changed to require the caller to resolve the error set beforehand. If you must introduce new +/// call sites, please make sure the error set in question is definitely resolved first! pub fn errorSetIsEmpty(ty: Type, zcu: *const Zcu) bool { const ip = &zcu.intern_pool; return switch (ty.toIntern()) { @@ -2141,6 +1684,11 @@ pub fn errorSetIsEmpty(ty: Type, zcu: *const Zcu) bool { /// Returns true if it is an error set that includes anyerror, false otherwise. /// Note that the result may be a false negative if the type did not get error set /// resolution prior to this call. +/// +/// TODO: this function will behave incorrectly under incremental compilation, because in that case +/// it may see an outdated resolved error set. This function must be either deleted, or its contract +/// changed to require the caller to resolve the error set beforehand. If you must introduce new +/// call sites, please make sure the error set in question is definitely resolved first! pub fn isAnyError(ty: Type, zcu: *const Zcu) bool { const ip = &zcu.intern_pool; return switch (ty.toIntern()) { @@ -2163,46 +1711,25 @@ pub fn isError(ty: Type, zcu: *const Zcu) bool { /// Returns whether ty, which must be an error set, includes an error `name`. /// Might return a false negative if `ty` is an inferred error set and not fully /// resolved yet. -pub fn errorSetHasFieldIp( - ip: *const InternPool, - ty: InternPool.Index, +/// +/// TODO: this function will behave incorrectly under incremental compilation, because in that case +/// it may see an outdated resolved error set. This function must be either deleted, or its contract +/// changed to require the caller to resolve the error set beforehand. If you must introduce new +/// call sites, please make sure the error set in question is definitely resolved first! +pub fn errorSetHasField( + ty: Type, name: InternPool.NullTerminatedString, + zcu: *const Zcu, ) bool { - return switch (ty) { - .anyerror_type => true, - else => switch (ip.indexToKey(ty)) { - .error_set_type => |error_set_type| error_set_type.nameIndex(ip, name) != null, - .inferred_error_set_type => |i| switch (ip.funcIesResolvedUnordered(i)) { - .anyerror_type => true, - .none => false, - else => |t| ip.indexToKey(t).error_set_type.nameIndex(ip, name) != null, - }, - else => unreachable, - }, - }; -} - -/// Returns whether ty, which must be an error set, includes an error `name`. -/// Might return a false negative if `ty` is an inferred error set and not fully -/// resolved yet. -pub fn errorSetHasField(ty: Type, name: []const u8, zcu: *const Zcu) bool { const ip = &zcu.intern_pool; return switch (ty.toIntern()) { .anyerror_type => true, else => switch (ip.indexToKey(ty.toIntern())) { - .error_set_type => |error_set_type| { - // If the string is not interned, then the field certainly is not present. - const field_name_interned = ip.getString(name).unwrap() orelse return false; - return error_set_type.nameIndex(ip, field_name_interned) != null; - }, + .error_set_type => |error_set_type| error_set_type.nameIndex(ip, name) != null, .inferred_error_set_type => |i| switch (ip.funcIesResolvedUnordered(i)) { .anyerror_type => true, .none => false, - else => |t| { - // If the string is not interned, then the field certainly is not present. - const field_name_interned = ip.getString(name).unwrap() orelse return false; - return ip.indexToKey(t).error_set_type.nameIndex(ip, field_name_interned) != null; - }, + else => |t| ip.indexToKey(t).error_set_type.nameIndex(ip, name) != null, }, else => unreachable, }, @@ -2275,12 +1802,12 @@ pub fn isUnsignedInt(ty: Type, zcu: *const Zcu) bool { }; } -/// Returns true for integers, enums, error sets, and packed structs. +/// Returns true for integers, enums, error sets, and packed structs/unions. /// If this function returns true, then intInfo() can be called on the type. pub fn isAbiInt(ty: Type, zcu: *const Zcu) bool { return switch (ty.zigTypeTag(zcu)) { .int, .@"enum", .error_set => true, - .@"struct" => ty.containerLayout(zcu) == .@"packed", + .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed", else => false, }; } @@ -2308,8 +1835,17 @@ pub fn intInfo(starting_ty: Type, zcu: *const Zcu) InternPool.Key.IntType { .c_ulonglong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulonglong) }, else => switch (ip.indexToKey(ty.toIntern())) { .int_type => |int_type| return int_type, - .struct_type => ty = Type.fromInterned(ip.loadStructType(ty.toIntern()).backingIntTypeUnordered(ip)), - .enum_type => ty = Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty), + .struct_type => { + const struct_obj = ip.loadStructType(ty.toIntern()); + assert(struct_obj.layout == .@"packed"); + ty = .fromInterned(struct_obj.packed_backing_int_type); + }, + .union_type => { + const union_obj = ip.loadUnionType(ty.toIntern()); + assert(union_obj.layout == .@"packed"); + ty = .fromInterned(union_obj.packed_backing_int_type); + }, + .enum_type => ty = .fromInterned(ip.loadEnumType(ty.toIntern()).int_tag_type), .vector_type => |vector_type| ty = Type.fromInterned(vector_type.child), .error_set_type, .inferred_error_set_type => { @@ -2327,7 +1863,6 @@ pub fn intInfo(starting_ty: Type, zcu: *const Zcu) InternPool.Key.IntType { .func_type => unreachable, .simple_type => unreachable, // handled via Index enum tag above - .union_type => unreachable, .opaque_type => unreachable, // values, not types @@ -2341,13 +1876,13 @@ pub fn intInfo(starting_ty: Type, zcu: *const Zcu) InternPool.Key.IntType { .error_union, .enum_literal, .enum_tag, - .empty_enum_value, .float, .ptr, .slice, .opt, .aggregate, .un, + .bitpack, // memoization, not types .memoized_call, => unreachable, @@ -2355,25 +1890,6 @@ pub fn intInfo(starting_ty: Type, zcu: *const Zcu) InternPool.Key.IntType { }; } -pub fn isNamedInt(ty: Type) bool { - return switch (ty.toIntern()) { - .usize_type, - .isize_type, - .c_char_type, - .c_short_type, - .c_ushort_type, - .c_int_type, - .c_uint_type, - .c_long_type, - .c_ulong_type, - .c_longlong_type, - .c_ulonglong_type, - => true, - - else => false, - }; -} - /// Returns `false` for `comptime_float`. pub fn isRuntimeFloat(ty: Type) bool { return switch (ty.toIntern()) { @@ -2488,429 +2004,181 @@ pub fn isNumeric(ty: Type, zcu: *const Zcu) bool { }; } -/// During semantic analysis, instead call `Sema.typeHasOnePossibleValue` which -/// resolves field types rather than asserting they are already resolved. -pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value { +/// If the type's classification is `Class.one_possible_value` (see `classify`), returns the only +/// possible value for the type. Otherwise, returns `null`. +pub fn onePossibleValue(ty: Type, pt: Zcu.PerThread) !?Value { const zcu = pt.zcu; const comp = zcu.comp; const gpa = comp.gpa; - const io = comp.io; const ip = &zcu.intern_pool; - var ty = starting_type; - while (true) switch (ty.toIntern()) { - .empty_tuple_type => return Value.empty_tuple, - - else => switch (ip.indexToKey(ty.toIntern())) { - .int_type => |int_type| { - if (int_type.bits == 0) { - return try pt.intValue(ty, 0); - } else { - return null; - } - }, - - .ptr_type, - .error_union_type, - .func_type, - .anyframe_type, - .error_set_type, - .inferred_error_set_type, - => return null, - - inline .array_type, .vector_type => |seq_type, seq_tag| { - const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none; - if (seq_type.len + @intFromBool(has_sentinel) == 0) { - return try pt.aggregateValue(ty, &.{}); - } - if (try Type.fromInterned(seq_type.child).onePossibleValue(pt)) |opv| { - return try pt.aggregateSplatValue(ty, opv); - } - return null; - }, - .opt_type => |child| { - if (child == .noreturn_type) { - return try pt.nullValue(ty); - } else { - return null; - } - }, - - .simple_type => |t| switch (t) { - .f16, - .f32, - .f64, - .f80, - .f128, - .usize, - .isize, - .c_char, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .c_longdouble, - .anyopaque, - .bool, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .enum_literal, - .adhoc_inferred_error_set, - => return null, - - .void => return Value.void, - .noreturn => return Value.@"unreachable", - .null => return Value.null, - .undefined => return Value.undef, - - .generic_poison => unreachable, - }, - .struct_type => { - const struct_type = ip.loadStructType(ty.toIntern()); - assert(struct_type.haveFieldTypes(ip)); - if (struct_type.knownNonOpv(ip)) - return null; - const field_vals = try zcu.gpa.alloc(InternPool.Index, struct_type.field_types.len); - defer zcu.gpa.free(field_vals); - for (field_vals, 0..) |*field_val, i_usize| { - const i: u32 = @intCast(i_usize); - if (struct_type.fieldIsComptime(ip, i)) { - assert(struct_type.haveFieldInits(ip)); - field_val.* = struct_type.field_inits.get(ip)[i]; - continue; - } - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); - if (try field_ty.onePossibleValue(pt)) |field_opv| { - field_val.* = field_opv.toIntern(); - } else return null; - } - - // In this case the struct has no runtime-known fields and - // therefore has one possible value. - return try pt.aggregateValue(ty, field_vals); - }, - - .tuple_type => |tuple| { - if (tuple.types.len == 0) { - return try pt.aggregateValue(ty, &.{}); - } - - const field_vals = try zcu.gpa.alloc( - InternPool.Index, - tuple.types.len, - ); - defer zcu.gpa.free(field_vals); - for ( - field_vals, - tuple.types.get(ip), - tuple.values.get(ip), - ) |*field_val, field_ty, field_comptime_val| { - if (field_comptime_val != .none) { - field_val.* = field_comptime_val; - continue; - } - if (try Type.fromInterned(field_ty).onePossibleValue(pt)) |opv| { - field_val.* = opv.toIntern(); - } else return null; - } - - return try pt.aggregateValue(ty, field_vals); - }, - - .union_type => { - const union_obj = ip.loadUnionType(ty.toIntern()); - const tag_val = (try Type.fromInterned(union_obj.enum_tag_ty).onePossibleValue(pt)) orelse - return null; - if (union_obj.field_types.len == 0) { - const only = try pt.intern(.{ .empty_enum_value = ty.toIntern() }); - return Value.fromInterned(only); - } - const only_field_ty = union_obj.field_types.get(ip)[0]; - const val_val = (try Type.fromInterned(only_field_ty).onePossibleValue(pt)) orelse - return null; - const only = try pt.internUnion(.{ - .ty = ty.toIntern(), - .tag = tag_val.toIntern(), - .val = val_val.toIntern(), - }); - return Value.fromInterned(only); - }, - .opaque_type => return null, - .enum_type => { - const enum_type = ip.loadEnumType(ty.toIntern()); - switch (enum_type.tag_mode) { - .nonexhaustive => { - if (enum_type.tag_ty == .comptime_int_type) return null; - - if (try Type.fromInterned(enum_type.tag_ty).onePossibleValue(pt)) |int_opv| { - const only = try pt.intern(.{ .enum_tag = .{ - .ty = ty.toIntern(), - .int = int_opv.toIntern(), - } }); - return Value.fromInterned(only); - } - - return null; - }, - .auto, .explicit => { - if (Type.fromInterned(enum_type.tag_ty).hasRuntimeBits(zcu)) return null; - - return Value.fromInterned(switch (enum_type.names.len) { - 0 => try pt.intern(.{ .empty_enum_value = ty.toIntern() }), - 1 => try pt.intern(.{ .enum_tag = .{ - .ty = ty.toIntern(), - .int = if (enum_type.values.len == 0) - (try pt.intValue(.fromInterned(enum_type.tag_ty), 0)).toIntern() - else - try ip.getCoercedInts( - gpa, - io, - pt.tid, - ip.indexToKey(enum_type.values.get(ip)[0]).int, - enum_type.tag_ty, - ), - } }), - else => return null, - }); - }, - } - }, + assertHasLayout(ty, zcu); + return switch (ip.indexToKey(ty.toIntern())) { + .ptr_type, + .error_union_type, + .func_type, + .anyframe_type, + .error_set_type, + .inferred_error_set_type, + .opaque_type, + => null, - // values, not types - .undef, - .simple_value, - .variable, - .@"extern", - .func, - .int, - .err, - .error_union, + .simple_type => |t| switch (t) { + .f16, + .f32, + .f64, + .f80, + .f128, + .usize, + .isize, + .c_char, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .c_longdouble, + .anyopaque, + .bool, + .type, + .anyerror, + .comptime_int, + .comptime_float, .enum_literal, - .enum_tag, - .empty_enum_value, - .float, - .ptr, - .slice, - .opt, - .aggregate, - .un, - // memoization, not types - .memoized_call, - => unreachable, - }, - }; -} - -/// During semantic analysis, instead call `ty.comptimeOnlySema` which -/// resolves field types rather than asserting they are already resolved. -pub fn comptimeOnly(ty: Type, zcu: *const Zcu) bool { - return ty.comptimeOnlyInner(.normal, zcu, {}) catch unreachable; -} - -pub fn comptimeOnlySema(ty: Type, pt: Zcu.PerThread) SemaError!bool { - return try ty.comptimeOnlyInner(.sema, pt.zcu, pt.tid); -} - -/// `generic_poison` will return false. -/// May return false negatives when structs and unions are having their field types resolved. -pub fn comptimeOnlyInner( - ty: Type, - comptime strat: ResolveStrat, - zcu: strat.ZcuPtr(), - tid: strat.Tid(), -) SemaError!bool { - const ip = &zcu.intern_pool; - const io = zcu.comp.io; - return switch (ty.toIntern()) { - .empty_tuple_type => false, + .adhoc_inferred_error_set, + .null, + .undefined, + .noreturn, + => null, - else => switch (ip.indexToKey(ty.toIntern())) { - .int_type => false, - .ptr_type => |ptr_type| { - const child_ty = Type.fromInterned(ptr_type.child); - switch (child_ty.zigTypeTag(zcu)) { - .@"fn" => return !try child_ty.fnHasRuntimeBitsInner(strat, zcu, tid), - .@"opaque" => return false, - else => return child_ty.comptimeOnlyInner(strat, zcu, tid), - } - }, - .anyframe_type => |child| { - if (child == .none) return false; - return Type.fromInterned(child).comptimeOnlyInner(strat, zcu, tid); - }, - .array_type => |array_type| return Type.fromInterned(array_type.child).comptimeOnlyInner(strat, zcu, tid), - .vector_type => |vector_type| return Type.fromInterned(vector_type.child).comptimeOnlyInner(strat, zcu, tid), - .opt_type => |child| return Type.fromInterned(child).comptimeOnlyInner(strat, zcu, tid), - .error_union_type => |error_union_type| return Type.fromInterned(error_union_type.payload_type).comptimeOnlyInner(strat, zcu, tid), + .void => .void, - .error_set_type, - .inferred_error_set_type, - => false, + .generic_poison => unreachable, + }, - // These are function bodies, not function pointers. - .func_type => true, - - .simple_type => |t| switch (t) { - .f16, - .f32, - .f64, - .f80, - .f128, - .usize, - .isize, - .c_char, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .c_longdouble, - .anyopaque, - .bool, - .void, - .anyerror, - .adhoc_inferred_error_set, - .noreturn, - .generic_poison, - => false, - - .type, - .comptime_int, - .comptime_float, - .null, - .undefined, - .enum_literal, - => true, - }, - .struct_type => { - const struct_type = ip.loadStructType(ty.toIntern()); - // packed structs cannot be comptime-only because they have a well-defined - // memory layout and every field has a well-defined bit pattern. - if (struct_type.layout == .@"packed") - return false; - - return switch (strat) { - .normal => switch (struct_type.requiresComptime(ip)) { - .wip => unreachable, - .no => false, - .yes => true, - .unknown => unreachable, - }, - .sema => switch (struct_type.setRequiresComptimeWip(ip, io)) { - .no, .wip => false, - .yes => true, - .unknown => { - if (struct_type.flagsUnordered(ip).field_types_wip) { - struct_type.setRequiresComptime(ip, io, .unknown); - return false; - } - - errdefer struct_type.setRequiresComptime(ip, io, .unknown); - - const pt = strat.pt(zcu, tid); - try ty.resolveFields(pt); - - for (0..struct_type.field_types.len) |i_usize| { - const i: u32 = @intCast(i_usize); - if (struct_type.fieldIsComptime(ip, i)) continue; - const field_ty = struct_type.field_types.get(ip)[i]; - if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) { - // Note that this does not cause the layout to - // be considered resolved. Comptime-only types - // still maintain a layout of their - // runtime-known fields. - struct_type.setRequiresComptime(ip, io, .yes); - return true; - } - } - - struct_type.setRequiresComptime(ip, io, .no); - return false; - }, - }, - }; - }, + .int_type => |int_type| switch (int_type.bits) { + 0 => try pt.intValue(ty, 0), + else => null, + }, - .tuple_type => |tuple| { - for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| { - const have_comptime_val = val != .none; - if (!have_comptime_val and try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) return true; + inline .array_type, .vector_type => |seq_type, seq_tag| { + const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none; + if (seq_type.len + @intFromBool(has_sentinel) == 0) { + return try pt.aggregateValue(ty, &.{}); + } + if (try Type.fromInterned(seq_type.child).onePossibleValue(pt)) |opv| { + return try pt.aggregateSplatValue(ty, opv); + } + return null; + }, + .opt_type => |child| switch (Type.fromInterned(child).classify(zcu)) { + .no_possible_value => try pt.nullValue(ty), + else => null, + }, + .tuple_type => |tuple| { + // Check *whether* the OPV exists first, because constructing it is a little more expensive. + if (ty.classify(zcu) != .one_possible_value) return null; + const field_vals = try zcu.gpa.dupe(InternPool.Index, tuple.values.get(ip)); + defer zcu.gpa.free(field_vals); + for (field_vals, tuple.types.get(ip)) |*field_val, field_ty_ip| { + if (field_val.* != .none) continue; // comptime field value + const field_ty: Type = .fromInterned(field_ty_ip); + field_val.* = (try field_ty.onePossibleValue(pt)).?.toIntern(); + } + return try pt.aggregateValue(ty, field_vals); + }, + .struct_type => { + const struct_obj = ip.loadStructType(ty.toIntern()); + switch (struct_obj.layout) { + .auto, .@"extern" => {}, + .@"packed" => { + const backing_ty: Type = .fromInterned(struct_obj.packed_backing_int_type); + const backing_val = try backing_ty.onePossibleValue(pt) orelse return null; + return try pt.bitpackValue(ty, backing_val); + }, + } + // Type resolution already figured out whether there is an OPV, but if there is, it's + // our job to compute it. + if (struct_obj.class != .one_possible_value) return null; + const field_vals = try gpa.alloc(InternPool.Index, struct_obj.field_types.len); + defer gpa.free(field_vals); + for (field_vals, 0..) |*field_val, i_usize| { + const i: u32 = @intCast(i_usize); + if (struct_obj.field_is_comptime_bits.get(ip, i)) { + field_val.* = struct_obj.field_defaults.get(ip)[i]; + assert(field_val.* != .none); + continue; } - return false; - }, - - .union_type => { - const union_type = ip.loadUnionType(ty.toIntern()); - return switch (strat) { - .normal => switch (union_type.requiresComptime(ip)) { - .wip => unreachable, - .no => false, - .yes => true, - .unknown => unreachable, - }, - .sema => switch (union_type.setRequiresComptimeWip(ip, io)) { - .no, .wip => return false, - .yes => return true, - .unknown => { - if (union_type.flagsUnordered(ip).status == .field_types_wip) { - union_type.setRequiresComptime(ip, io, .unknown); - return false; - } - - errdefer union_type.setRequiresComptime(ip, io, .unknown); - - const pt = strat.pt(zcu, tid); - try ty.resolveFields(pt); - - for (0..union_type.field_types.len) |field_idx| { - const field_ty = union_type.field_types.get(ip)[field_idx]; - if (try Type.fromInterned(field_ty).comptimeOnlyInner(strat, zcu, tid)) { - union_type.setRequiresComptime(ip, io, .yes); - return true; - } - } - - union_type.setRequiresComptime(ip, io, .no); - return false; - }, - }, - }; - }, - - .opaque_type => false, - - .enum_type => return Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty).comptimeOnlyInner(strat, zcu, tid), - - // values, not types - .undef, - .simple_value, - .variable, - .@"extern", - .func, - .int, - .err, - .error_union, - .enum_literal, - .enum_tag, - .empty_enum_value, - .float, - .ptr, - .slice, - .opt, - .aggregate, - .un, - // memoization, not types - .memoized_call, - => unreachable, + const field_ty: Type = .fromInterned(struct_obj.field_types.get(ip)[i]); + field_val.* = (try field_ty.onePossibleValue(pt)).?.toIntern(); + } + return try pt.aggregateValue(ty, field_vals); }, + .union_type => { + const union_obj = ip.loadUnionType(ty.toIntern()); + if (union_obj.layout == .@"packed") { + const backing_ty: Type = .fromInterned(union_obj.packed_backing_int_type); + const backing_val = try backing_ty.onePossibleValue(pt) orelse return null; + return try pt.bitpackValue(ty, backing_val); + } + // Type resolution already figured out whether there is an OPV, but if there is, it's + // our job to compute it. + if (union_obj.class != .one_possible_value) return null; + // The OPV comes from exactly one field whose type is OPV, while all others are NPV. + for (union_obj.field_types.get(ip), 0..) |field_ty_ip, field_index| { + const field_ty: Type = .fromInterned(field_ty_ip); + switch (field_ty.classify(zcu)) { + .no_possible_value => continue, + .one_possible_value => {}, + else => unreachable, + } + // This field is the one! + const enum_tag_ty: Type = .fromInterned(union_obj.enum_tag_type); + const tag_val = try pt.enumValueFieldIndex(enum_tag_ty, @intCast(field_index)); + const payload_val = (try field_ty.onePossibleValue(pt)).?; + return try pt.unionValue(ty, tag_val, payload_val); + } else unreachable; + }, + .enum_type => if (try ty.intTagType(zcu).onePossibleValue(pt)) |int_tag_opv| { + return .fromInterned(try pt.intern(.{ .enum_tag = .{ + .ty = ty.toIntern(), + .int = int_tag_opv.toIntern(), + } })); + } else null, + + // values, not types + .undef, + .simple_value, + .variable, + .@"extern", + .func, + .int, + .err, + .error_union, + .enum_literal, + .enum_tag, + .float, + .ptr, + .slice, + .opt, + .aggregate, + .un, + .bitpack, + // memoization, not types + .memoized_call, + => unreachable, + }; +} + +/// Asserts that `ty` has its layout resolved. `generic_poison` will return `false`. +pub fn comptimeOnly(ty: Type, zcu: *const Zcu) bool { + if (ty.toIntern() == .generic_poison_type) return false; + if (ty.zigTypeTag(zcu) == .error_union and ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type) return false; + return switch (ty.classify(zcu)) { + .no_possible_value, .one_possible_value, .runtime => false, + .partially_comptime, .fully_comptime => true, }; } @@ -3056,20 +2324,18 @@ pub fn maxIntScalar(ty: Type, pt: Zcu.PerThread, dest_ty: Type) !Value { /// Asserts the type is an enum or a union. pub fn intTagType(ty: Type, zcu: *const Zcu) Type { const ip = &zcu.intern_pool; - return switch (ip.indexToKey(ty.toIntern())) { - .union_type => Type.fromInterned(ip.loadUnionType(ty.toIntern()).enum_tag_ty).intTagType(zcu), - .enum_type => Type.fromInterned(ip.loadEnumType(ty.toIntern()).tag_ty), + const enum_ty: Type = switch (ip.indexToKey(ty.toIntern())) { + .union_type => .fromInterned(ip.loadUnionType(ty.toIntern()).enum_tag_type), + .enum_type => ty, else => unreachable, }; + return .fromInterned(ip.loadEnumType(enum_ty.toIntern()).int_tag_type); } pub fn isNonexhaustiveEnum(ty: Type, zcu: *const Zcu) bool { const ip = &zcu.intern_pool; return switch (ip.indexToKey(ty.toIntern())) { - .enum_type => switch (ip.loadEnumType(ty.toIntern()).tag_mode) { - .nonexhaustive => true, - .auto, .explicit => false, - }, + .enum_type => ip.loadEnumType(ty.toIntern()).nonexhaustive, else => false, }; } @@ -3090,28 +2356,33 @@ pub fn errorSetNames(ty: Type, zcu: *const Zcu) InternPool.NullTerminatedString. } pub fn enumFields(ty: Type, zcu: *const Zcu) InternPool.NullTerminatedString.Slice { - return zcu.intern_pool.loadEnumType(ty.toIntern()).names; + assertHasLayout(ty, zcu); + return zcu.intern_pool.loadEnumType(ty.toIntern()).field_names; } pub fn enumFieldCount(ty: Type, zcu: *const Zcu) usize { - return zcu.intern_pool.loadEnumType(ty.toIntern()).names.len; + assertHasLayout(ty, zcu); + return zcu.intern_pool.loadEnumType(ty.toIntern()).field_names.len; } pub fn enumFieldName(ty: Type, field_index: usize, zcu: *const Zcu) InternPool.NullTerminatedString { + assertHasLayout(ty, zcu); const ip = &zcu.intern_pool; - return ip.loadEnumType(ty.toIntern()).names.get(ip)[field_index]; + return ip.loadEnumType(ty.toIntern()).field_names.get(ip)[field_index]; } pub fn enumFieldIndex(ty: Type, field_name: InternPool.NullTerminatedString, zcu: *const Zcu) ?u32 { + assertHasLayout(ty, zcu); const ip = &zcu.intern_pool; const enum_type = ip.loadEnumType(ty.toIntern()); return enum_type.nameIndex(ip, field_name); } -/// Asserts `ty` is an enum. `enum_tag` can either be `enum_field_index` or -/// an integer which represents the enum value. Returns the field index in +/// Asserts `ty` is an enum. `enum_tag` can either be the actual enum tag value +/// or an integer which represents the enum value. Returns the field index in /// declaration order, or `null` if `enum_tag` does not match any field. pub fn enumTagFieldIndex(ty: Type, enum_tag: Value, zcu: *const Zcu) ?u32 { + assertHasLayout(ty, zcu); const ip = &zcu.intern_pool; const enum_type = ip.loadEnumType(ty.toIntern()); const int_tag = switch (ip.indexToKey(enum_tag.toIntern())) { @@ -3119,200 +2390,116 @@ pub fn enumTagFieldIndex(ty: Type, enum_tag: Value, zcu: *const Zcu) ?u32 { .enum_tag => |info| info.int, else => unreachable, }; - assert(ip.typeOf(int_tag) == enum_type.tag_ty); + assert(ip.typeOf(int_tag) == enum_type.int_tag_type); return enum_type.tagValueIndex(ip, int_tag); } /// Returns none in the case of a tuple which uses the integer index as the field name. pub fn structFieldName(ty: Type, index: usize, zcu: *const Zcu) InternPool.OptionalNullTerminatedString { const ip = &zcu.intern_pool; - return switch (ip.indexToKey(ty.toIntern())) { - .struct_type => ip.loadStructType(ty.toIntern()).fieldName(ip, index).toOptional(), - .tuple_type => .none, + switch (ip.indexToKey(ty.toIntern())) { + .struct_type => { + assertHasLayout(ty, zcu); + return ip.loadStructType(ty.toIntern()).field_names.get(ip)[index].toOptional(); + }, + .tuple_type => return .none, else => unreachable, - }; + } } pub fn structFieldCount(ty: Type, zcu: *const Zcu) u32 { const ip = &zcu.intern_pool; - return switch (ip.indexToKey(ty.toIntern())) { - .struct_type => ip.loadStructType(ty.toIntern()).field_types.len, - .tuple_type => |tuple| tuple.types.len, + switch (ip.indexToKey(ty.toIntern())) { + .struct_type => { + assertHasLayout(ty, zcu); + return ip.loadStructType(ty.toIntern()).field_types.len; + }, + .tuple_type => |tuple| return tuple.types.len, else => unreachable, - }; + } } -/// Returns the field type. Supports structs and unions. +/// Returns the field type. Supports tuples, structs, and unions. pub fn fieldType(ty: Type, index: usize, zcu: *const Zcu) Type { const ip = &zcu.intern_pool; - return switch (ip.indexToKey(ty.toIntern())) { - .struct_type => Type.fromInterned(ip.loadStructType(ty.toIntern()).field_types.get(ip)[index]), - .union_type => { - const union_obj = ip.loadUnionType(ty.toIntern()); - return Type.fromInterned(union_obj.field_types.get(ip)[index]); + const types = switch (ip.indexToKey(ty.toIntern())) { + .struct_type => types: { + assertHasLayout(ty, zcu); + break :types ip.loadStructType(ty.toIntern()).field_types; }, - .tuple_type => |tuple| Type.fromInterned(tuple.types.get(ip)[index]), + .union_type => types: { + assertHasLayout(ty, zcu); + break :types ip.loadUnionType(ty.toIntern()).field_types; + }, + .tuple_type => |tuple| tuple.types, else => unreachable, }; + return .fromInterned(types.get(ip)[index]); } -pub fn fieldAlignment(ty: Type, index: usize, zcu: *Zcu) Alignment { - return ty.fieldAlignmentInner(index, .normal, zcu, {}) catch unreachable; -} - -pub fn fieldAlignmentSema(ty: Type, index: usize, pt: Zcu.PerThread) SemaError!Alignment { - return try ty.fieldAlignmentInner(index, .sema, pt.zcu, pt.tid); -} - -/// Returns the field alignment. Supports structs and unions. -/// If `strat` is `.sema`, may perform type resolution. -/// Asserts the layout is not packed. +/// If an alignment was explicitly specified for the given field of the struct or union type `ty`, +/// returns that. Otherwise, returns `.none`. This function also supports tuples, for which it +/// always returns `.none`. /// -/// Provide the struct field as the `ty`. -pub fn fieldAlignmentInner( - ty: Type, - index: usize, - comptime strat: ResolveStrat, - zcu: strat.ZcuPtr(), - tid: strat.Tid(), -) SemaError!Alignment { +/// Asserts that the layout of `ty` is resolved, unless `ty` is a tuple. +pub fn explicitFieldAlignment(ty: Type, index: usize, zcu: *const Zcu) Alignment { const ip = &zcu.intern_pool; - switch (ip.indexToKey(ty.toIntern())) { + return switch (ip.indexToKey(ty.toIntern())) { + .tuple_type => .none, .struct_type => { - const struct_type = ip.loadStructType(ty.toIntern()); - assert(struct_type.layout != .@"packed"); - const explicit_align = struct_type.fieldAlign(ip, index); - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[index]); - return field_ty.structFieldAlignmentInner(explicit_align, struct_type.layout, strat, zcu, tid); - }, - .tuple_type => |tuple| { - return (try Type.fromInterned(tuple.types.get(ip)[index]).abiAlignmentInner( - strat.toLazy(), - zcu, - tid, - )).scalar; + assertHasLayout(ty, zcu); + const struct_obj = ip.loadStructType(ty.toIntern()); + assert(struct_obj.layout != .@"packed"); + if (struct_obj.field_aligns.len == 0) return .none; + return struct_obj.field_aligns.get(ip)[index]; }, .union_type => { + assertHasLayout(ty, zcu); const union_obj = ip.loadUnionType(ty.toIntern()); - const layout = union_obj.flagsUnordered(ip).layout; - assert(layout != .@"packed"); - const explicit_align = union_obj.fieldAlign(ip, index); - const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[index]); - return field_ty.unionFieldAlignmentInner(explicit_align, layout, strat, zcu, tid); + assert(union_obj.layout != .@"packed"); + if (union_obj.field_aligns.len == 0) return .none; + return union_obj.field_aligns.get(ip)[index]; }, else => unreachable, - } + }; } -/// Returns the alignment of a non-packed struct field. Assert the layout is not packed. +/// Returns the alignment a struct field of type `field_ty` will be given if no alignment is +/// explicitly specified. However, in an `extern struct`, a higher alignment may be available due +/// to the struct's full layout (i.e. a field might coincidentally be more aligned). /// -/// Asserts that all resolution needed was done. -pub fn structFieldAlignment( +/// Asserts that the layout of `field_ty` is resolved. Asserts that `layout` is not `.@"packed"`. +pub fn defaultStructFieldAlignment( field_ty: Type, - explicit_alignment: InternPool.Alignment, layout: std.builtin.Type.ContainerLayout, - zcu: *Zcu, + zcu: *const Zcu, ) Alignment { - return field_ty.structFieldAlignmentInner( - explicit_alignment, - layout, - .normal, - zcu, - {}, - ) catch unreachable; -} - -/// Returns the alignment of a non-packed struct field. Assert the layout is not packed. -/// May do type resolution when needed. -/// Asserts that all resolution needed was done. -pub fn structFieldAlignmentSema( - field_ty: Type, - explicit_alignment: InternPool.Alignment, - layout: std.builtin.Type.ContainerLayout, - pt: Zcu.PerThread, -) SemaError!Alignment { - return try field_ty.structFieldAlignmentInner( - explicit_alignment, - layout, - .sema, - pt.zcu, - pt.tid, - ); -} - -/// Returns the alignment of a non-packed struct field. Asserts the layout is not packed. -/// If `strat` is `.sema`, may perform type resolution. -pub fn structFieldAlignmentInner( - field_ty: Type, - explicit_alignment: Alignment, - layout: std.builtin.Type.ContainerLayout, - comptime strat: Type.ResolveStrat, - zcu: strat.ZcuPtr(), - tid: strat.Tid(), -) SemaError!Alignment { - assert(layout != .@"packed"); - if (explicit_alignment != .none) return explicit_alignment; - const ty_abi_align = (try field_ty.abiAlignmentInner( - strat.toLazy(), - zcu, - tid, - )).scalar; - switch (layout) { + const overalign_big_int = switch (layout) { .@"packed" => unreachable, - .auto => if (zcu.getTarget().ofmt != .c) return ty_abi_align, - .@"extern" => {}, - } - // extern - if (field_ty.isAbiInt(zcu) and field_ty.intInfo(zcu).bits >= 128) { - return ty_abi_align.maxStrict(.@"16"); + .auto => zcu.getTarget().ofmt == .c, + .@"extern" => true, + }; + const abi_align = field_ty.abiAlignment(zcu); + assert(abi_align != .none); + if (overalign_big_int and field_ty.isAbiInt(zcu) and field_ty.intInfo(zcu).bits >= 128) { + return abi_align.maxStrict(.@"16"); } - return ty_abi_align; -} - -pub fn unionFieldAlignmentSema( - field_ty: Type, - explicit_alignment: Alignment, - layout: std.builtin.Type.ContainerLayout, - pt: Zcu.PerThread, -) SemaError!Alignment { - return field_ty.unionFieldAlignmentInner( - explicit_alignment, - layout, - .sema, - pt.zcu, - pt.tid, - ); -} - -pub fn unionFieldAlignmentInner( - field_ty: Type, - explicit_alignment: Alignment, - layout: std.builtin.Type.ContainerLayout, - comptime strat: Type.ResolveStrat, - zcu: strat.ZcuPtr(), - tid: strat.Tid(), -) SemaError!Alignment { - assert(layout != .@"packed"); - if (explicit_alignment != .none) return explicit_alignment; - if (field_ty.isNoReturn(zcu)) return .none; - return (try field_ty.abiAlignmentInner(strat.toLazy(), zcu, tid)).scalar; + return abi_align; } -pub fn structFieldDefaultValue(ty: Type, index: usize, zcu: *const Zcu) Value { +pub fn structFieldDefaultValue(ty: Type, index: usize, zcu: *const Zcu) ?Value { const ip = &zcu.intern_pool; switch (ip.indexToKey(ty.toIntern())) { .struct_type => { - const struct_type = ip.loadStructType(ty.toIntern()); - const val = struct_type.fieldInit(ip, index); - // TODO: avoid using `unreachable` to indicate this. - if (val == .none) return Value.@"unreachable"; - return Value.fromInterned(val); + const field_defaults = ip.loadStructType(ty.toIntern()).field_defaults.get(ip); + if (field_defaults.len == 0) return null; + if (field_defaults[index] == .none) return null; + return .fromInterned(field_defaults[index]); }, .tuple_type => |tuple| { const val = tuple.values.get(ip)[index]; - // TODO: avoid using `unreachable` to indicate this. - if (val == .none) return Value.@"unreachable"; - return Value.fromInterned(val); + if (val == .none) return null; + return .fromInterned(val); }, else => unreachable, } @@ -3324,9 +2511,8 @@ pub fn structFieldValueComptime(ty: Type, pt: Zcu.PerThread, index: usize) !?Val switch (ip.indexToKey(ty.toIntern())) { .struct_type => { const struct_type = ip.loadStructType(ty.toIntern()); - if (struct_type.fieldIsComptime(ip, index)) { - assert(struct_type.haveFieldInits(ip)); - return Value.fromInterned(struct_type.field_inits.get(ip)[index]); + if (struct_type.field_is_comptime_bits.get(ip, index)) { + return .fromInterned(struct_type.field_defaults.get(ip)[index]); } else { return Type.fromInterned(struct_type.field_types.get(ip)[index]).onePossibleValue(pt); } @@ -3336,7 +2522,7 @@ pub fn structFieldValueComptime(ty: Type, pt: Zcu.PerThread, index: usize) !?Val if (val == .none) { return Type.fromInterned(tuple.types.get(ip)[index]).onePossibleValue(pt); } else { - return Value.fromInterned(val); + return .fromInterned(val); } }, else => unreachable, @@ -3345,11 +2531,14 @@ pub fn structFieldValueComptime(ty: Type, pt: Zcu.PerThread, index: usize) !?Val pub fn structFieldIsComptime(ty: Type, index: usize, zcu: *const Zcu) bool { const ip = &zcu.intern_pool; - return switch (ip.indexToKey(ty.toIntern())) { - .struct_type => ip.loadStructType(ty.toIntern()).fieldIsComptime(ip, index), - .tuple_type => |tuple| tuple.values.get(ip)[index] != .none, + switch (ip.indexToKey(ty.toIntern())) { + .struct_type => { + assertHasLayout(ty, zcu); + return ip.loadStructType(ty.toIntern()).field_is_comptime_bits.get(ip, index); + }, + .tuple_type => |tuple| return tuple.values.get(ip)[index] != .none, else => unreachable, - }; + } } pub const FieldOffset = struct { @@ -3357,15 +2546,15 @@ pub const FieldOffset = struct { offset: u64, }; -/// Supports structs and unions. +/// Supports structs, tuples, and unions. pub fn structFieldOffset(ty: Type, index: usize, zcu: *const Zcu) u64 { + assertHasLayout(ty, zcu); const ip = &zcu.intern_pool; switch (ip.indexToKey(ty.toIntern())) { .struct_type => { const struct_type = ip.loadStructType(ty.toIntern()); - assert(struct_type.haveLayout(ip)); assert(struct_type.layout != .@"packed"); - return struct_type.offsets.get(ip)[index]; + return struct_type.field_offsets.get(ip)[index]; }, .tuple_type => |tuple| { @@ -3375,7 +2564,7 @@ pub fn structFieldOffset(ty: Type, index: usize, zcu: *const Zcu) u64 { for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, i| { if (field_val != .none or !Type.fromInterned(field_ty).hasRuntimeBits(zcu)) { // comptime field - if (i == index) return offset; + if (i == index) return 0; continue; } @@ -3391,8 +2580,7 @@ pub fn structFieldOffset(ty: Type, index: usize, zcu: *const Zcu) u64 { .union_type => { const union_type = ip.loadUnionType(ty.toIntern()); - if (!union_type.hasTag(ip)) - return 0; + if (!union_type.has_runtime_tag) return 0; const layout = Type.getUnionLayout(union_type, zcu); if (layout.tag_align.compare(.gte, layout.payload_align)) { // {Tag, Payload} @@ -3414,7 +2602,7 @@ pub fn srcLocOrNull(ty: Type, zcu: *Zcu) ?Zcu.LazySrcLoc { .struct_type, .union_type, .opaque_type, .enum_type => |info| switch (info) { .declared => |d| d.zir_index, .reified => |r| r.zir_index, - .generated_tag => |gt| ip.loadUnionType(gt.union_type).zir_index, + .generated_union_tag => |union_ty| ip.loadUnionType(union_ty).zir_index, }, else => return null, }, @@ -3438,8 +2626,8 @@ pub fn isTuple(ty: Type, zcu: *const Zcu) bool { }; } -/// Traverses optional child types and error union payloads until the type -/// is not a pointer. For `E!?u32`, returns `u32`; for `*u8`, returns `*u8`. +/// Traverses optional child types and error union payloads until the type is neither of those. +/// For `E!?u32`, returns `u32`; for `*u8`, returns `*u8`. pub fn optEuBaseType(ty: Type, zcu: *const Zcu) Type { var cur = ty; while (true) switch (cur.zigTypeTag(zcu)) { @@ -3485,439 +2673,81 @@ pub fn typeDeclInstAllowGeneratedTag(ty: Type, zcu: *const Zcu) ?InternPool.Trac const ip = &zcu.intern_pool; return switch (ip.indexToKey(ty.toIntern())) { .struct_type => ip.loadStructType(ty.toIntern()).zir_index, - .union_type => ip.loadUnionType(ty.toIntern()).zir_index, - .enum_type => |e| switch (e) { - .declared, .reified => ip.loadEnumType(ty.toIntern()).zir_index.unwrap().?, - .generated_tag => |gt| ip.loadUnionType(gt.union_type).zir_index, - }, - .opaque_type => ip.loadOpaqueType(ty.toIntern()).zir_index, - else => null, - }; -} - -pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 { - // Note that changes to ZIR instruction tracking only need to update this code - // if a newly-tracked instruction can be a type's owner `zir_index`. - comptime assert(Zir.inst_tracking_version == 0); - - const ip = &zcu.intern_pool; - const tracked = switch (ip.indexToKey(ty.toIntern())) { - .struct_type, .union_type, .opaque_type, .enum_type => |info| switch (info) { - .declared => |d| d.zir_index, - .reified => |r| r.zir_index, - .generated_tag => |gt| ip.loadUnionType(gt.union_type).zir_index, - }, - else => return null, - }; - const info = tracked.resolveFull(&zcu.intern_pool) orelse return null; - const file = zcu.fileByIndex(info.file); - const zir = switch (file.getMode()) { - .zig => file.zir.?, - .zon => return 0, - }; - const inst = zir.instructions.get(@intFromEnum(info.inst)); - return switch (inst.tag) { - .struct_init, .struct_init_ref => zir.extraData(Zir.Inst.StructInit, inst.data.pl_node.payload_index).data.abs_line, - .struct_init_anon => zir.extraData(Zir.Inst.StructInitAnon, inst.data.pl_node.payload_index).data.abs_line, - .extended => switch (inst.data.extended.opcode) { - .struct_decl => zir.extraData(Zir.Inst.StructDecl, inst.data.extended.operand).data.src_line, - .union_decl => zir.extraData(Zir.Inst.UnionDecl, inst.data.extended.operand).data.src_line, - .enum_decl => zir.extraData(Zir.Inst.EnumDecl, inst.data.extended.operand).data.src_line, - .opaque_decl => zir.extraData(Zir.Inst.OpaqueDecl, inst.data.extended.operand).data.src_line, - .reify_enum => zir.extraData(Zir.Inst.ReifyEnum, inst.data.extended.operand).data.src_line, - .reify_struct => zir.extraData(Zir.Inst.ReifyStruct, inst.data.extended.operand).data.src_line, - .reify_union => zir.extraData(Zir.Inst.ReifyUnion, inst.data.extended.operand).data.src_line, - else => unreachable, - }, - else => unreachable, - }; -} - -/// Given a namespace type, returns its list of captured values. -pub fn getCaptures(ty: Type, zcu: *const Zcu) InternPool.CaptureValue.Slice { - const ip = &zcu.intern_pool; - return switch (ip.indexToKey(ty.toIntern())) { - .struct_type => ip.loadStructType(ty.toIntern()).captures, - .union_type => ip.loadUnionType(ty.toIntern()).captures, - .enum_type => ip.loadEnumType(ty.toIntern()).captures, - .opaque_type => ip.loadOpaqueType(ty.toIntern()).captures, - else => unreachable, - }; -} - -pub fn arrayBase(ty: Type, zcu: *const Zcu) struct { Type, u64 } { - var cur_ty: Type = ty; - var cur_len: u64 = 1; - while (cur_ty.zigTypeTag(zcu) == .array) { - cur_len *= cur_ty.arrayLenIncludingSentinel(zcu); - cur_ty = cur_ty.childType(zcu); - } - return .{ cur_ty, cur_len }; -} - -/// Returns a bit-pointer with the same value and a new packed offset. -pub fn packedStructFieldPtrInfo( - struct_ty: Type, - parent_ptr_ty: Type, - field_idx: u32, - pt: Zcu.PerThread, -) InternPool.Key.PtrType.PackedOffset { - comptime assert(Type.packed_struct_layout_version == 2); - - const zcu = pt.zcu; - const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu); - - var bit_offset: u16 = 0; - var running_bits: u16 = 0; - for (0..struct_ty.structFieldCount(zcu)) |i| { - const f_ty = struct_ty.fieldType(i, zcu); - if (i == field_idx) { - bit_offset = running_bits; - } - running_bits += @intCast(f_ty.bitSize(zcu)); - } - - const res_host_size: u16, const res_bit_offset: u16 = if (parent_ptr_info.packed_offset.host_size != 0) .{ - parent_ptr_info.packed_offset.host_size, - parent_ptr_info.packed_offset.bit_offset + bit_offset, - } else .{ - switch (zcu.comp.getZigBackend()) { - else => (running_bits + 7) / 8, - .stage2_x86_64, .stage2_c => @intCast(struct_ty.abiSize(zcu)), - }, - bit_offset, - }; - - return .{ - .host_size = res_host_size, - .bit_offset = res_bit_offset, - }; -} - -pub fn resolveLayout(ty: Type, pt: Zcu.PerThread) SemaError!void { - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - switch (ty.zigTypeTag(zcu)) { - .@"struct" => switch (ip.indexToKey(ty.toIntern())) { - .tuple_type => |tuple_type| for (0..tuple_type.types.len) |i| { - const field_ty = Type.fromInterned(tuple_type.types.get(ip)[i]); - try field_ty.resolveLayout(pt); - }, - .struct_type => return ty.resolveStructInner(pt, .layout), - else => unreachable, - }, - .@"union" => return ty.resolveUnionInner(pt, .layout), - .array => { - if (ty.arrayLenIncludingSentinel(zcu) == 0) return; - const elem_ty = ty.childType(zcu); - return elem_ty.resolveLayout(pt); - }, - .optional => { - const payload_ty = ty.optionalChild(zcu); - return payload_ty.resolveLayout(pt); - }, - .error_union => { - const payload_ty = ty.errorUnionPayload(zcu); - return payload_ty.resolveLayout(pt); - }, - .@"fn" => { - const info = zcu.typeToFunc(ty).?; - if (info.is_generic) { - // Resolving of generic function types is deferred to when - // the function is instantiated. - return; - } - for (0..info.param_types.len) |i| { - const param_ty = info.param_types.get(ip)[i]; - try Type.fromInterned(param_ty).resolveLayout(pt); - } - try Type.fromInterned(info.return_type).resolveLayout(pt); - }, - else => {}, - } -} - -pub fn resolveFields(ty: Type, pt: Zcu.PerThread) SemaError!void { - const ip = &pt.zcu.intern_pool; - const ty_ip = ty.toIntern(); - - switch (ty_ip) { - .none => unreachable, - - .u0_type, - .i0_type, - .u1_type, - .u8_type, - .i8_type, - .u16_type, - .i16_type, - .u29_type, - .u32_type, - .i32_type, - .u64_type, - .i64_type, - .u80_type, - .u128_type, - .i128_type, - .usize_type, - .isize_type, - .c_char_type, - .c_short_type, - .c_ushort_type, - .c_int_type, - .c_uint_type, - .c_long_type, - .c_ulong_type, - .c_longlong_type, - .c_ulonglong_type, - .c_longdouble_type, - .f16_type, - .f32_type, - .f64_type, - .f80_type, - .f128_type, - .anyopaque_type, - .bool_type, - .void_type, - .type_type, - .anyerror_type, - .adhoc_inferred_error_set_type, - .comptime_int_type, - .comptime_float_type, - .noreturn_type, - .anyframe_type, - .null_type, - .undefined_type, - .enum_literal_type, - .ptr_usize_type, - .ptr_const_comptime_int_type, - .manyptr_u8_type, - .manyptr_const_u8_type, - .manyptr_const_u8_sentinel_0_type, - .slice_const_u8_type, - .slice_const_u8_sentinel_0_type, - .optional_noreturn_type, - .anyerror_void_error_union_type, - .generic_poison_type, - .empty_tuple_type, - => {}, - - .undef => unreachable, - .zero => unreachable, - .zero_usize => unreachable, - .zero_u1 => unreachable, - .zero_u8 => unreachable, - .one => unreachable, - .one_usize => unreachable, - .one_u1 => unreachable, - .one_u8 => unreachable, - .four_u8 => unreachable, - .negative_one => unreachable, - .void_value => unreachable, - .unreachable_value => unreachable, - .null_value => unreachable, - .bool_true => unreachable, - .bool_false => unreachable, - .empty_tuple => unreachable, - - else => switch (ty_ip.unwrap(ip).getTag(ip)) { - .type_struct, - .type_struct_packed, - .type_struct_packed_inits, - => return ty.resolveStructInner(pt, .fields), - - .type_union => return ty.resolveUnionInner(pt, .fields), - - else => {}, - }, - } -} - -pub fn resolveFully(ty: Type, pt: Zcu.PerThread) SemaError!void { - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - - switch (ty.zigTypeTag(zcu)) { - .type, - .void, - .bool, - .noreturn, - .int, - .float, - .comptime_float, - .comptime_int, - .undefined, - .null, - .error_set, - .@"enum", - .@"opaque", - .frame, - .@"anyframe", - .vector, - .enum_literal, - => {}, - - .pointer => return ty.childType(zcu).resolveFully(pt), - .array => return ty.childType(zcu).resolveFully(pt), - .optional => return ty.optionalChild(zcu).resolveFully(pt), - .error_union => return ty.errorUnionPayload(zcu).resolveFully(pt), - .@"fn" => { - const info = zcu.typeToFunc(ty).?; - if (info.is_generic) return; - for (0..info.param_types.len) |i| { - const param_ty = info.param_types.get(ip)[i]; - try Type.fromInterned(param_ty).resolveFully(pt); - } - try Type.fromInterned(info.return_type).resolveFully(pt); - }, - - .@"struct" => switch (ip.indexToKey(ty.toIntern())) { - .tuple_type => |tuple_type| for (0..tuple_type.types.len) |i| { - const field_ty = Type.fromInterned(tuple_type.types.get(ip)[i]); - try field_ty.resolveFully(pt); - }, - .struct_type => return ty.resolveStructInner(pt, .full), - else => unreachable, - }, - .@"union" => return ty.resolveUnionInner(pt, .full), - } -} - -pub fn resolveStructFieldInits(ty: Type, pt: Zcu.PerThread) SemaError!void { - // TODO: stop calling this for tuples! - _ = pt.zcu.typeToStruct(ty) orelse return; - return ty.resolveStructInner(pt, .inits); -} - -pub fn resolveStructAlignment(ty: Type, pt: Zcu.PerThread) SemaError!void { - return ty.resolveStructInner(pt, .alignment); -} - -pub fn resolveUnionAlignment(ty: Type, pt: Zcu.PerThread) SemaError!void { - return ty.resolveUnionInner(pt, .alignment); -} - -/// `ty` must be a struct. -fn resolveStructInner( - ty: Type, - pt: Zcu.PerThread, - resolution: enum { fields, inits, alignment, layout, full }, -) SemaError!void { - const zcu = pt.zcu; - const gpa = zcu.gpa; - - const struct_obj = zcu.typeToStruct(ty).?; - const owner: InternPool.AnalUnit = .wrap(.{ .type = ty.toIntern() }); - - if (zcu.failed_analysis.contains(owner) or zcu.transitive_failed_analysis.contains(owner)) { - return error.AnalysisFail; - } - - if (zcu.comp.debugIncremental()) { - const info = try zcu.incremental_debug_state.getUnitInfo(gpa, owner); - info.last_update_gen = zcu.generation; - } - - var analysis_arena = std.heap.ArenaAllocator.init(gpa); - defer analysis_arena.deinit(); + .union_type => ip.loadUnionType(ty.toIntern()).zir_index, + .enum_type => |e| switch (e) { + .declared, .reified => ip.loadEnumType(ty.toIntern()).zir_index.unwrap().?, + .generated_union_tag => |union_ty| ip.loadUnionType(union_ty).zir_index, + }, + .opaque_type => ip.loadOpaqueType(ty.toIntern()).zir_index, + else => null, + }; +} - var comptime_err_ret_trace = std.array_list.Managed(Zcu.LazySrcLoc).init(gpa); - defer comptime_err_ret_trace.deinit(); +pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 { + // Note that changes to ZIR instruction tracking only need to update this code + // if a newly-tracked instruction can be a type's owner `zir_index`. + comptime assert(Zir.inst_tracking_version == 0); - const zir = zcu.namespacePtr(struct_obj.namespace).fileScope(zcu).zir.?; - var sema: Sema = .{ - .pt = pt, - .gpa = gpa, - .arena = analysis_arena.allocator(), - .code = zir, - .owner = owner, - .func_index = .none, - .func_is_naked = false, - .fn_ret_ty = Type.void, - .fn_ret_ty_ies = null, - .comptime_err_ret_trace = &comptime_err_ret_trace, + const ip = &zcu.intern_pool; + const tracked = switch (ip.indexToKey(ty.toIntern())) { + .struct_type, .union_type, .opaque_type, .enum_type => |info| switch (info) { + .declared => |d| d.zir_index, + .reified => |r| r.zir_index, + .generated_union_tag => |union_ty| ip.loadUnionType(union_ty).zir_index, + }, + else => return null, }; - defer sema.deinit(); - - (switch (resolution) { - .fields => sema.resolveStructFieldTypes(ty.toIntern(), struct_obj), - .inits => sema.resolveStructFieldInits(ty), - .alignment => sema.resolveStructAlignment(ty.toIntern(), struct_obj), - .layout => sema.resolveStructLayout(ty), - .full => sema.resolveStructFully(ty), - }) catch |err| switch (err) { - error.AnalysisFail => { - if (!zcu.failed_analysis.contains(owner)) { - try zcu.transitive_failed_analysis.put(gpa, owner, {}); - } - return error.AnalysisFail; + const info = tracked.resolveFull(&zcu.intern_pool) orelse return null; + const file = zcu.fileByIndex(info.file); + const zir = switch (file.getMode()) { + .zig => file.zir.?, + .zon => return 0, + }; + const inst = zir.instructions.get(@intFromEnum(info.inst)); + return switch (inst.tag) { + .struct_init, .struct_init_ref => zir.extraData(Zir.Inst.StructInit, inst.data.pl_node.payload_index).data.abs_line, + .struct_init_anon => zir.extraData(Zir.Inst.StructInitAnon, inst.data.pl_node.payload_index).data.abs_line, + .extended => switch (inst.data.extended.opcode) { + .struct_decl => zir.getStructDecl(info.inst).src_line, + .union_decl => zir.getUnionDecl(info.inst).src_line, + .enum_decl => zir.getEnumDecl(info.inst).src_line, + .opaque_decl => zir.getOpaqueDecl(info.inst).src_line, + .reify_enum => zir.extraData(Zir.Inst.ReifyEnum, inst.data.extended.operand).data.src_line, + .reify_struct => zir.extraData(Zir.Inst.ReifyStruct, inst.data.extended.operand).data.src_line, + .reify_union => zir.extraData(Zir.Inst.ReifyUnion, inst.data.extended.operand).data.src_line, + else => unreachable, }, - error.OutOfMemory, error.Canceled => |e| return e, + else => unreachable, }; } -/// `ty` must be a union. -fn resolveUnionInner( - ty: Type, - pt: Zcu.PerThread, - resolution: enum { fields, alignment, layout, full }, -) SemaError!void { - const zcu = pt.zcu; - const gpa = zcu.gpa; - - const union_obj = zcu.typeToUnion(ty).?; - const owner: InternPool.AnalUnit = .wrap(.{ .type = ty.toIntern() }); - - if (zcu.failed_analysis.contains(owner) or zcu.transitive_failed_analysis.contains(owner)) { - return error.AnalysisFail; - } +/// Given a namespace type, returns its list of captured values. +pub fn getCaptures(ty: Type, zcu: *const Zcu) InternPool.CaptureValue.Slice { + const ip = &zcu.intern_pool; + return switch (ip.indexToKey(ty.toIntern())) { + .struct_type => ip.loadStructType(ty.toIntern()).captures, + .union_type => ip.loadUnionType(ty.toIntern()).captures, + .enum_type => ip.loadEnumType(ty.toIntern()).captures, + .opaque_type => ip.loadOpaqueType(ty.toIntern()).captures, + else => unreachable, + }; +} - if (zcu.comp.debugIncremental()) { - const info = try zcu.incremental_debug_state.getUnitInfo(gpa, owner); - info.last_update_gen = zcu.generation; +pub fn arrayBase(ty: Type, zcu: *const Zcu) struct { Type, u64 } { + var cur_ty: Type = ty; + var cur_len: u64 = 1; + while (cur_ty.zigTypeTag(zcu) == .array) { + cur_len *= cur_ty.arrayLenIncludingSentinel(zcu); + cur_ty = cur_ty.childType(zcu); } - - var analysis_arena = std.heap.ArenaAllocator.init(gpa); - defer analysis_arena.deinit(); - - var comptime_err_ret_trace = std.array_list.Managed(Zcu.LazySrcLoc).init(gpa); - defer comptime_err_ret_trace.deinit(); - - const zir = zcu.namespacePtr(union_obj.namespace).fileScope(zcu).zir.?; - var sema: Sema = .{ - .pt = pt, - .gpa = gpa, - .arena = analysis_arena.allocator(), - .code = zir, - .owner = owner, - .func_index = .none, - .func_is_naked = false, - .fn_ret_ty = Type.void, - .fn_ret_ty_ies = null, - .comptime_err_ret_trace = &comptime_err_ret_trace, - }; - defer sema.deinit(); - - (switch (resolution) { - .fields => sema.resolveUnionFieldTypes(ty, union_obj), - .alignment => sema.resolveUnionAlignment(ty, union_obj), - .layout => sema.resolveUnionLayout(ty), - .full => sema.resolveUnionFully(ty), - }) catch |err| switch (err) { - error.AnalysisFail => { - if (!zcu.failed_analysis.contains(owner)) { - try zcu.transitive_failed_analysis.put(gpa, owner, {}); - } - return error.AnalysisFail; - }, - error.OutOfMemory => |e| return e, - error.Canceled => |e| return e, - }; + return .{ cur_ty, cur_len }; } +/// Asserts that `loaded_union.layout` is not `.@"packed"`. pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) Zcu.UnionLayout { + assert(loaded_union.layout != .@"packed"); + const ip = &zcu.intern_pool; - assert(loaded_union.haveLayout(ip)); var most_aligned_field: u32 = 0; var most_aligned_field_align: InternPool.Alignment = .@"1"; var most_aligned_field_size: u64 = 0; @@ -3928,11 +2758,14 @@ pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) const field_ty: Type = .fromInterned(field_ty_ip_index); if (field_ty.isNoReturn(zcu)) continue; - const explicit_align = loaded_union.fieldAlign(ip, field_index); - const field_align = if (explicit_align != .none) - explicit_align - else - field_ty.abiAlignment(zcu); + const field_align: InternPool.Alignment = a: { + const explicit_aligns = loaded_union.field_aligns.get(ip); + if (explicit_aligns.len > 0) { + const a = explicit_aligns[field_index]; + if (a != .none) break :a a; + } + break :a field_ty.abiAlignment(zcu); + }; if (field_ty.hasRuntimeBits(zcu)) { const field_size = field_ty.abiSize(zcu); if (field_size > payload_size) { @@ -3947,8 +2780,9 @@ pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) } payload_align = payload_align.max(field_align); } - const have_tag = loaded_union.flagsUnordered(ip).runtime_tag.hasTag(); - if (!have_tag or !Type.fromInterned(loaded_union.enum_tag_ty).hasRuntimeBits(zcu)) { + if (!loaded_union.has_runtime_tag or + !Type.fromInterned(loaded_union.enum_tag_type).hasRuntimeBits(zcu)) + { return .{ .abi_size = payload_align.forward(payload_size), .abi_align = payload_align, @@ -3963,10 +2797,10 @@ pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) }; } - const tag_size = Type.fromInterned(loaded_union.enum_tag_ty).abiSize(zcu); - const tag_align = Type.fromInterned(loaded_union.enum_tag_ty).abiAlignment(zcu).max(.@"1"); + const tag_size = Type.fromInterned(loaded_union.enum_tag_type).abiSize(zcu); + const tag_align = Type.fromInterned(loaded_union.enum_tag_type).abiAlignment(zcu).max(.@"1"); return .{ - .abi_size = loaded_union.sizeUnordered(ip), + .abi_size = loaded_union.size, .abi_align = tag_align.max(payload_align), .most_aligned_field = most_aligned_field, .most_aligned_field_size = most_aligned_field_size, @@ -3975,85 +2809,229 @@ pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) .payload_align = payload_align, .tag_align = tag_align, .tag_size = tag_size, - .padding = loaded_union.paddingUnordered(ip), + .padding = loaded_union.padding, }; } -/// Returns the type of a pointer to an element. -/// Asserts that the type is a pointer, and that the element type is indexable. -/// If the element index is comptime-known, it must be passed in `offset`. -/// For *@Vector(n, T), return *align(a:b:h:v) T -/// For *[N]T, return *T -/// For [*]T, returns *T -/// For []T, returns *T -/// Handles const-ness and address spaces in particular. -/// This code is duplicated in `Sema.analyzePtrArithmetic`. -/// May perform type resolution and return a transitive `error.AnalysisFail`. -pub fn elemPtrType(ptr_ty: Type, offset: ?usize, pt: Zcu.PerThread) !Type { +/// Asserts that `ptr_ty` is either a many-item pointer, a slice, a C pointer, or a single pointer +/// to array (in other words, a pointer which is indexed by pointer arithmetic), and returns the +/// type of the element pointer at the given index. +/// +/// Asserts that the layout of the pointer element type is resolved. +/// +/// If `index` is `null`, the index is an arbitrary runtime-known value. +pub fn elemPtrType(ptr_ty: Type, index: ?u64, pt: Zcu.PerThread) Allocator.Error!Type { const zcu = pt.zcu; - const ptr_info = ptr_ty.ptrInfo(zcu); - const elem_ty = ptr_ty.elemType2(zcu); - const is_allowzero = ptr_info.flags.is_allowzero and (offset orelse 0) == 0; - const parent_ty = ptr_ty.childType(zcu); - - const VI = InternPool.Key.PtrType.VectorIndex; - - const vector_info: struct { - host_size: u16 = 0, - alignment: Alignment = .none, - vector_index: VI = .none, - } = if (parent_ty.isVector(zcu) and ptr_info.flags.size == .one) blk: { - const elem_bits = elem_ty.bitSize(zcu); - if (elem_bits == 0) break :blk .{}; - const is_packed = elem_bits < 8 or !std.math.isPowerOfTwo(elem_bits); - if (!is_packed) break :blk .{}; - - break :blk .{ - .host_size = @intCast(parent_ty.arrayLen(zcu)), - .alignment = parent_ty.abiAlignment(zcu), - .vector_index = @enumFromInt(offset.?), - }; - } else .{}; - - const alignment: Alignment = a: { - // Calculate the new pointer alignment. - if (ptr_info.flags.alignment == .none) { - // In case of an ABI-aligned pointer, any pointer arithmetic - // maintains the same ABI-alignedness. - break :a vector_info.alignment; - } - // If the addend is not a comptime-known value we can still count on - // it being a multiple of the type size. - const elem_size = (try elem_ty.abiSizeInner(.sema, zcu, pt.tid)).scalar; - const addend = if (offset) |off| elem_size * off else elem_size; - - // The resulting pointer is aligned to the lcd between the offset (an - // arbitrary number) and the alignment factor (always a power of two, - // non zero). - const new_align: Alignment = @enumFromInt(@min( - @ctz(addend), - ptr_info.flags.alignment.toLog2Units(), - )); - assert(new_align != .none); - break :a new_align; + const ip = &zcu.intern_pool; + const ptr_info = ip.indexToKey(ptr_ty.toIntern()).ptr_type; + const elem_ty: Type = switch (ptr_info.flags.size) { + .slice, .many, .c => .fromInterned(ptr_info.child), + .one => switch (ip.indexToKey(ptr_info.child)) { + .array_type => |array_type| .fromInterned(array_type.child), + else => unreachable, + }, + }; + elem_ty.assertHasLayout(zcu); + const elem_align: Alignment = switch (elem_ty.classify(zcu)) { + .no_possible_value, + .one_possible_value, + => ptr_info.flags.alignment, + + .partially_comptime, + .fully_comptime, + => switch (ptr_info.flags.alignment) { + .none => .none, + else => |array_align| .minStrict(array_align, elem_ty.abiAlignment(zcu)), + }, + + .runtime => switch (ptr_info.flags.alignment) { + .none => .none, + else => |array_align| elem_align: { + // If the index is runtime-known, use 1 as it gives the minimum possible alignment. + const effective_index = index orelse 1; + if (effective_index == 0) break :elem_align array_align; + const byte_offset = effective_index * elem_ty.abiSize(zcu); + break :elem_align .minStrict(array_align, .fromLog2Units(@ctz(byte_offset))); + }, + }, }; - return pt.ptrTypeSema(.{ + return pt.ptrType(.{ .child = elem_ty.toIntern(), .flags = .{ - .alignment = alignment, + .size = .one, .is_const = ptr_info.flags.is_const, .is_volatile = ptr_info.flags.is_volatile, - .is_allowzero = is_allowzero, + .is_allowzero = ptr_info.flags.is_allowzero and (index == null or index == 0), .address_space = ptr_info.flags.address_space, - .vector_index = vector_info.vector_index, - }, - .packed_offset = .{ - .host_size = vector_info.host_size, - .bit_offset = 0, + .alignment = elem_align, }, }); } +/// Asserts that `ptr_ty` is a pointer (single-item or C) to a struct, union, tuple, or slice, and +/// returns the type of a pointer to the field at `field_index`. +/// +/// Asserts that the layout of the pointer child type is resolved. +/// +/// For slices, `Value.slice_ptr_index` and `Value.slice_len_index` are used for the field index. +pub fn fieldPtrType(ptr_ty: Type, field_index: u32, pt: Zcu.PerThread) Allocator.Error!Type { + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const ptr_info = ip.indexToKey(ptr_ty.toIntern()).ptr_type; + assert(ptr_info.flags.size == .one or ptr_info.flags.size == .c); + const aggregate_ty: Type = .fromInterned(ptr_info.child); + aggregate_ty.assertHasLayout(zcu); + // We only exit this `switch` for default-layout aggregates, where the field pointer alignment + // is a simple minimum of the aggregate pointer alignment and the field alignment. + // `field_align` is `.none` if there is no explicit alignment annotation. + const field_ty: Type, const field_align: Alignment = switch (aggregate_ty.zigTypeTag(zcu)) { + .@"struct" => switch (aggregate_ty.containerLayout(zcu)) { + .auto => field: { + if (aggregate_ty.isTuple(zcu)) { + break :field .{ aggregate_ty.fieldType(field_index, zcu), .none }; + } + const struct_obj = ip.loadStructType(aggregate_ty.toIntern()); + break :field .{ + .fromInterned(struct_obj.field_types.get(ip)[field_index]), + struct_obj.field_aligns.getOrNone(ip, field_index), + }; + }, + .@"extern" => { + // Field alignment is determined based on the actual field offset. For instance, in + // `extern struct { x: u32, y: u16 }`, the `y` field is 4-byte aligned. + const field_ty = aggregate_ty.fieldType(field_index, zcu); + const field_offset = aggregate_ty.structFieldOffset(field_index, zcu); + const parent_align = switch (ptr_info.flags.alignment) { + .none => aggregate_ty.abiAlignment(zcu), + else => |a| a, + }; + const actual_field_align = switch (field_offset) { + 0 => parent_align, + else => parent_align.minStrict(.fromLog2Units(@ctz(field_offset))), + }; + const field_ptr_align: Alignment = a: { + if (ptr_info.flags.alignment == .none and + aggregate_ty.explicitFieldAlignment(field_index, zcu) == .none and + actual_field_align == field_ty.abiAlignment(zcu)) + { + // There's no user-specified 'align' in sight, and the alignment from the + // field offset matches the field type's natural alignment, so just use a + // default-aligned pointer. + break :a .none; + } + break :a actual_field_align; + }; + var field_ptr_info = ptr_info; + field_ptr_info.child = field_ty.toIntern(); + field_ptr_info.flags.alignment = field_ptr_align; + return pt.ptrType(field_ptr_info); + }, + .@"packed" => { + var field_ptr_info = ptr_info; + if (field_ptr_info.flags.alignment == .none) { + field_ptr_info.flags.alignment = aggregate_ty.abiAlignment(zcu); + } + field_ptr_info.packed_offset = packed_offset: { + comptime assert(Type.packed_struct_layout_version == 2); + const bit_offset = zcu.structPackedFieldBitOffset( + ip.loadStructType(aggregate_ty.toIntern()), + field_index, + ); + break :packed_offset if (ptr_info.packed_offset.host_size != 0) .{ + .host_size = ptr_info.packed_offset.host_size, + .bit_offset = ptr_info.packed_offset.bit_offset + bit_offset, + } else .{ + .host_size = switch (zcu.comp.getZigBackend()) { + else => @intCast((aggregate_ty.bitSize(zcu) + 7) / 8), + .stage2_x86_64, .stage2_c => @intCast(aggregate_ty.abiSize(zcu)), + }, + .bit_offset = ptr_info.packed_offset.bit_offset + bit_offset, + }; + }; + field_ptr_info.child = aggregate_ty.fieldType(field_index, zcu).toIntern(); + return pt.ptrType(field_ptr_info); + }, + }, + .@"union" => switch (aggregate_ty.containerLayout(zcu)) { + .auto => field: { + const union_obj = ip.loadUnionType(aggregate_ty.toIntern()); + break :field .{ + .fromInterned(union_obj.field_types.get(ip)[field_index]), + union_obj.field_aligns.getOrNone(ip, field_index), + }; + }, + .@"extern" => { + // The alignment always matches that of the union pointer. If the union pointer is + // default aligned (`.none`), we may need to explicitly align the result pointer. + const field_ty = aggregate_ty.fieldType(field_index, zcu); + var field_ptr_info = ptr_info; + field_ptr_info.child = field_ty.toIntern(); + if (field_ptr_info.flags.alignment == .none and + Alignment.compareStrict(field_ty.abiAlignment(zcu), .neq, aggregate_ty.abiAlignment(zcu))) + { + field_ptr_info.flags.alignment = aggregate_ty.abiAlignment(zcu); + } + return pt.ptrType(field_ptr_info); + }, + .@"packed" => { + const field_ty = aggregate_ty.fieldType(field_index, zcu); + var field_ptr_info = ptr_info; + if (field_ptr_info.flags.alignment == .none) { + const resolved_align = aggregate_ty.abiAlignment(zcu); + if (field_ty.abiAlignment(zcu) != resolved_align) { + field_ptr_info.flags.alignment = resolved_align; + } + } + field_ptr_info.child = aggregate_ty.fieldType(field_index, zcu).toIntern(); + return pt.ptrType(field_ptr_info); + }, + }, + .pointer => field: { + assert(aggregate_ty.isSlice(zcu)); + break :field switch (field_index) { + Value.slice_ptr_index => .{ aggregate_ty.slicePtrFieldType(zcu), .none }, + Value.slice_len_index => .{ .usize, .none }, + else => unreachable, + }; + }, + else => unreachable, + }; + const field_ptr_align: Alignment = a: { + if (aggregate_ty.zigTypeTag(zcu) == .@"struct" and aggregate_ty.structFieldIsComptime(field_index, zcu)) { + // For `comptime` fields, just use exactly what was specified, or ABI alignment if nothing was specified. + break :a field_align; + } + const actual_field_align = switch (field_align) { + .none => switch (ip.indexToKey(aggregate_ty.toIntern())) { + .tuple_type, .union_type => field_ty.abiAlignment(zcu), + .struct_type => field_ty.defaultStructFieldAlignment(.auto, zcu), + .ptr_type => Type.usize.abiAlignment(zcu), + else => unreachable, + }, + else => |a| a, + }; + const actual_aggregate_align = switch (ptr_info.flags.alignment) { + .none => aggregate_ty.abiAlignment(zcu), + else => |a| a, + }; + if (actual_aggregate_align.compareStrict(.lt, actual_field_align)) { + // Underaligned aggregate; use that alignment. + assert(ptr_info.flags.alignment != .none); + break :a actual_aggregate_align; + } + if (field_align == .none and actual_field_align == field_ty.abiAlignment(zcu)) { + // No explicit annotation on the field (nor an unusual default), and the aggregate + // alignment is irrelevant to us, so return an un-annotated pointer. + break :a .none; + } + break :a actual_field_align; + }; + var field_ptr_info = ptr_info; + field_ptr_info.flags.alignment = field_ptr_align; + field_ptr_info.child = field_ty.toIntern(); + return pt.ptrType(field_ptr_info); +} + pub fn containerTypeName(ty: Type, ip: *const InternPool) InternPool.NullTerminatedString { return switch (ip.indexToKey(ty.toIntern())) { .struct_type => ip.loadStructType(ty.toIntern()).name, @@ -4064,14 +3042,257 @@ pub fn containerTypeName(ty: Type, ip: *const InternPool) InternPool.NullTermina }; } -/// Returns `true` if a value of this type is always `null`. -/// Returns `false` if a value of this type is neve `null`. -/// Returns `null` otherwise. -pub fn isNullFromType(ty: Type, zcu: *const Zcu) ?bool { - if (ty.zigTypeTag(zcu) != .optional and !ty.isCPtr(zcu)) return false; - const child = ty.optionalChild(zcu); - if (child.zigTypeTag(zcu) == .noreturn) return true; // `?noreturn` is always null - return null; +pub fn destructurable(ty: Type, zcu: *const Zcu) bool { + return switch (ty.zigTypeTag(zcu)) { + .array, .vector => true, + .@"struct" => ty.isTuple(zcu), + else => false, + }; +} + +pub const UnpackableReason = union(enum) { + comptime_only, + pointer, + enum_inferred_int_tag: Type, + non_packed_struct: Type, + non_packed_union: Type, + slice, + other, +}; + +/// Returns `null` iff `ty` is allowed in packed types. +pub fn unpackable(ty: Type, zcu: *const Zcu) ?UnpackableReason { + return switch (ty.zigTypeTag(zcu)) { + .void, + .bool, + .float, + .int, + => null, + + .type, + .comptime_float, + .comptime_int, + .enum_literal, + .undefined, + .null, + => .comptime_only, + + .noreturn, + .@"opaque", + .error_union, + .error_set, + .frame, + .@"anyframe", + .@"fn", + .array, + .vector, + => .other, + + .optional => if (ty.isPtrLikeOptional(zcu)) + .pointer + else + .other, + + .pointer => switch (ty.ptrSize(zcu)) { + .slice => .slice, + .one, .many, .c => .pointer, + }, + + .@"enum" => switch (zcu.intern_pool.loadEnumType(ty.toIntern()).int_tag_mode) { + .explicit => null, + .auto => .{ .enum_inferred_int_tag = ty }, + }, + + .@"struct" => switch (ty.containerLayout(zcu)) { + .@"packed" => null, + .auto, .@"extern" => .{ .non_packed_struct = ty }, + }, + .@"union" => switch (ty.containerLayout(zcu)) { + .@"packed" => null, + .auto, .@"extern" => .{ .non_packed_union = ty }, + }, + }; +} + +pub const ExternPosition = enum { + ret_ty, + param_ty, + union_field, + struct_field, + element, + other, +}; + +/// Returns true if `ty` is allowed in extern types. +/// Asserts that `ty` is fully resolved. +/// Keep in sync with `Sema.explainWhyTypeIsNotExtern`. +pub fn validateExtern(ty: Type, position: ExternPosition, zcu: *const Zcu) bool { + ty.assertHasLayout(zcu); + return switch (ty.zigTypeTag(zcu)) { + .type, + .comptime_float, + .comptime_int, + .enum_literal, + .undefined, + .null, + .error_union, + .error_set, + .frame, + => false, + + .void => switch (position) { + .ret_ty, + .union_field, + .struct_field, + .element, + => true, + .param_ty, + .other, + => false, + }, + + .noreturn => position == .ret_ty, + + .@"opaque", + .bool, + .float, + .@"anyframe", + => true, + + .pointer => { + if (ty.isSlice(zcu)) return false; + const child_ty = ty.childType(zcu); + if (child_ty.zigTypeTag(zcu) == .@"fn") { + return ty.isConstPtr(zcu) and validateExternCallconv(child_ty.fnCallingConvention(zcu)); + } + return true; + }, + .int => switch (ty.intInfo(zcu).bits) { + 0, 8, 16, 32, 64, 128 => true, + else => false, + }, + .@"fn" => { + if (position != .other) return false; + return validateExternCallconv(ty.fnCallingConvention(zcu)); + }, + .@"enum" => { + const enum_obj = zcu.intern_pool.loadEnumType(ty.toIntern()); + return switch (enum_obj.int_tag_mode) { + .auto => false, + .explicit => Type.fromInterned(enum_obj.int_tag_type).validateExtern(position, zcu), + }; + }, + .@"struct" => { + const struct_obj = zcu.intern_pool.loadStructType(ty.toIntern()); + return switch (struct_obj.layout) { + .auto => false, + .@"extern" => true, + .@"packed" => switch (struct_obj.packed_backing_mode) { + .auto => false, + .explicit => Type.fromInterned(struct_obj.packed_backing_int_type).validateExtern(position, zcu), + }, + }; + }, + .@"union" => { + const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern()); + return switch (union_obj.layout) { + .auto => false, + .@"extern" => true, + .@"packed" => switch (union_obj.packed_backing_mode) { + .auto => false, + .explicit => Type.fromInterned(union_obj.packed_backing_int_type).validateExtern(position, zcu), + }, + }; + }, + .array => switch (position) { + .ret_ty, + .param_ty, + => false, + + .union_field, + .struct_field, + .element, + .other, + => ty.childType(zcu).validateExtern(.element, zcu), + }, + .vector => ty.childType(zcu).validateExtern(.element, zcu), + .optional => ty.isPtrLikeOptional(zcu), + }; +} +fn validateExternCallconv(cc: std.builtin.CallingConvention) bool { + return switch (cc) { + // For now we want to authorize PTX kernel to use zig objects, even if we end up exposing the ABI. + // The goal is to experiment with more integrated CPU/GPU code. + .nvptx_kernel => true, + else => !target_util.fnCallConvAllowsZigTypes(cc), + }; +} + +/// Asserts that `ty` has resolved layout. +pub fn assertHasLayout(ty: Type, zcu: *const Zcu) void { + if (!std.debug.runtime_safety) { + // This early exit isn't necessary (`Zcu.assertUpToDate` checks `std.debug.runtime_safety` + // itself), but LLVM has been observed to fail at optimizing away this safety check, which + // has a major performance impact on ReleaseFast compiler builds. + return; + } + switch (zcu.intern_pool.indexToKey(ty.toIntern())) { + .int_type, + .ptr_type, + .anyframe_type, + .simple_type, + .opaque_type, + .error_set_type, + .inferred_error_set_type, + => {}, + .func_type => |func_type| { + for (func_type.param_types.get(&zcu.intern_pool)) |param_ty| { + assertHasLayout(.fromInterned(param_ty), zcu); + } + assertHasLayout(.fromInterned(func_type.return_type), zcu); + }, + .array_type => |arr| assertHasLayout(.fromInterned(arr.child), zcu), + .vector_type => |vec| assertHasLayout(.fromInterned(vec.child), zcu), + .opt_type => |child| assertHasLayout(.fromInterned(child), zcu), + .error_union_type => |eu| assertHasLayout(.fromInterned(eu.payload_type), zcu), + .tuple_type => |tuple| for (tuple.types.get(&zcu.intern_pool)) |field_ty| { + assertHasLayout(.fromInterned(field_ty), zcu); + }, + .struct_type => { + assert(zcu.intern_pool.loadStructType(ty.toIntern()).want_layout); + zcu.assertUpToDate(.wrap(.{ .type_layout = ty.toIntern() })); + }, + .union_type => { + assert(zcu.intern_pool.loadUnionType(ty.toIntern()).want_layout); + zcu.assertUpToDate(.wrap(.{ .type_layout = ty.toIntern() })); + }, + .enum_type => { + assert(zcu.intern_pool.loadEnumType(ty.toIntern()).want_layout); + zcu.assertUpToDate(.wrap(.{ .type_layout = ty.toIntern() })); + }, + + // values, not types + .simple_value, + .variable, + .@"extern", + .func, + .int, + .err, + .error_union, + .enum_literal, + .enum_tag, + .float, + .ptr, + .slice, + .opt, + .aggregate, + .un, + .bitpack, + .undef, + // memoization, not types + .memoized_call, + => unreachable, + } } /// Recursively walks the type and marks for each subtype how many times it has been seen @@ -4138,13 +3359,13 @@ fn collectSubtypes(ty: Type, pt: Zcu.PerThread, visited: *std.AutoArrayHashMapUn .error_union, .enum_literal, .enum_tag, - .empty_enum_value, .float, .ptr, .slice, .opt, .aggregate, .un, + .bitpack, // memoization, not types .memoized_call, => unreachable, @@ -4243,6 +3464,7 @@ pub const Comparison = struct { }; }; +pub const @"u0": Type = .{ .ip_index = .u0_type }; pub const @"u1": Type = .{ .ip_index = .u1_type }; pub const @"u8": Type = .{ .ip_index = .u8_type }; pub const @"u16": Type = .{ .ip_index = .u16_type }; diff --git a/src/Value.zig b/src/Value.zig @@ -146,80 +146,23 @@ pub fn toType(self: Value) Type { return Type.fromInterned(self.toIntern()); } -pub fn intFromEnum(val: Value, ty: Type, pt: Zcu.PerThread) Allocator.Error!Value { - const ip = &pt.zcu.intern_pool; - const enum_ty = ip.typeOf(val.toIntern()); - return switch (ip.indexToKey(enum_ty)) { - // Assume it is already an integer and return it directly. - .simple_type, .int_type => val, - .enum_literal => |enum_literal| { - const field_index = ty.enumFieldIndex(enum_literal, pt.zcu).?; - switch (ip.indexToKey(ty.toIntern())) { - // Assume it is already an integer and return it directly. - .simple_type, .int_type => return val, - .enum_type => { - const enum_type = ip.loadEnumType(ty.toIntern()); - if (enum_type.values.len != 0) { - return Value.fromInterned(enum_type.values.get(ip)[field_index]); - } else { - // Field index and integer values are the same. - return pt.intValue(Type.fromInterned(enum_type.tag_ty), field_index); - } - }, - else => unreachable, - } - }, - .enum_type => try pt.getCoerced(val, Type.fromInterned(ip.loadEnumType(enum_ty).tag_ty)), - else => unreachable, - }; +pub fn intFromEnum(val: Value, zcu: *const Zcu) Value { + return .fromInterned(zcu.intern_pool.indexToKey(val.toIntern()).enum_tag.int); } -pub const ResolveStrat = Type.ResolveStrat; - -/// Asserts the value is an integer. -pub fn toBigInt(val: Value, space: *BigIntSpace, zcu: *Zcu) BigIntConst { - return val.toBigIntAdvanced(space, .normal, zcu, {}) catch unreachable; -} - -pub fn toBigIntSema(val: Value, space: *BigIntSpace, pt: Zcu.PerThread) !BigIntConst { - return try val.toBigIntAdvanced(space, .sema, pt.zcu, pt.tid); -} - -/// Asserts the value is an integer. -pub fn toBigIntAdvanced( - val: Value, - space: *BigIntSpace, - comptime strat: ResolveStrat, - zcu: *Zcu, - tid: strat.Tid(), -) Zcu.SemaError!BigIntConst { +/// Asserts that `val` is an integer. +pub fn toBigInt(val: Value, space: *BigIntSpace, zcu: *const Zcu) BigIntConst { + if (val.getUnsignedInt(zcu)) |x| { + return BigIntMutable.init(&space.limbs, x).toConst(); + } const ip = &zcu.intern_pool; - return switch (val.toIntern()) { - .bool_false => BigIntMutable.init(&space.limbs, 0).toConst(), - .bool_true => BigIntMutable.init(&space.limbs, 1).toConst(), - .null_value => BigIntMutable.init(&space.limbs, 0).toConst(), - else => switch (ip.indexToKey(val.toIntern())) { - .int => |int| switch (int.storage) { - .u64, .i64, .big_int => int.storage.toBigInt(space), - .lazy_align, .lazy_size => |ty| { - if (strat == .sema) try Type.fromInterned(ty).resolveLayout(strat.pt(zcu, tid)); - const x = switch (int.storage) { - else => unreachable, - .lazy_align => Type.fromInterned(ty).abiAlignment(zcu).toByteUnits() orelse 0, - .lazy_size => Type.fromInterned(ty).abiSize(zcu), - }; - return BigIntMutable.init(&space.limbs, x).toConst(); - }, - }, - .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).toBigIntAdvanced(space, strat, zcu, tid), - .opt, .ptr => BigIntMutable.init( - &space.limbs, - (try val.getUnsignedIntInner(strat, zcu, tid)).?, - ).toConst(), - .err => |err| BigIntMutable.init(&space.limbs, ip.getErrorValueIfExists(err.name).?).toConst(), - else => unreachable, - }, + const int_key = switch (ip.indexToKey(val.toIntern())) { + .enum_tag => |enum_tag| ip.indexToKey(enum_tag.int).int, + .bitpack => |bitpack| ip.indexToKey(bitpack.backing_int_val).int, + .int => |int| int, + else => unreachable, }; + return int_key.storage.toBigInt(space); } pub fn isFuncBody(val: Value, zcu: *Zcu) bool { @@ -240,31 +183,17 @@ pub fn getVariable(val: Value, mod: *Zcu) ?InternPool.Key.Variable { }; } -/// If the value fits in a u64, return it, otherwise null. -/// Asserts not undefined. -pub fn getUnsignedInt(val: Value, zcu: *const Zcu) ?u64 { - return getUnsignedIntInner(val, .normal, zcu, {}) catch unreachable; -} - -/// Asserts the value is an integer and it fits in a u64 +/// Asserts the value is a (defined) integer and it fits in a u64. pub fn toUnsignedInt(val: Value, zcu: *const Zcu) u64 { return getUnsignedInt(val, zcu).?; } -pub fn getUnsignedIntSema(val: Value, pt: Zcu.PerThread) !?u64 { - return try val.getUnsignedIntInner(.sema, pt.zcu, pt.tid); -} - /// If the value fits in a u64, return it, otherwise null. /// Asserts not undefined. -pub fn getUnsignedIntInner( - val: Value, - comptime strat: ResolveStrat, - zcu: strat.ZcuPtr(), - tid: strat.Tid(), -) !?u64 { +pub fn getUnsignedInt(val: Value, zcu: *const Zcu) ?u64 { return switch (val.toIntern()) { .undef => unreachable, + .null_value => 0, .bool_false => 0, .bool_true => 1, else => switch (zcu.intern_pool.indexToKey(val.toIntern())) { @@ -273,37 +202,28 @@ pub fn getUnsignedIntInner( .big_int => |big_int| big_int.toInt(u64) catch null, .u64 => |x| x, .i64 => |x| std.math.cast(u64, x), - .lazy_align => |ty| (try Type.fromInterned(ty).abiAlignmentInner(strat.toLazy(), zcu, tid)).scalar.toByteUnits() orelse 0, - .lazy_size => |ty| (try Type.fromInterned(ty).abiSizeInner(strat.toLazy(), zcu, tid)).scalar, }, .ptr => |ptr| switch (ptr.base_addr) { .int => ptr.byte_offset, .field => |field| { - const base_addr = (try Value.fromInterned(field.base).getUnsignedIntInner(strat, zcu, tid)) orelse return null; + const base_addr = Value.fromInterned(field.base).getUnsignedInt(zcu) orelse return null; const struct_ty = Value.fromInterned(field.base).typeOf(zcu).childType(zcu); - if (strat == .sema) { - const pt = strat.pt(zcu, tid); - try struct_ty.resolveLayout(pt); - } return base_addr + struct_ty.structFieldOffset(@intCast(field.index), zcu) + ptr.byte_offset; }, else => null, }, .opt => |opt| switch (opt.val) { .none => 0, - else => |payload| Value.fromInterned(payload).getUnsignedIntInner(strat, zcu, tid), + else => |payload| Value.fromInterned(payload).getUnsignedInt(zcu), }, - .enum_tag => |enum_tag| return Value.fromInterned(enum_tag.int).getUnsignedIntInner(strat, zcu, tid), + .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).getUnsignedInt(zcu), + .bitpack => |bitpack| Value.fromInterned(bitpack.backing_int_val).getUnsignedInt(zcu), + .err => |err| zcu.intern_pool.getErrorValueIfExists(err.name).?, else => null, }, }; } -/// Asserts the value is an integer and it fits in a u64 -pub fn toUnsignedIntSema(val: Value, pt: Zcu.PerThread) !u64 { - return (try getUnsignedIntInner(val, .sema, pt.zcu, pt.tid)).?; -} - /// Asserts the value is an integer and it fits in a i64 pub fn toSignedInt(val: Value, zcu: *const Zcu) i64 { return switch (val.toIntern()) { @@ -314,8 +234,6 @@ pub fn toSignedInt(val: Value, zcu: *const Zcu) i64 { .big_int => |big_int| big_int.toInt(i64) catch unreachable, .i64 => |x| x, .u64 => |x| @intCast(x), - .lazy_align => |ty| @intCast(Type.fromInterned(ty).abiAlignment(zcu).toByteUnits() orelse 0), - .lazy_size => |ty| @intCast(Type.fromInterned(ty).abiSize(zcu)), }, else => unreachable, }, @@ -393,7 +311,7 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{ // We use byte_count instead of abi_size here, so that any padding bytes // follow the data bytes, on both big- and little-endian systems. const byte_count = (@as(usize, @intCast(ty.bitSize(zcu))) + 7) / 8; - return writeToPackedMemory(val, ty, pt, buffer[0..byte_count], 0); + return writeToPackedMemory(val, pt, buffer[0..byte_count], 0); }, .@"struct" => { const struct_type = zcu.typeToStruct(ty) orelse return error.IllDefinedMemoryLayout; @@ -412,8 +330,8 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{ try writeToMemory(field_val, pt, buffer[off..]); }, .@"packed" => { - const byte_count = (@as(usize, @intCast(ty.bitSize(zcu))) + 7) / 8; - return writeToPackedMemory(val, ty, pt, buffer[0..byte_count], 0); + const int_index = ip.indexToKey(val.toIntern()).bitpack.backing_int_val; + return Value.fromInterned(int_index).writeToMemory(pt, buffer); }, } }, @@ -428,15 +346,14 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{ const byte_count: usize = @intCast(field_type.abiSize(zcu)); return writeToMemory(field_val, pt, buffer[0..byte_count]); } else { - const backing_ty = try ty.unionBackingType(pt); + const backing_ty = try ty.externUnionBackingType(pt); const byte_count: usize = @intCast(backing_ty.abiSize(zcu)); - return writeToMemory(val.unionValue(zcu), pt, buffer[0..byte_count]); + return writeToMemory(val.unionPayload(zcu), pt, buffer[0..byte_count]); } }, .@"packed" => { - const backing_ty = try ty.unionBackingType(pt); - const byte_count: usize = @intCast(backing_ty.abiSize(zcu)); - return writeToPackedMemory(val, ty, pt, buffer[0..byte_count], 0); + const int_val: Value = .fromInterned(ip.indexToKey(val.toIntern()).bitpack.backing_int_val); + return writeToMemory(int_val, pt, buffer); }, }, .optional => { @@ -458,7 +375,6 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{ /// big-endian packed memory layouts start at the end of the buffer. pub fn writeToPackedMemory( val: Value, - ty: Type, pt: Zcu.PerThread, buffer: []u8, bit_offset: usize, @@ -467,6 +383,7 @@ pub fn writeToPackedMemory( const ip = &zcu.intern_pool; const target = zcu.getTarget(); const endian = target.cpu.arch.endian(); + const ty = val.typeOf(zcu); if (val.isUndef(zcu)) { const bit_size: usize = @intCast(ty.bitSize(zcu)); if (bit_size != 0) { @@ -487,22 +404,22 @@ pub fn writeToPackedMemory( buffer[byte_index] &= ~(@as(u8, 1) << @as(u3, @intCast(bit_offset % 8))); } }, - .int, .@"enum" => { - if (buffer.len == 0) return; + .@"enum" => { + const int_val = val.intFromEnum(zcu); + return int_val.writeToPackedMemory(pt, buffer, bit_offset); + }, + .pointer => { + assert(!ty.isSlice(zcu)); // No well defined layout. + if (ip.getBackingAddrTag(val.toIntern()).? != .int) return error.ReinterpretDeclRef; + const addr = val.toUnsignedInt(zcu); + std.mem.writeVarPackedInt(buffer, bit_offset, zcu.getTarget().ptrBitWidth(), addr, endian); + }, + .int => { const bits = ty.intInfo(zcu).bits; - if (bits == 0) return; - - switch (ip.indexToKey((try val.intFromEnum(ty, pt)).toIntern()).int.storage) { + if (bits == 0 or buffer.len == 0) return; + switch (ip.indexToKey(val.toIntern()).int.storage) { inline .u64, .i64 => |int| std.mem.writeVarPackedInt(buffer, bit_offset, bits, int, endian), .big_int => |bigint| bigint.writePackedTwosComplement(buffer, bit_offset, bits, endian), - .lazy_align => |lazy_align| { - const num = Type.fromInterned(lazy_align).abiAlignment(zcu).toByteUnits() orelse 0; - std.mem.writeVarPackedInt(buffer, bit_offset, bits, num, endian); - }, - .lazy_size => |lazy_size| { - const num = Type.fromInterned(lazy_size).abiSize(zcu); - std.mem.writeVarPackedInt(buffer, bit_offset, bits, num, endian); - }, } }, .float => switch (ty.floatBits(target)) { @@ -524,58 +441,21 @@ pub fn writeToPackedMemory( // On big-endian systems, LLVM reverses the element order of vectors by default const tgt_elem_i = if (endian == .big) len - elem_i - 1 else elem_i; const elem_val = try val.elemValue(pt, tgt_elem_i); - try elem_val.writeToPackedMemory(elem_ty, pt, buffer, bit_offset + bits); + try elem_val.writeToPackedMemory(pt, buffer, bit_offset + bits); bits += elem_bit_size; } }, - .@"struct" => { - const struct_type = ip.loadStructType(ty.toIntern()); - // Sema is supposed to have emitted a compile error already in the case of Auto, - // and Extern is handled in non-packed writeToMemory. - assert(struct_type.layout == .@"packed"); - var bits: u16 = 0; - for (0..struct_type.field_types.len) |i| { - const field_val = Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) { - .bytes => unreachable, - .elems => |elems| elems[i], - .repeated_elem => |elem| elem, - }); - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); - const field_bits: u16 = @intCast(field_ty.bitSize(zcu)); - try field_val.writeToPackedMemory(field_ty, pt, buffer, bit_offset + bits); - bits += field_bits; - } - }, - .@"union" => { - const union_obj = zcu.typeToUnion(ty).?; - switch (union_obj.flagsUnordered(ip).layout) { - .auto, .@"extern" => unreachable, // Handled in non-packed writeToMemory - .@"packed" => { - if (val.unionTag(zcu)) |union_tag| { - const field_index = zcu.unionTagFieldIndex(union_obj, union_tag).?; - const field_type = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); - const field_val = try val.fieldValue(pt, field_index); - return field_val.writeToPackedMemory(field_type, pt, buffer, bit_offset); - } else { - const backing_ty = try ty.unionBackingType(pt); - return val.unionValue(zcu).writeToPackedMemory(backing_ty, pt, buffer, bit_offset); - } - }, - } - }, - .pointer => { - assert(!ty.isSlice(zcu)); // No well defined layout. - if (ip.getBackingAddrTag(val.toIntern()).? != .int) return error.ReinterpretDeclRef; - return val.writeToPackedMemory(Type.usize, pt, buffer, bit_offset); + .@"struct", .@"union" => { + assert(ty.containerLayout(zcu) == .@"packed"); + const int_val: Value = .fromInterned(ip.indexToKey(val.toIntern()).bitpack.backing_int_val); + return int_val.writeToPackedMemory(pt, buffer, bit_offset); }, .optional => { assert(ty.isPtrLikeOptional(zcu)); - const child = ty.optionalChild(zcu); - const opt_val = val.optionalValue(zcu); - if (opt_val) |some| { - return some.writeToPackedMemory(child, pt, buffer, bit_offset); + if (val.optionalValue(zcu)) |ptr_val| { + return ptr_val.writeToPackedMemory(pt, buffer, bit_offset); } else { - return writeToPackedMemory(try pt.intValue(Type.usize, 0), Type.usize, pt, buffer, bit_offset); + return Value.zero_usize.writeToPackedMemory(pt, buffer, bit_offset); } }, else => @panic("TODO implement writeToPackedMemory for more types"), @@ -625,13 +505,12 @@ pub fn readFromPackedMemory( pt: Zcu.PerThread, buffer: []const u8, bit_offset: usize, - arena: Allocator, + gpa: Allocator, ) error{ IllDefinedMemoryLayout, OutOfMemory, }!Value { const zcu = pt.zcu; - const ip = &zcu.intern_pool; const target = zcu.getTarget(); const endian = target.cpu.arch.endian(); switch (ty.zigTypeTag(zcu)) { @@ -665,7 +544,8 @@ pub fn readFromPackedMemory( const abi_size: usize = @intCast(ty.abiSize(zcu)); const Limb = std.math.big.Limb; const limb_count = (abi_size + @sizeOf(Limb) - 1) / @sizeOf(Limb); - const limbs_buffer = try arena.alloc(Limb, limb_count); + const limbs_buffer = try gpa.alloc(Limb, limb_count); + defer gpa.free(limbs_buffer); var bigint = BigIntMutable.init(limbs_buffer, 0); bigint.readPackedTwosComplement(buffer, bit_offset, bits, endian, int_info.signedness); @@ -673,7 +553,7 @@ pub fn readFromPackedMemory( }, .@"enum" => { const int_ty = ty.intTagType(zcu); - const int_val = try Value.readFromPackedMemory(int_ty, pt, buffer, bit_offset, arena); + const int_val = try Value.readFromPackedMemory(int_ty, pt, buffer, bit_offset, gpa); return pt.getCoerced(int_val, ty); }, .float => return Value.fromInterned(try pt.intern(.{ .float = .{ @@ -689,64 +569,35 @@ pub fn readFromPackedMemory( } })), .vector => { const elem_ty = ty.childType(zcu); - const elems = try arena.alloc(InternPool.Index, @intCast(ty.arrayLen(zcu))); + const elems = try gpa.alloc(InternPool.Index, @intCast(ty.arrayLen(zcu))); + defer gpa.free(elems); var bits: u16 = 0; const elem_bit_size: u16 = @intCast(elem_ty.bitSize(zcu)); for (elems, 0..) |_, i| { // On big-endian systems, LLVM reverses the element order of vectors by default const tgt_elem_i = if (endian == .big) elems.len - i - 1 else i; - elems[tgt_elem_i] = (try readFromPackedMemory(elem_ty, pt, buffer, bit_offset + bits, arena)).toIntern(); + elems[tgt_elem_i] = (try readFromPackedMemory(elem_ty, pt, buffer, bit_offset + bits, gpa)).toIntern(); bits += elem_bit_size; } return pt.aggregateValue(ty, elems); }, - .@"struct" => { - // Sema is supposed to have emitted a compile error already for Auto layout structs, - // and Extern is handled by non-packed readFromMemory. - const struct_type = zcu.typeToPackedStruct(ty).?; - var bits: u16 = 0; - const field_vals = try arena.alloc(InternPool.Index, struct_type.field_types.len); - for (field_vals, 0..) |*field_val, i| { - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); - const field_bits: u16 = @intCast(field_ty.bitSize(zcu)); - field_val.* = (try readFromPackedMemory(field_ty, pt, buffer, bit_offset + bits, arena)).toIntern(); - bits += field_bits; - } - return pt.aggregateValue(ty, field_vals); - }, - .@"union" => switch (ty.containerLayout(zcu)) { - .auto, .@"extern" => unreachable, // Handled by non-packed readFromMemory - .@"packed" => { - const backing_ty = try ty.unionBackingType(pt); - const val = (try readFromPackedMemory(backing_ty, pt, buffer, bit_offset, arena)).toIntern(); - return Value.fromInterned(try pt.internUnion(.{ - .ty = ty.toIntern(), - .tag = .none, - .val = val, - })); - }, + .@"struct", .@"union" => { + assert(ty.containerLayout(zcu) == .@"packed"); + const int_val: Value = try .readFromPackedMemory(ty.bitpackBackingInt(zcu), pt, buffer, bit_offset, gpa); + return pt.bitpackValue(ty, int_val); }, .pointer => { assert(!ty.isSlice(zcu)); // No well defined layout. - const int_val = try readFromPackedMemory(Type.usize, pt, buffer, bit_offset, arena); - return Value.fromInterned(try pt.intern(.{ .ptr = .{ - .ty = ty.toIntern(), - .base_addr = .int, - .byte_offset = int_val.toUnsignedInt(zcu), - } })); + const addr = (try readFromPackedMemory(Type.usize, pt, buffer, bit_offset, gpa)).toUnsignedInt(zcu); + return pt.ptrIntValue(ty, addr); }, .optional => { assert(ty.isPtrLikeOptional(zcu)); - const child_ty = ty.optionalChild(zcu); - const child_val = try readFromPackedMemory(child_ty, pt, buffer, bit_offset, arena); - return Value.fromInterned(try pt.intern(.{ .opt = .{ + const addr = (try readFromPackedMemory(Type.usize, pt, buffer, bit_offset, gpa)).toUnsignedInt(zcu); + return .fromInterned(try pt.intern(.{ .opt = .{ .ty = ty.toIntern(), - .val = switch (child_val.orderAgainstZero(zcu)) { - .lt => unreachable, - .eq => .none, - .gt => child_val.toIntern(), - }, + .val = if (addr == 0) .none else (try pt.ptrIntValue(ty.childType(zcu), addr)).toIntern(), } })); }, else => @panic("TODO implement readFromPackedMemory for more types"), @@ -764,8 +615,6 @@ pub fn toFloat(val: Value, comptime T: type, zcu: *const Zcu) T { } return @floatFromInt(x); }, - .lazy_align => |ty| @floatFromInt(Type.fromInterned(ty).abiAlignment(zcu).toByteUnits() orelse 0), - .lazy_size => |ty| @floatFromInt(Type.fromInterned(ty).abiSize(zcu)), }, .float => |float| switch (float.storage) { inline else => |x| @floatCast(x), @@ -819,110 +668,8 @@ pub fn floatCast(val: Value, dest_ty: Type, pt: Zcu.PerThread) !Value { } })); } -pub fn orderAgainstZero(lhs: Value, zcu: *Zcu) std.math.Order { - return orderAgainstZeroInner(lhs, .normal, zcu, {}) catch unreachable; -} - -pub fn orderAgainstZeroSema(lhs: Value, pt: Zcu.PerThread) !std.math.Order { - return try orderAgainstZeroInner(lhs, .sema, pt.zcu, pt.tid); -} - -pub fn orderAgainstZeroInner( - lhs: Value, - comptime strat: ResolveStrat, - zcu: *Zcu, - tid: strat.Tid(), -) Zcu.SemaError!std.math.Order { - return switch (lhs.toIntern()) { - .bool_false => .eq, - .bool_true => .gt, - else => switch (zcu.intern_pool.indexToKey(lhs.toIntern())) { - .ptr => |ptr| if (ptr.byte_offset > 0) .gt else switch (ptr.base_addr) { - .nav, .comptime_alloc, .comptime_field => .gt, - .int => .eq, - else => unreachable, - }, - .int => |int| switch (int.storage) { - .big_int => |big_int| big_int.orderAgainstScalar(0), - inline .u64, .i64 => |x| std.math.order(x, 0), - .lazy_align => .gt, // alignment is never 0 - .lazy_size => |ty| return if (Type.fromInterned(ty).hasRuntimeBitsInner( - false, - strat.toLazy(), - zcu, - tid, - ) catch |err| switch (err) { - error.NeedLazy => unreachable, - else => |e| return e, - }) .gt else .eq, - }, - .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).orderAgainstZeroInner(strat, zcu, tid), - .float => |float| switch (float.storage) { - inline else => |x| std.math.order(x, 0), - }, - .err => .gt, // error values cannot be 0 - else => unreachable, - }, - }; -} - -/// Asserts the value is comparable. -pub fn order(lhs: Value, rhs: Value, zcu: *Zcu) std.math.Order { - return orderAdvanced(lhs, rhs, .normal, zcu, {}) catch unreachable; -} - -/// Asserts the value is comparable. -pub fn orderAdvanced( - lhs: Value, - rhs: Value, - comptime strat: ResolveStrat, - zcu: *Zcu, - tid: strat.Tid(), -) !std.math.Order { - const lhs_against_zero = try lhs.orderAgainstZeroInner(strat, zcu, tid); - const rhs_against_zero = try rhs.orderAgainstZeroInner(strat, zcu, tid); - switch (lhs_against_zero) { - .lt => if (rhs_against_zero != .lt) return .lt, - .eq => return rhs_against_zero.invert(), - .gt => {}, - } - switch (rhs_against_zero) { - .lt => if (lhs_against_zero != .lt) return .gt, - .eq => return lhs_against_zero, - .gt => {}, - } - - if (lhs.isFloat(zcu) or rhs.isFloat(zcu)) { - const lhs_f128 = lhs.toFloat(f128, zcu); - const rhs_f128 = rhs.toFloat(f128, zcu); - return std.math.order(lhs_f128, rhs_f128); - } - - var lhs_bigint_space: BigIntSpace = undefined; - var rhs_bigint_space: BigIntSpace = undefined; - const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_bigint_space, strat, zcu, tid); - const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_bigint_space, strat, zcu, tid); - return lhs_bigint.order(rhs_bigint); -} - -/// Asserts the value is comparable. Does not take a type parameter because it supports -/// comparisons between heterogeneous types. -pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, zcu: *Zcu) bool { - return compareHeteroAdvanced(lhs, op, rhs, .normal, zcu, {}) catch unreachable; -} - -pub fn compareHeteroSema(lhs: Value, op: std.math.CompareOperator, rhs: Value, pt: Zcu.PerThread) !bool { - return try compareHeteroAdvanced(lhs, op, rhs, .sema, pt.zcu, pt.tid); -} - -pub fn compareHeteroAdvanced( - lhs: Value, - op: std.math.CompareOperator, - rhs: Value, - comptime strat: ResolveStrat, - zcu: *Zcu, - tid: strat.Tid(), -) !bool { +/// Asserts the value is comparable. Supports comparisons between heterogeneous types. +pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, zcu: *const Zcu) bool { if (lhs.pointerNav(zcu)) |lhs_nav| { if (rhs.pointerNav(zcu)) |rhs_nav| { switch (op) { @@ -944,9 +691,21 @@ pub fn compareHeteroAdvanced( else => {}, } } - if (lhs.isNan(zcu) or rhs.isNan(zcu)) return op == .neq; - return (try orderAdvanced(lhs, rhs, strat, zcu, tid)).compare(op); + return order(lhs, rhs, zcu).compare(op); +} + +pub fn order(lhs: Value, rhs: Value, zcu: *const Zcu) std.math.Order { + if (lhs.isFloat(zcu) or rhs.isFloat(zcu)) { + const lhs_f128 = lhs.toFloat(f128, zcu); + const rhs_f128 = rhs.toFloat(f128, zcu); + return std.math.order(lhs_f128, rhs_f128); + } + var lhs_bigint_space: BigIntSpace = undefined; + var rhs_bigint_space: BigIntSpace = undefined; + const lhs_bigint = lhs.toBigInt(&lhs_bigint_space, zcu); + const rhs_bigint = rhs.toBigInt(&rhs_bigint_space, zcu); + return lhs_bigint.order(rhs_bigint); } /// Asserts the values are comparable. Both operands have type `ty`. @@ -988,55 +747,30 @@ pub fn compareScalar( /// /// Note that `!compareAllWithZero(.eq, ...) != compareAllWithZero(.neq, ...)` pub fn compareAllWithZero(lhs: Value, op: std.math.CompareOperator, zcu: *Zcu) bool { - return compareAllWithZeroAdvancedExtra(lhs, op, .normal, zcu, {}) catch unreachable; -} - -pub fn compareAllWithZeroSema( - lhs: Value, - op: std.math.CompareOperator, - pt: Zcu.PerThread, -) Zcu.CompileError!bool { - return compareAllWithZeroAdvancedExtra(lhs, op, .sema, pt.zcu, pt.tid); -} - -pub fn compareAllWithZeroAdvancedExtra( - lhs: Value, - op: std.math.CompareOperator, - comptime strat: ResolveStrat, - zcu: *Zcu, - tid: strat.Tid(), -) Zcu.CompileError!bool { - if (lhs.isInf(zcu)) { - switch (op) { - .neq => return true, - .eq => return false, - .gt, .gte => return !lhs.isNegativeInf(zcu), - .lt, .lte => return lhs.isNegativeInf(zcu), - } - } - - switch (zcu.intern_pool.indexToKey(lhs.toIntern())) { + return switch (zcu.intern_pool.indexToKey(lhs.toIntern())) { .float => |float| switch (float.storage) { - inline else => |x| if (std.math.isNan(x)) return op == .neq, + inline else => |x| std.math.compare(x, op, 0), }, - .aggregate => |aggregate| return switch (aggregate.storage) { - .bytes => |bytes| for (bytes.toSlice(lhs.typeOf(zcu).arrayLenIncludingSentinel(zcu), &zcu.intern_pool)) |byte| { - if (!std.math.order(byte, 0).compare(op)) break false; + .aggregate => |aggregate| switch (aggregate.storage) { + .bytes => |bytes| for (bytes.toSlice( + lhs.typeOf(zcu).arrayLenIncludingSentinel(zcu), + &zcu.intern_pool, + )) |byte| { + if (!std.math.compare(byte, op, 0)) break false; } else true, .elems => |elems| for (elems) |elem| { - if (!try Value.fromInterned(elem).compareAllWithZeroAdvancedExtra(op, strat, zcu, tid)) break false; + if (!Value.fromInterned(elem).compareAllWithZero(op, zcu)) break false; } else true, - .repeated_elem => |elem| Value.fromInterned(elem).compareAllWithZeroAdvancedExtra(op, strat, zcu, tid), + .repeated_elem => |elem| Value.fromInterned(elem).compareAllWithZero(op, zcu), }, - .undef => return false, - else => {}, - } - return (try orderAgainstZeroInner(lhs, strat, zcu, tid)).compare(op); + .undef => false, + else => order(lhs, .zero_comptime_int, zcu).compare(op), + }; } pub fn eql(a: Value, b: Value, ty: Type, zcu: *Zcu) bool { - assert(zcu.intern_pool.typeOf(a.toIntern()) == ty.toIntern()); - assert(zcu.intern_pool.typeOf(b.toIntern()) == ty.toIntern()); + assert(a.typeOf(zcu).toIntern() == ty.toIntern()); + assert(b.typeOf(zcu).toIntern() == ty.toIntern()); return a.toIntern() == b.toIntern(); } @@ -1071,7 +805,7 @@ pub fn canMutateComptimeVarState(val: Value, zcu: *Zcu) bool { /// Gets the `Nav` referenced by this pointer. If the pointer does not point /// to a `Nav`, or if it points to some part of one (like a field or element), /// returns null. -pub fn pointerNav(val: Value, zcu: *Zcu) ?InternPool.Nav.Index { +pub fn pointerNav(val: Value, zcu: *const Zcu) ?InternPool.Nav.Index { return switch (zcu.intern_pool.indexToKey(val.toIntern())) { // TODO: these 3 cases are weird; these aren't pointer values! .variable => |v| v.owner_nav, @@ -1088,16 +822,13 @@ pub fn pointerNav(val: Value, zcu: *Zcu) ?InternPool.Nav.Index { pub const slice_ptr_index = 0; pub const slice_len_index = 1; +pub fn sliceLen(val: Value, zcu: *Zcu) u64 { + return Value.fromInterned(zcu.intern_pool.sliceLen(val.toIntern())).toUnsignedInt(zcu); +} pub fn slicePtr(val: Value, zcu: *Zcu) Value { return Value.fromInterned(zcu.intern_pool.slicePtr(val.toIntern())); } -/// Gets the `len` field of a slice value as a `u64`. -/// Resolves the length using `Sema` if necessary. -pub fn sliceLen(val: Value, pt: Zcu.PerThread) !u64 { - return Value.fromInterned(pt.zcu.intern_pool.sliceLen(val.toIntern())).toUnsignedIntSema(pt); -} - /// Asserts the value is an aggregate, and returns the element value at the given index. pub fn elemValue(val: Value, pt: Zcu.PerThread, index: usize) Allocator.Error!Value { const zcu = pt.zcu; @@ -1123,62 +854,6 @@ pub fn elemValue(val: Value, pt: Zcu.PerThread, index: usize) Allocator.Error!Va } } -pub fn isLazyAlign(val: Value, zcu: *Zcu) bool { - return switch (zcu.intern_pool.indexToKey(val.toIntern())) { - .int => |int| int.storage == .lazy_align, - else => false, - }; -} - -pub fn isLazySize(val: Value, zcu: *Zcu) bool { - return switch (zcu.intern_pool.indexToKey(val.toIntern())) { - .int => |int| int.storage == .lazy_size, - else => false, - }; -} - -// Asserts that the provided start/end are in-bounds. -pub fn sliceArray( - val: Value, - sema: *Sema, - start: usize, - end: usize, -) error{OutOfMemory}!Value { - const pt = sema.pt; - const ip = &pt.zcu.intern_pool; - const io = pt.zcu.comp.io; - return Value.fromInterned(try pt.intern(.{ - .aggregate = .{ - .ty = switch (pt.zcu.intern_pool.indexToKey(pt.zcu.intern_pool.typeOf(val.toIntern()))) { - .array_type => |array_type| try pt.arrayType(.{ - .len = @intCast(end - start), - .child = array_type.child, - .sentinel = if (end == array_type.len) array_type.sentinel else .none, - }), - .vector_type => |vector_type| try pt.vectorType(.{ - .len = @intCast(end - start), - .child = vector_type.child, - }), - else => unreachable, - }.toIntern(), - .storage = switch (ip.indexToKey(val.toIntern()).aggregate.storage) { - .bytes => |bytes| storage: { - try ip.string_bytes.ensureUnusedCapacity(sema.gpa, end - start + 1); - break :storage .{ .bytes = try ip.getOrPutString( - sema.gpa, - io, - bytes.toSlice(end, ip)[start..], - .maybe_embedded_nulls, - ) }; - }, - // TODO: write something like getCoercedInts to avoid needing to dupe - .elems => |elems| .{ .elems = try sema.arena.dupe(InternPool.Index, elems[start..end]) }, - .repeated_elem => |elem| .{ .repeated_elem = elem }, - }, - }, - })); -} - pub fn fieldValue(val: Value, pt: Zcu.PerThread, index: usize) !Value { const zcu = pt.zcu; return switch (zcu.intern_pool.indexToKey(val.toIntern())) { @@ -1193,8 +868,44 @@ pub fn fieldValue(val: Value, pt: Zcu.PerThread, index: usize) !Value { .elems => |elems| elems[index], .repeated_elem => |elem| elem, }), - // TODO assert the tag is correct - .un => |un| Value.fromInterned(un.val), + .un => |un| { + switch (Type.fromInterned(un.ty).containerLayout(zcu)) { + .auto, .@"extern" => {}, // TODO assert the tag is correct + .@"packed" => unreachable, + } + return .fromInterned(un.val); + }, + .bitpack => |bitpack| { + const ty: Type = .fromInterned(bitpack.ty); + assert(ty.containerLayout(zcu) == .@"packed"); + const int_val: Value = .fromInterned(bitpack.backing_int_val); + assert(!int_val.isUndef(zcu)); + const field_ty = ty.fieldType(index, zcu); + const field_bit_offset: u16 = switch (ty.zigTypeTag(zcu)) { + .@"union" => 0, + .@"struct" => off: { + var off: u16 = 0; + for (0..index) |preceding_field_index| { + off += @intCast(ty.fieldType(preceding_field_index, zcu).bitSize(zcu)); + } + break :off off; + }, + else => unreachable, + }; + // Avoid hitting gpa for accesses to small packed structs + var sfba_state = std.heap.stackFallback(128, zcu.comp.gpa); + const sfba = sfba_state.get(); + const buf = try sfba.alloc(u8, @intCast((ty.bitSize(zcu) + 7) / 8)); + defer sfba.free(buf); + int_val.writeToPackedMemory(pt, buf, 0) catch |err| switch (err) { + error.ReinterpretDeclRef => unreachable, // it's an integer + error.OutOfMemory => |e| return e, + }; + return Value.readFromPackedMemory(field_ty, pt, buf, field_bit_offset, sfba) catch |err| switch (err) { + error.IllDefinedMemoryLayout => unreachable, // it's a bitpack + error.OutOfMemory => |e| return e, + }; + }, else => unreachable, }; } @@ -1207,7 +918,7 @@ pub fn unionTag(val: Value, zcu: *Zcu) ?Value { }; } -pub fn unionValue(val: Value, zcu: *Zcu) Value { +pub fn unionPayload(val: Value, zcu: *Zcu) Value { return switch (zcu.intern_pool.indexToKey(val.toIntern())) { .un => |un| Value.fromInterned(un.val), else => unreachable, @@ -1334,63 +1045,6 @@ pub fn isFloat(self: Value, zcu: *const Zcu) bool { }; } -pub fn floatFromInt(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, zcu: *Zcu) !Value { - return floatFromIntAdvanced(val, arena, int_ty, float_ty, zcu, .normal) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - else => unreachable, - }; -} - -pub fn floatFromIntAdvanced( - val: Value, - arena: Allocator, - int_ty: Type, - float_ty: Type, - pt: Zcu.PerThread, - comptime strat: ResolveStrat, -) !Value { - const zcu = pt.zcu; - if (int_ty.zigTypeTag(zcu) == .vector) { - const result_data = try arena.alloc(InternPool.Index, int_ty.vectorLen(zcu)); - const scalar_ty = float_ty.scalarType(zcu); - for (result_data, 0..) |*scalar, i| { - const elem_val = try val.elemValue(pt, i); - scalar.* = (try floatFromIntScalar(elem_val, scalar_ty, pt, strat)).toIntern(); - } - return pt.aggregateValue(float_ty, result_data); - } - return floatFromIntScalar(val, float_ty, pt, strat); -} - -pub fn floatFromIntScalar(val: Value, float_ty: Type, pt: Zcu.PerThread, comptime strat: ResolveStrat) !Value { - return switch (pt.zcu.intern_pool.indexToKey(val.toIntern())) { - .undef => try pt.undefValue(float_ty), - .int => |int| switch (int.storage) { - .big_int => |big_int| pt.floatValue(float_ty, big_int.toFloat(f128, .nearest_even)[0]), - inline .u64, .i64 => |x| floatFromIntInner(x, float_ty, pt), - .lazy_align => |ty| floatFromIntInner((try Type.fromInterned(ty).abiAlignmentInner(strat.toLazy(), pt.zcu, pt.tid)).scalar.toByteUnits() orelse 0, float_ty, pt), - .lazy_size => |ty| floatFromIntInner((try Type.fromInterned(ty).abiSizeInner(strat.toLazy(), pt.zcu, pt.tid)).scalar, float_ty, pt), - }, - else => unreachable, - }; -} - -fn floatFromIntInner(x: anytype, dest_ty: Type, pt: Zcu.PerThread) !Value { - const target = pt.zcu.getTarget(); - const storage: InternPool.Key.Float.Storage = switch (dest_ty.floatBits(target)) { - 16 => .{ .f16 = @floatFromInt(x) }, - 32 => .{ .f32 = @floatFromInt(x) }, - 64 => .{ .f64 = @floatFromInt(x) }, - 80 => .{ .f80 = @floatFromInt(x) }, - 128 => .{ .f128 = @floatFromInt(x) }, - else => unreachable, - }; - return Value.fromInterned(try pt.intern(.{ .float = .{ - .ty = dest_ty.toIntern(), - .storage = storage, - } })); -} - fn calcLimbLenFloat(scalar: anytype) usize { if (scalar == 0) { return 1; @@ -1410,11 +1064,11 @@ pub fn numberMax(lhs: Value, rhs: Value, zcu: *Zcu) Value { if (lhs.isUndef(zcu) or rhs.isUndef(zcu)) return undef; if (lhs.isNan(zcu)) return rhs; if (rhs.isNan(zcu)) return lhs; - - return switch (order(lhs, rhs, zcu)) { - .lt => rhs, - .gt, .eq => lhs, - }; + if (compareHetero(lhs, .gt, rhs, zcu)) { + return lhs; + } else { + return rhs; + } } /// Supports both floats and ints; handles undefined. @@ -1422,11 +1076,11 @@ pub fn numberMin(lhs: Value, rhs: Value, zcu: *Zcu) Value { if (lhs.isUndef(zcu) or rhs.isUndef(zcu)) return undef; if (lhs.isNan(zcu)) return rhs; if (rhs.isNan(zcu)) return lhs; - - return switch (order(lhs, rhs, zcu)) { - .lt => lhs, - .gt, .eq => rhs, - }; + if (compareHetero(lhs, .lt, rhs, zcu)) { + return lhs; + } else { + return rhs; + } } /// Returns true if the value is a floating point type and is NaN. Returns false otherwise. @@ -2033,8 +1687,6 @@ pub fn makeBool(x: bool) Value { /// `parent_ptr` must be a single-pointer or C pointer to some optional. /// /// Returns a pointer to the payload of the optional. -/// -/// May perform type resolution. pub fn ptrOptPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value { const zcu = pt.zcu; const parent_ptr_ty = parent_ptr.typeOf(zcu); @@ -2044,7 +1696,7 @@ pub fn ptrOptPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value { assert(ptr_size == .one or ptr_size == .c); assert(opt_ty.zigTypeTag(zcu) == .optional); - const result_ty = try pt.ptrTypeSema(info: { + const result_ty = try pt.ptrType(info: { var new = parent_ptr_ty.ptrInfo(zcu); // We can correctly preserve alignment `.none`, since an optional has the same // natural alignment as its child type. @@ -2060,7 +1712,7 @@ pub fn ptrOptPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value { } const base_ptr = try parent_ptr.canonicalizeBasePtr(.one, opt_ty, pt); - return Value.fromInterned(try pt.intern(.{ .ptr = .{ + return .fromInterned(try pt.intern(.{ .ptr = .{ .ty = result_ty.toIntern(), .base_addr = .{ .opt_payload = base_ptr.toIntern() }, .byte_offset = 0, @@ -2069,7 +1721,6 @@ pub fn ptrOptPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value { /// `parent_ptr` must be a single-pointer to some error union. /// Returns a pointer to the payload of the error union. -/// May perform type resolution. pub fn ptrEuPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value { const zcu = pt.zcu; const parent_ptr_ty = parent_ptr.typeOf(zcu); @@ -2078,7 +1729,7 @@ pub fn ptrEuPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value { assert(parent_ptr_ty.ptrSize(zcu) == .one); assert(eu_ty.zigTypeTag(zcu) == .error_union); - const result_ty = try pt.ptrTypeSema(info: { + const result_ty = try pt.ptrType(info: { var new = parent_ptr_ty.ptrInfo(zcu); // We can correctly preserve alignment `.none`, since an error union has a // natural alignment greater than or equal to that of its payload type. @@ -2089,147 +1740,57 @@ pub fn ptrEuPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value { if (parent_ptr.isUndef(zcu)) return pt.undefValue(result_ty); const base_ptr = try parent_ptr.canonicalizeBasePtr(.one, eu_ty, pt); - return Value.fromInterned(try pt.intern(.{ .ptr = .{ + return .fromInterned(try pt.intern(.{ .ptr = .{ .ty = result_ty.toIntern(), .base_addr = .{ .eu_payload = base_ptr.toIntern() }, .byte_offset = 0, } })); } -/// `parent_ptr` must be a single-pointer or c pointer to a struct, union, or slice. +/// `parent_ptr` must be a single-item pointer or C pointer to a struct, union, or slice. /// /// Returns a pointer to the aggregate field at the specified index. /// /// For slices, uses `slice_ptr_index` and `slice_len_index`. /// -/// May perform type resolution. +/// Asserts that the layout of the aggregate type is resolved. pub fn ptrField(parent_ptr: Value, field_idx: u32, pt: Zcu.PerThread) !Value { const zcu = pt.zcu; const parent_ptr_ty = parent_ptr.typeOf(zcu); const aggregate_ty = parent_ptr_ty.childType(zcu); + aggregate_ty.assertHasLayout(zcu); const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu); assert(parent_ptr_info.flags.size == .one or parent_ptr_info.flags.size == .c); - // Exiting this `switch` indicates that the `field` pointer representation should be used. - // `field_align` may be `.none` to represent the natural alignment of `field_ty`, but is not necessarily. - const field_ty: Type, const field_align: InternPool.Alignment = switch (aggregate_ty.zigTypeTag(zcu)) { - .@"struct" => field: { - const field_ty = aggregate_ty.fieldType(field_idx, zcu); - switch (aggregate_ty.containerLayout(zcu)) { - .auto => break :field .{ field_ty, try aggregate_ty.fieldAlignmentSema(field_idx, pt) }, - .@"extern" => { - // Well-defined layout, so just offset the pointer appropriately. - try aggregate_ty.resolveLayout(pt); - const byte_off = aggregate_ty.structFieldOffset(field_idx, zcu); - const field_align = a: { - const parent_align = if (parent_ptr_info.flags.alignment == .none) pa: { - break :pa try aggregate_ty.abiAlignmentSema(pt); - } else parent_ptr_info.flags.alignment; - break :a InternPool.Alignment.fromLog2Units(@min(parent_align.toLog2Units(), @ctz(byte_off))); - }; - const result_ty = try pt.ptrTypeSema(info: { - var new = parent_ptr_info; - new.child = field_ty.toIntern(); - new.flags.alignment = field_align; - break :info new; - }); - return parent_ptr.getOffsetPtr(byte_off, result_ty, pt); - }, - .@"packed" => { - const packed_offset = aggregate_ty.packedStructFieldPtrInfo(parent_ptr_ty, field_idx, pt); - const result_ty = try pt.ptrType(info: { - var new = parent_ptr_info; - new.packed_offset = packed_offset; - new.child = field_ty.toIntern(); - if (new.flags.alignment == .none) { - new.flags.alignment = try aggregate_ty.abiAlignmentSema(pt); - } - break :info new; - }); - return pt.getCoerced(parent_ptr, result_ty); - }, - } - }, - .@"union" => field: { - const union_obj = zcu.typeToUnion(aggregate_ty).?; - const field_ty = Type.fromInterned(union_obj.field_types.get(&zcu.intern_pool)[field_idx]); - switch (aggregate_ty.containerLayout(zcu)) { - .auto => break :field .{ field_ty, try aggregate_ty.fieldAlignmentSema(field_idx, pt) }, - .@"extern" => { - // Point to the same address. - const result_ty = try pt.ptrTypeSema(info: { - var new = parent_ptr_info; - new.child = field_ty.toIntern(); - break :info new; - }); - return pt.getCoerced(parent_ptr, result_ty); - }, - .@"packed" => { - // If the field has an ABI size matching its bit size, then we can continue to use a - // non-bit pointer if the parent pointer is also a non-bit pointer. - if (parent_ptr_info.packed_offset.host_size == 0 and (try field_ty.abiSizeInner(.sema, zcu, pt.tid)).scalar * 8 == try field_ty.bitSizeSema(pt)) { - // We must offset the pointer on big-endian targets, since the bits of packed memory don't align nicely. - const byte_offset = switch (zcu.getTarget().cpu.arch.endian()) { - .little => 0, - .big => (try aggregate_ty.abiSizeInner(.sema, zcu, pt.tid)).scalar - (try field_ty.abiSizeInner(.sema, zcu, pt.tid)).scalar, - }; - const result_ty = try pt.ptrTypeSema(info: { - var new = parent_ptr_info; - new.child = field_ty.toIntern(); - new.flags.alignment = InternPool.Alignment.fromLog2Units( - @ctz(byte_offset | (try parent_ptr_ty.ptrAlignmentSema(pt)).toByteUnits().?), - ); - break :info new; - }); - return parent_ptr.getOffsetPtr(byte_offset, result_ty, pt); - } else { - // The result must be a bit-pointer if it is not already. - const result_ty = try pt.ptrTypeSema(info: { - var new = parent_ptr_info; - new.child = field_ty.toIntern(); - if (new.packed_offset.host_size == 0) { - new.packed_offset.host_size = @intCast(((try aggregate_ty.bitSizeSema(pt)) + 7) / 8); - assert(new.packed_offset.bit_offset == 0); - } - break :info new; - }); - return pt.getCoerced(parent_ptr, result_ty); - } - }, - } + const field_ptr_ty = try parent_ptr_ty.fieldPtrType(field_idx, pt); + + switch (aggregate_ty.zigTypeTag(zcu)) { + .pointer => assert(aggregate_ty.isSlice(zcu)), + .@"struct" => switch (aggregate_ty.containerLayout(zcu)) { + .auto => {}, + .@"extern" => return parent_ptr.getOffsetPtr( + aggregate_ty.structFieldOffset(field_idx, zcu), + field_ptr_ty, + pt, + ), + .@"packed" => return pt.getCoerced(parent_ptr, field_ptr_ty), }, - .pointer => field_ty: { - assert(aggregate_ty.isSlice(zcu)); - break :field_ty switch (field_idx) { - Value.slice_ptr_index => .{ aggregate_ty.slicePtrFieldType(zcu), Type.usize.abiAlignment(zcu) }, - Value.slice_len_index => .{ Type.usize, Type.usize.abiAlignment(zcu) }, - else => unreachable, - }; + .@"union" => switch (aggregate_ty.containerLayout(zcu)) { + .auto => {}, + .@"packed", .@"extern" => return pt.getCoerced(parent_ptr, field_ptr_ty), }, else => unreachable, - }; + } - const new_align: InternPool.Alignment = if (parent_ptr_info.flags.alignment != .none) a: { - const ty_align = (try field_ty.abiAlignmentInner(.sema, zcu, pt.tid)).scalar; - const true_field_align = if (field_align == .none) ty_align else field_align; - const new_align = true_field_align.min(parent_ptr_info.flags.alignment); - if (new_align == ty_align) break :a .none; - break :a new_align; - } else field_align; - - const result_ty = try pt.ptrTypeSema(info: { - var new = parent_ptr_info; - new.child = field_ty.toIntern(); - new.flags.alignment = new_align; - break :info new; - }); + // If we get here, we need to use the `.field` comptime pointer representation, because the + // aggregate does not have a well-defined layout. - if (parent_ptr.isUndef(zcu)) return pt.undefValue(result_ty); + if (parent_ptr.isUndef(zcu)) return pt.undefValue(field_ptr_ty); const base_ptr = try parent_ptr.canonicalizeBasePtr(.one, aggregate_ty, pt); - return Value.fromInterned(try pt.intern(.{ .ptr = .{ - .ty = result_ty.toIntern(), + return .fromInterned(try pt.intern(.{ .ptr = .{ + .ty = field_ptr_ty.toIntern(), .base_addr = .{ .field = .{ .base = base_ptr.toIntern(), .index = field_idx, @@ -2238,9 +1799,9 @@ pub fn ptrField(parent_ptr: Value, field_idx: u32, pt: Zcu.PerThread) !Value { } })); } -/// `orig_parent_ptr` must be either a single-pointer to an array or vector, or a many-pointer or C-pointer or slice. +/// `orig_parent_ptr` must be either a single-pointer to an array, a slice, a many-item pointer, or a C pointer. /// Returns a pointer to the element at the specified index. -/// May perform type resolution. +/// Asserts that the layout of the pointer element type is resolved. pub fn ptrElem(orig_parent_ptr: Value, field_idx: u64, pt: Zcu.PerThread) !Value { const zcu = pt.zcu; const parent_ptr = switch (orig_parent_ptr.typeOf(zcu).ptrSize(zcu)) { @@ -2249,79 +1810,50 @@ pub fn ptrElem(orig_parent_ptr: Value, field_idx: u64, pt: Zcu.PerThread) !Value }; const parent_ptr_ty = parent_ptr.typeOf(zcu); - const elem_ty = parent_ptr_ty.childType(zcu); - const result_ty = try parent_ptr_ty.elemPtrType(@intCast(field_idx), pt); + const result_ty = try parent_ptr_ty.elemPtrType(field_idx, pt); + const elem_ty = result_ty.childType(zcu); + elem_ty.assertHasLayout(zcu); if (parent_ptr.isUndef(zcu)) return pt.undefValue(result_ty); - if (result_ty.ptrInfo(zcu).packed_offset.host_size != 0) { - // Since we have a bit-pointer, the pointer address should be unchanged. - assert(elem_ty.zigTypeTag(zcu) == .vector); - return pt.getCoerced(parent_ptr, result_ty); + if (!elem_ty.comptimeOnly(zcu)) { + const byte_offset = field_idx * elem_ty.abiSize(zcu); + return parent_ptr.getOffsetPtr(byte_offset, result_ty, pt); } - const PtrStrat = union(enum) { - offset: u64, - elem_ptr: Type, // many-ptr elem ty - }; - - const strat: PtrStrat = switch (parent_ptr_ty.ptrSize(zcu)) { - .one => switch (elem_ty.zigTypeTag(zcu)) { - .vector => .{ .offset = field_idx * @divExact(try elem_ty.childType(zcu).bitSizeSema(pt), 8) }, - .array => strat: { - const arr_elem_ty = elem_ty.childType(zcu); - if (try arr_elem_ty.comptimeOnlySema(pt)) { - break :strat .{ .elem_ptr = arr_elem_ty }; - } - break :strat .{ .offset = field_idx * (try arr_elem_ty.abiSizeInner(.sema, zcu, pt.tid)).scalar }; - }, - else => unreachable, - }, + // Comptime-only element type. - .many, .c => if (try elem_ty.comptimeOnlySema(pt)) - .{ .elem_ptr = elem_ty } - else - .{ .offset = field_idx * (try elem_ty.abiSizeInner(.sema, zcu, pt.tid)).scalar }, - - .slice => unreachable, - }; + if (field_idx == 0) { + return pt.getCoerced(parent_ptr, result_ty); + } - switch (strat) { - .offset => |byte_offset| { - return parent_ptr.getOffsetPtr(byte_offset, result_ty, pt); - }, - .elem_ptr => |manyptr_elem_ty| if (field_idx == 0) { - return pt.getCoerced(parent_ptr, result_ty); - } else { - const arr_base_ty, const arr_base_len = manyptr_elem_ty.arrayBase(zcu); - const base_idx = arr_base_len * field_idx; - const parent_info = zcu.intern_pool.indexToKey(parent_ptr.toIntern()).ptr; - switch (parent_info.base_addr) { - .arr_elem => |arr_elem| { - if (Value.fromInterned(arr_elem.base).typeOf(zcu).childType(zcu).toIntern() == arr_base_ty.toIntern()) { - // We already have a pointer to an element of an array of this type. - // Just modify the index. - return Value.fromInterned(try pt.intern(.{ .ptr = ptr: { - var new = parent_info; - new.base_addr.arr_elem.index += base_idx; - new.ty = result_ty.toIntern(); - break :ptr new; - } })); - } - }, - else => {}, + const arr_base_ty, const arr_base_len = elem_ty.arrayBase(zcu); + const base_idx = arr_base_len * field_idx; + const parent_info = zcu.intern_pool.indexToKey(parent_ptr.toIntern()).ptr; + switch (parent_info.base_addr) { + .arr_elem => |arr_elem| { + if (Value.fromInterned(arr_elem.base).typeOf(zcu).childType(zcu).toIntern() == arr_base_ty.toIntern()) { + // We already have a pointer to an element of an array of this type. + // Just modify the index. + return .fromInterned(try pt.intern(.{ .ptr = ptr: { + var new = parent_info; + new.base_addr.arr_elem.index += base_idx; + new.ty = result_ty.toIntern(); + break :ptr new; + } })); } - const base_ptr = try parent_ptr.canonicalizeBasePtr(.many, arr_base_ty, pt); - return Value.fromInterned(try pt.intern(.{ .ptr = .{ - .ty = result_ty.toIntern(), - .base_addr = .{ .arr_elem = .{ - .base = base_ptr.toIntern(), - .index = base_idx, - } }, - .byte_offset = 0, - } })); }, + else => {}, } + const base_ptr = try parent_ptr.canonicalizeBasePtr(.many, arr_base_ty, pt); + return .fromInterned(try pt.intern(.{ .ptr = .{ + .ty = result_ty.toIntern(), + .base_addr = .{ .arr_elem = .{ + .base = base_ptr.toIntern(), + .index = base_idx, + } }, + .byte_offset = 0, + } })); } fn canonicalizeBasePtr(base_ptr: Value, want_size: std.builtin.Type.Pointer.Size, want_child: Type, pt: Zcu.PerThread) !Value { @@ -2417,19 +1949,11 @@ pub const PointerDeriveStep = union(enum) { } }; -pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread) Allocator.Error!PointerDeriveStep { - return ptr_val.pointerDerivationAdvanced(arena, pt, false, null) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.Canceled => @panic("TODO"), // pls remove from error set mlugg - error.AnalysisFail => unreachable, - }; -} - /// Given a pointer value, get the sequence of steps to derive it, ideally by taking /// only field and element pointers with no casts. This can be used by codegen backends /// which prefer field/elem accesses when lowering constant pointer values. /// It is also used by the Value printing logic for pointers. -pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread, comptime resolve_types: bool, opt_sema: ?*Sema) !PointerDeriveStep { +pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread, opt_sema: ?*Sema) Allocator.Error!PointerDeriveStep { const zcu = pt.zcu; const ptr = zcu.intern_pool.indexToKey(ptr_val.toIntern()).ptr; const base_derive: PointerDeriveStep = switch (ptr.base_addr) { @@ -2454,7 +1978,7 @@ pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerTh .comptime_alloc => |idx| base: { const sema = opt_sema.?; const alloc = sema.getComptimeAlloc(idx); - const val = try alloc.val.intern(pt, sema.arena); + const val = try alloc.val.intern(pt, arena); const ty = val.typeOf(zcu); break :base .{ .comptime_alloc_ptr = .{ .idx = idx, @@ -2472,7 +1996,7 @@ pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerTh const base_ptr = Value.fromInterned(eu_ptr); const base_ptr_ty = base_ptr.typeOf(zcu); const parent_step = try arena.create(PointerDeriveStep); - parent_step.* = try pointerDerivationAdvanced(Value.fromInterned(eu_ptr), arena, pt, resolve_types, opt_sema); + parent_step.* = try pointerDerivation(.fromInterned(eu_ptr), arena, pt, opt_sema); break :base .{ .eu_payload_ptr = .{ .parent = parent_step, .result_ptr_ty = try pt.adjustPtrTypeChild(base_ptr_ty, base_ptr_ty.childType(zcu).errorUnionPayload(zcu)), @@ -2482,7 +2006,7 @@ pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerTh const base_ptr = Value.fromInterned(opt_ptr); const base_ptr_ty = base_ptr.typeOf(zcu); const parent_step = try arena.create(PointerDeriveStep); - parent_step.* = try pointerDerivationAdvanced(Value.fromInterned(opt_ptr), arena, pt, resolve_types, opt_sema); + parent_step.* = try pointerDerivation(.fromInterned(opt_ptr), arena, pt, opt_sema); break :base .{ .opt_payload_ptr = .{ .parent = parent_step, .result_ptr_ty = try pt.adjustPtrTypeChild(base_ptr_ty, base_ptr_ty.childType(zcu).optionalChild(zcu)), @@ -2490,59 +2014,32 @@ pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerTh }, .field => |field| base: { const base_ptr = Value.fromInterned(field.base); - const base_ptr_ty = base_ptr.typeOf(zcu); - const agg_ty = base_ptr_ty.childType(zcu); - const field_ty, const field_align = switch (agg_ty.zigTypeTag(zcu)) { - .@"struct" => .{ agg_ty.fieldType(@intCast(field.index), zcu), try agg_ty.fieldAlignmentInner( - @intCast(field.index), - if (resolve_types) .sema else .normal, - pt.zcu, - if (resolve_types) pt.tid else {}, - ) }, - .@"union" => .{ agg_ty.unionFieldTypeByIndex(@intCast(field.index), zcu), try agg_ty.fieldAlignmentInner( - @intCast(field.index), - if (resolve_types) .sema else .normal, - pt.zcu, - if (resolve_types) pt.tid else {}, - ) }, - .pointer => .{ switch (field.index) { - Value.slice_ptr_index => agg_ty.slicePtrFieldType(zcu), - Value.slice_len_index => Type.usize, - else => unreachable, - }, Type.usize.abiAlignment(zcu) }, - else => unreachable, - }; - const base_align = base_ptr_ty.ptrAlignment(zcu); - const result_align = field_align.minStrict(base_align); - const result_ty = try pt.ptrType(.{ - .child = field_ty.toIntern(), - .flags = flags: { - var flags = base_ptr_ty.ptrInfo(zcu).flags; - if (result_align == field_ty.abiAlignment(zcu)) { - flags.alignment = .none; - } else { - flags.alignment = result_align; - } - break :flags flags; - }, + const base_ptr_ty = try pt.ptrType(info: { + var info = base_ptr.typeOf(zcu).ptrInfo(zcu); + info.flags.size = .one; + break :info info; }); const parent_step = try arena.create(PointerDeriveStep); - parent_step.* = try pointerDerivationAdvanced(base_ptr, arena, pt, resolve_types, opt_sema); + parent_step.* = try pointerDerivation(base_ptr, arena, pt, opt_sema); break :base .{ .field_ptr = .{ .parent = parent_step, .field_idx = @intCast(field.index), - .result_ptr_ty = result_ty, + .result_ptr_ty = try base_ptr_ty.fieldPtrType(@intCast(field.index), pt), } }; }, .arr_elem => |arr_elem| base: { const parent_step = try arena.create(PointerDeriveStep); - parent_step.* = try pointerDerivationAdvanced(Value.fromInterned(arr_elem.base), arena, pt, resolve_types, opt_sema); + parent_step.* = try pointerDerivation(.fromInterned(arr_elem.base), arena, pt, opt_sema); const parent_ptr_info = (try parent_step.ptrType(pt)).ptrInfo(zcu); const result_ptr_ty = try pt.ptrType(.{ .child = parent_ptr_info.child, .flags = flags: { var flags = parent_ptr_info.flags; flags.size = .one; + if (flags.alignment != .none) flags.alignment = .minStrict( + flags.alignment, + Type.fromInterned(parent_ptr_info.child).abiAlignment(zcu), + ); break :flags flags; }, }); @@ -2560,7 +2057,7 @@ pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerTh const ptr_ty_info = Type.fromInterned(ptr.ty).ptrInfo(zcu); const need_child: Type = .fromInterned(ptr_ty_info.child); - if (need_child.comptimeOnly(zcu)) { + if (need_child.comptimeOnly(zcu) or need_child.zigTypeTag(zcu) == .@"opaque") { // No refinement can happen - this pointer is presumably invalid. // Just offset it. const parent = try arena.create(PointerDeriveStep); @@ -2662,27 +2159,17 @@ pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerTh const start_off = cur_ty.structFieldOffset(field_idx, zcu); const end_off = start_off + field_ty.abiSize(zcu); if (cur_offset >= start_off and cur_offset + need_bytes <= end_off) { - const old_ptr_ty = try cur_derive.ptrType(pt); - const parent_align = old_ptr_ty.ptrAlignment(zcu); - const field_align = InternPool.Alignment.fromLog2Units(@min(parent_align.toLog2Units(), @ctz(start_off))); + const base_ptr_ty = try pt.ptrType(info: { + var info = (try cur_derive.ptrType(pt)).ptrInfo(zcu); + info.flags.size = .one; + break :info info; + }); const parent = try arena.create(PointerDeriveStep); parent.* = cur_derive; - const new_ptr_ty = try pt.ptrType(.{ - .child = field_ty.toIntern(), - .flags = flags: { - var flags = old_ptr_ty.ptrInfo(zcu).flags; - if (field_align == field_ty.abiAlignment(zcu)) { - flags.alignment = .none; - } else { - flags.alignment = field_align; - } - break :flags flags; - }, - }); cur_derive = .{ .field_ptr = .{ .parent = parent, .field_idx = @intCast(field_idx), - .result_ptr_ty = new_ptr_ty, + .result_ptr_ty = try base_ptr_ty.fieldPtrType(@intCast(field_idx), pt), } }; cur_offset -= start_off; break; @@ -2720,148 +2207,6 @@ pub fn pointerDerivationAdvanced(ptr_val: Value, arena: Allocator, pt: Zcu.PerTh } }; } -pub fn resolveLazy( - val: Value, - arena: Allocator, - pt: Zcu.PerThread, -) Zcu.SemaError!Value { - switch (pt.zcu.intern_pool.indexToKey(val.toIntern())) { - .int => |int| switch (int.storage) { - .u64, .i64, .big_int => return val, - .lazy_align, .lazy_size => return pt.intValue( - Type.fromInterned(int.ty), - try val.toUnsignedIntSema(pt), - ), - }, - .slice => |slice| { - const ptr = try Value.fromInterned(slice.ptr).resolveLazy(arena, pt); - const len = try Value.fromInterned(slice.len).resolveLazy(arena, pt); - if (ptr.toIntern() == slice.ptr and len.toIntern() == slice.len) return val; - return Value.fromInterned(try pt.intern(.{ .slice = .{ - .ty = slice.ty, - .ptr = ptr.toIntern(), - .len = len.toIntern(), - } })); - }, - .ptr => |ptr| { - switch (ptr.base_addr) { - .nav, .comptime_alloc, .uav, .int => return val, - .comptime_field => |field_val| { - const resolved_field_val = (try Value.fromInterned(field_val).resolveLazy(arena, pt)).toIntern(); - return if (resolved_field_val == field_val) - val - else - Value.fromInterned(try pt.intern(.{ .ptr = .{ - .ty = ptr.ty, - .base_addr = .{ .comptime_field = resolved_field_val }, - .byte_offset = ptr.byte_offset, - } })); - }, - .eu_payload, .opt_payload => |base| { - const resolved_base = (try Value.fromInterned(base).resolveLazy(arena, pt)).toIntern(); - return if (resolved_base == base) - val - else - Value.fromInterned(try pt.intern(.{ .ptr = .{ - .ty = ptr.ty, - .base_addr = switch (ptr.base_addr) { - .eu_payload => .{ .eu_payload = resolved_base }, - .opt_payload => .{ .opt_payload = resolved_base }, - else => unreachable, - }, - .byte_offset = ptr.byte_offset, - } })); - }, - .arr_elem, .field => |base_index| { - const resolved_base = (try Value.fromInterned(base_index.base).resolveLazy(arena, pt)).toIntern(); - return if (resolved_base == base_index.base) - val - else - Value.fromInterned(try pt.intern(.{ .ptr = .{ - .ty = ptr.ty, - .base_addr = switch (ptr.base_addr) { - .arr_elem => .{ .arr_elem = .{ - .base = resolved_base, - .index = base_index.index, - } }, - .field => .{ .field = .{ - .base = resolved_base, - .index = base_index.index, - } }, - else => unreachable, - }, - .byte_offset = ptr.byte_offset, - } })); - }, - } - }, - .aggregate => |aggregate| switch (aggregate.storage) { - .bytes => return val, - .elems => |elems| { - var resolved_elems: []InternPool.Index = &.{}; - for (elems, 0..) |elem, i| { - const resolved_elem = (try Value.fromInterned(elem).resolveLazy(arena, pt)).toIntern(); - if (resolved_elems.len == 0 and resolved_elem != elem) { - resolved_elems = try arena.alloc(InternPool.Index, elems.len); - @memcpy(resolved_elems[0..i], elems[0..i]); - } - if (resolved_elems.len > 0) resolved_elems[i] = resolved_elem; - } - return if (resolved_elems.len == 0) - val - else - pt.aggregateValue(.fromInterned(aggregate.ty), resolved_elems); - }, - .repeated_elem => |elem| { - const resolved_elem = try Value.fromInterned(elem).resolveLazy(arena, pt); - return if (resolved_elem.toIntern() == elem) - val - else - pt.aggregateSplatValue(.fromInterned(aggregate.ty), resolved_elem); - }, - }, - .un => |un| { - const resolved_tag = if (un.tag == .none) - .none - else - (try Value.fromInterned(un.tag).resolveLazy(arena, pt)).toIntern(); - const resolved_val = (try Value.fromInterned(un.val).resolveLazy(arena, pt)).toIntern(); - return if (resolved_tag == un.tag and resolved_val == un.val) - val - else - Value.fromInterned(try pt.internUnion(.{ - .ty = un.ty, - .tag = resolved_tag, - .val = resolved_val, - })); - }, - .error_union => |eu| switch (eu.val) { - .err_name => return val, - .payload => |payload| { - const resolved_payload = try Value.fromInterned(payload).resolveLazy(arena, pt); - if (resolved_payload.toIntern() == payload) return val; - return .fromInterned(try pt.intern(.{ .error_union = .{ - .ty = eu.ty, - .val = .{ .payload = resolved_payload.toIntern() }, - } })); - }, - }, - .opt => |opt| switch (opt.val) { - .none => return val, - else => |payload| { - const resolved_payload = try Value.fromInterned(payload).resolveLazy(arena, pt); - if (resolved_payload.toIntern() == payload) return val; - return .fromInterned(try pt.intern(.{ .opt = .{ - .ty = opt.ty, - .val = resolved_payload.toIntern(), - } })); - }, - }, - - else => return val, - } -} - const InterpretMode = enum { /// In this mode, types are assumed to match what the compiler was built with in terms of field /// order, field types, etc. This improves compiler performance. However, it means that certain @@ -2878,7 +2223,6 @@ const interpret_mode: InterpretMode = @field(InterpretMode, @tagName(build_optio /// Given a `Value` representing a comptime-known value of type `T`, unwrap it into an actual `T` known to the compiler. /// This is useful for accessing `std.builtin` structures received from comptime logic. -/// `val` must be fully resolved. pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMemory, UndefinedValue, TypeMismatch }!T { const zcu = pt.zcu; const io = zcu.comp.io; @@ -2917,7 +2261,6 @@ pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMe }, .int => switch (ip.indexToKey(val.toIntern()).int.storage) { - .lazy_align, .lazy_size => unreachable, // `val` is fully resolved inline .u64, .i64 => |x| std.math.cast(T, x) orelse return error.TypeMismatch, .big_int => |big| big.toInt(T) catch return error.TypeMismatch, }, @@ -2949,7 +2292,7 @@ pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMe inline else => |tag_comptime| @unionInit( T, @tagName(tag_comptime), - try val.unionValue(zcu).interpret(@FieldType(T, @tagName(tag_comptime)), pt), + try val.unionPayload(zcu).interpret(@FieldType(T, @tagName(tag_comptime)), pt), ), }; }, @@ -3076,7 +2419,7 @@ pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory } for (field_vals, 0..) |*field_val, field_idx| { if (field_val.* == .none) { - const default_init = struct_obj.field_inits.get(ip)[field_idx]; + const default_init = struct_obj.field_defaults.get(ip)[field_idx]; if (default_init == .none) return error.TypeMismatch; field_val.* = default_init; } @@ -3092,8 +2435,8 @@ pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory pub fn doPointersOverlap(ptr_val_a: Value, ptr_val_b: Value, elem_count: u64, zcu: *const Zcu) bool { const ip = &zcu.intern_pool; - const a_elem_ty = ptr_val_a.typeOf(zcu).indexablePtrElem(zcu); - const b_elem_ty = ptr_val_b.typeOf(zcu).indexablePtrElem(zcu); + const a_elem_ty = ptr_val_a.typeOf(zcu).indexableElem(zcu); + const b_elem_ty = ptr_val_b.typeOf(zcu).indexableElem(zcu); const a_ptr = ip.indexToKey(ptr_val_a.toIntern()).ptr; const b_ptr = ip.indexToKey(ptr_val_b.toIntern()).ptr; @@ -3179,3 +2522,58 @@ pub fn eqlScalarNum(lhs: Value, rhs: Value, zcu: *Zcu) bool { const rhs_bigint = rhs.toBigInt(&rhs_bigint_space, zcu); return lhs_bigint.eql(rhs_bigint); } + +/// Asserts the value is an integer, and the destination type is ComptimeInt or Int. +/// Vectors are also accepted. Vector results are reduced with AND. +/// +/// If provided, `vector_index` reports the first element that failed the range check. +pub fn intFitsInType( + val: Value, + ty: Type, + vector_index: ?*usize, + zcu: *const Zcu, +) bool { + if (ty.toIntern() == .comptime_int_type) return true; + const info = ty.intInfo(zcu); + switch (val.toIntern()) { + .zero_usize, .zero_u8 => return true, + else => switch (zcu.intern_pool.indexToKey(val.toIntern())) { + .undef => return true, + .variable, .@"extern", .func, .ptr => { + const target = zcu.getTarget(); + const ptr_bits = target.ptrBitWidth(); + return switch (info.signedness) { + .signed => info.bits > ptr_bits, + .unsigned => info.bits >= ptr_bits, + }; + }, + .int => |int| { + var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined; + const big_int = int.storage.toBigInt(&buffer); + return big_int.fitsInTwosComp(info.signedness, info.bits); + }, + .aggregate => |aggregate| { + assert(ty.zigTypeTag(zcu) == .vector); + return switch (aggregate.storage) { + .bytes => |bytes| for (bytes.toSlice(ty.vectorLen(zcu), &zcu.intern_pool), 0..) |byte, i| { + if (byte == 0) continue; + const actual_needed_bits = std.math.log2(byte) + 1 + @intFromBool(info.signedness == .signed); + if (info.bits >= actual_needed_bits) continue; + if (vector_index) |vi| vi.* = i; + break false; + } else true, + .elems, .repeated_elem => for (switch (aggregate.storage) { + .bytes => unreachable, + .elems => |elems| elems, + .repeated_elem => |elem| @as(*const [1]InternPool.Index, &elem), + }, 0..) |elem, i| { + if (Value.fromInterned(elem).intFitsInType(ty.scalarType(zcu), null, zcu)) continue; + if (vector_index) |vi| vi.* = i; + break false; + } else true, + }; + }, + else => unreachable, + }, + } +} diff --git a/src/Zcu.zig b/src/Zcu.zig @@ -14,6 +14,8 @@ const mem = std.mem; const Allocator = std.mem.Allocator; const assert = std.debug.assert; const log = std.log.scoped(.zcu); +const deps_log = std.log.scoped(.zcu_deps); +const refs_log = std.log.scoped(.zcu_refs); const BigIntConst = std.math.big.int.Const; const BigIntMutable = std.math.big.int.Mutable; const Target = std.Target; @@ -117,7 +119,7 @@ module_roots: std.AutoArrayHashMapUnmanaged(*Package.Module, File.Index.Optional /// /// Always accessed through `ImportTableAdapter`, where keys are fully resolved /// file paths in order to ensure files are properly deduplicated. This table owns -/// the keys and values. +/// the keysand values. /// /// Protected by Compilation's mutex. /// @@ -175,7 +177,9 @@ embed_table: std.ArrayHashMapUnmanaged( /// is not yet implemented. intern_pool: InternPool = .empty, -analysis_in_progress: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .empty, +/// Value explains why this `AnalUnit` is being analyzed. It is `null` for the topmost analysis +/// (index 0), and non-`null` for all others. +analysis_in_progress: std.AutoArrayHashMapUnmanaged(AnalUnit, ?*const DependencyReason) = .empty, /// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator. failed_analysis: std.AutoArrayHashMapUnmanaged(AnalUnit, *ErrorMsg) = .empty, /// This `AnalUnit` failed semantic analysis because it required analysis of another `AnalUnit` which itself failed. @@ -187,6 +191,19 @@ transitive_failed_analysis: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .emp /// codegen and linking run on a separate thread. failed_codegen: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, *ErrorMsg) = .empty, failed_types: std.AutoArrayHashMapUnmanaged(InternPool.Index, *ErrorMsg) = .empty, + +/// Key is an `AnalUnit` which is in `dependency_loop_nodes`. For each dependency loop, exactly one +/// unit in the loop is in this map, though the choice is arbitrary and not necessarily reproducible +/// between compilations. So, instead of (for instance) defining where the dependency loop "starts", +/// this map simply exists to allow easily iterating all dependency loops exactly once. +dependency_loops: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .empty, +/// Key is an `AnalUnit`, value is the `AnalUnit` which the key references and why it does so. +/// All units in here form loops. To iterate loops, see `dependency_loops`. +dependency_loop_nodes: std.AutoArrayHashMapUnmanaged(AnalUnit, struct { + unit: AnalUnit, + reason: DependencyReason, +}) = .empty, + /// Keep track of `@compileLog`s per `AnalUnit`. /// We track the source location of the first `@compileLog` call, and all logged lines as a linked list. /// The list is singly linked, but we do track its tail for fast appends (optimizing many logs in one unit). @@ -247,6 +264,10 @@ cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = . /// Maximum amount of distinct error values, set by --error-limit error_limit: ErrorInt, +/// In safe builds, `Type.assertHasLayout` may be called cross-thread, so this lock +/// guards accesses to `outdated` and `potentially_outdated`. In unsafe builds, the +/// lock is not needed and is compiled out. +outdated_lock: if (std.debug.runtime_safety) std.Io.RwLock else void = if (std.debug.runtime_safety) .init, /// Value is the number of PO dependencies of this AnalUnit. /// This value will decrease as we perform semantic analysis to learn what is outdated. /// If any of these PO deps is outdated, this value will be moved to `outdated`. @@ -254,19 +275,22 @@ potentially_outdated: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .empty, /// Value is the number of PO dependencies of this AnalUnit. /// Once this value drops to 0, the AnalUnit is a candidate for re-analysis. outdated: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .empty, -/// This contains all `AnalUnit`s in `outdated` whose PO dependency count is 0. +/// This is the set of all `AnalUnit`s in `outdated` whose PO dependency count is 0. /// Such `AnalUnit`s are ready for immediate re-analysis. /// See `findOutdatedToAnalyze` for details. -outdated_ready: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .empty, +outdated_ready: struct { + /// These are separate from other units because it allows `findOutdatedToAnalyze` to prioritize + /// functions, which is useful because it means they will be sent to codegen more quickly. + funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), + /// Does not contain `.func` units. + other: std.AutoArrayHashMapUnmanaged(AnalUnit, void), +} = .{ .funcs = .empty, .other = .empty }, /// This contains a list of AnalUnit whose analysis or codegen failed, but the /// failure was something like running out of disk space, and trying again may /// succeed. On the next update, we will flush this list, marking all members of /// it as outdated. retryable_failures: std.ArrayList(AnalUnit) = .empty, -func_body_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty, -nav_val_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty, - /// These are the modules which we initially queue for analysis in `Compilation.update`. /// `resolveReferences` will use these as the root of its reachability traversal. analysis_roots_buffer: [5]*Package.Module, @@ -322,6 +346,12 @@ codegen_task_pool: CodegenTaskPool, generation: u32 = 0, +pub const DependencyReason = struct { + src: LazySrcLoc, + /// Only populated if this is for a `.type_layout` unit. + type_layout_reason: Sema.type_resolution.LayoutResolveReason, +}; + pub const IncrementalDebugState = struct { /// All container types in the ZCU, even dead ones. /// Value is the generation the type was created on. @@ -1220,6 +1250,15 @@ pub const ErrorMsg = struct { notes: []ErrorMsg = &.{}, reference_trace_root: AnalUnit.Optional = .none, + pub fn order(lhs: *const ErrorMsg, rhs: *const ErrorMsg, zcu: *Zcu) std.math.Order { + return lhs.src_loc.order(rhs.src_loc, zcu).differ() orelse + std.mem.order(u8, lhs.msg, rhs.msg).differ() orelse + std.math.order(lhs.notes.len, rhs.notes.len).differ() orelse + for (lhs.notes, rhs.notes) |*lhs_note, *rhs_note| { + if (order(lhs_note, rhs_note, zcu).differ()) |o| break o; + } else .eq; + } + pub fn create( gpa: Allocator, src_loc: LazySrcLoc, @@ -1910,40 +1949,6 @@ pub const SrcLoc = struct { const full = tree.fullPtrType(parent_node).?; return tree.nodeToSpan(full.ast.bit_range_end.unwrap().?); }, - .node_offset_container_tag => |node_off| { - const tree = try src_loc.file_scope.getTree(zcu); - const parent_node = node_off.toAbsolute(src_loc.base_node); - - switch (tree.nodeTag(parent_node)) { - .container_decl_arg, .container_decl_arg_trailing => { - const full = tree.containerDeclArg(parent_node); - const arg_node = full.ast.arg.unwrap().?; - return tree.nodeToSpan(arg_node); - }, - .tagged_union_enum_tag, .tagged_union_enum_tag_trailing => { - const full = tree.taggedUnionEnumTag(parent_node); - const arg_node = full.ast.arg.unwrap().?; - - return tree.tokensToSpan( - tree.firstToken(arg_node) - 2, - tree.lastToken(arg_node) + 1, - tree.nodeMainToken(arg_node), - ); - }, - else => unreachable, - } - }, - .node_offset_field_default => |node_off| { - const tree = try src_loc.file_scope.getTree(zcu); - const parent_node = node_off.toAbsolute(src_loc.base_node); - - const full: Ast.full.ContainerField = switch (tree.nodeTag(parent_node)) { - .container_field => tree.containerField(parent_node), - .container_field_init => tree.containerFieldInit(parent_node), - else => unreachable, - }; - return tree.nodeToSpan(full.ast.value_expr.unwrap().?); - }, .node_offset_init_ty => |node_off| { const tree = try src_loc.file_scope.getTree(zcu); const parent_node = node_off.toAbsolute(src_loc.base_node); @@ -2019,6 +2024,20 @@ pub const SrcLoc = struct { } return tree.nodeToSpan(node); }, + .container_arg => { + const tree = try src_loc.file_scope.getTree(zcu); + const node = src_loc.base_node; + var buf: [2]Ast.Node.Index = undefined; + if (tree.fullContainerDecl(&buf, node)) |container_decl| { + const arg_node = container_decl.ast.arg.unwrap() orelse return tree.nodeToSpan(node); + return tree.nodeToSpan(arg_node); + } else if (tree.builtinCallParams(&buf, node)) |args| { + // Builtin calls (`@Enum` etc) should use the first argument. + return tree.nodeToSpan(if (args.len > 0) args[0] else node); + } else { + return tree.nodeToSpan(node); + } + }, .container_field_name, .container_field_value, .container_field_type, @@ -2027,8 +2046,38 @@ pub const SrcLoc = struct { const tree = try src_loc.file_scope.getTree(zcu); const node = src_loc.base_node; var buf: [2]Ast.Node.Index = undefined; - const container_decl = tree.fullContainerDecl(&buf, node) orelse + const container_decl = tree.fullContainerDecl(&buf, node) orelse { + // This could be a reification builtin. These are the args we care about: + // * `@Enum(_, _, names, values)` + // * `@Struct(_, _, names, types, values_and_aligns)` + // * `@Union(_, _, names, types, aligns)` + if (tree.builtinCallParams(&buf, node)) |args| { + const builtin_name = tree.tokenSlice(tree.firstToken(node)); + const arg_index: ?u3 = if (std.mem.eql(u8, builtin_name, "@Enum")) switch (src_loc.lazy) { + .container_field_name => 2, + .container_field_value => 3, + .container_field_type => null, + .container_field_align => null, + else => unreachable, + } else if (std.mem.eql(u8, builtin_name, "@Struct")) switch (src_loc.lazy) { + .container_field_name => 2, + .container_field_value => 4, + .container_field_type => 3, + .container_field_align => 4, + else => unreachable, + } else if (std.mem.eql(u8, builtin_name, "@Union")) switch (src_loc.lazy) { + .container_field_name => 2, + .container_field_value => 4, + .container_field_type => 3, + .container_field_align => null, + else => unreachable, + } else null; + if (arg_index) |i| { + if (args.len >= i) return tree.nodeToSpan(args[i]); + } + } return tree.nodeToSpan(node); + }; var cur_field_idx: usize = 0; for (container_decl.ast.members) |member_node| { @@ -2260,7 +2309,11 @@ pub const SrcLoc = struct { var param_it = full.iterate(tree); for (0..param_idx) |_| assert(param_it.next() != null); const param = param_it.next().?; - return tree.nodeToSpan(param.type_expr.?); + if (param.anytype_ellipsis3) |tok| { + return tree.tokenToSpan(tok); + } else { + return tree.nodeToSpan(param.type_expr.?); + } }, } } @@ -2482,10 +2535,6 @@ pub const LazySrcLoc = struct { node_offset_ptr_bitoffset: Ast.Node.Offset, /// The source location points to the host size of a pointer. node_offset_ptr_hostsize: Ast.Node.Offset, - /// The source location points to the tag type of an union or an enum. - node_offset_container_tag: Ast.Node.Offset, - /// The source location points to the default value of a field. - node_offset_field_default: Ast.Node.Offset, /// The source location points to the type of an array or struct initializer. node_offset_init_ty: Ast.Node.Offset, /// The source location points to the LHS of an assignment (or assign-op, e.g. `+=`). @@ -2530,6 +2579,11 @@ pub const LazySrcLoc = struct { fn_proto_param_type: FnProtoParam, array_cat_lhs: ArrayCat, array_cat_rhs: ArrayCat, + /// The source location points to the backing or tag type expression of + /// the container type declaration at the base node. + /// + /// For 'union(enum(T))', this points to 'T', not 'enum(T)'. + container_arg, /// The source location points to the name of the field at the given index /// of the container type declaration at the base node. container_field_name: u32, @@ -2685,10 +2739,10 @@ pub const LazySrcLoc = struct { .struct_init, .struct_init_ref => zir.extraData(Zir.Inst.StructInit, inst.data.pl_node.payload_index).data.abs_node, .struct_init_anon => zir.extraData(Zir.Inst.StructInitAnon, inst.data.pl_node.payload_index).data.abs_node, .extended => switch (inst.data.extended.opcode) { - .struct_decl => zir.extraData(Zir.Inst.StructDecl, inst.data.extended.operand).data.src_node, - .union_decl => zir.extraData(Zir.Inst.UnionDecl, inst.data.extended.operand).data.src_node, - .enum_decl => zir.extraData(Zir.Inst.EnumDecl, inst.data.extended.operand).data.src_node, - .opaque_decl => zir.extraData(Zir.Inst.OpaqueDecl, inst.data.extended.operand).data.src_node, + .struct_decl => zir.getStructDecl(zir_inst).src_node, + .union_decl => zir.getUnionDecl(zir_inst).src_node, + .enum_decl => zir.getEnumDecl(zir_inst).src_node, + .opaque_decl => zir.getOpaqueDecl(zir_inst).src_node, .reify_enum => zir.extraData(Zir.Inst.ReifyEnum, inst.data.extended.operand).data.node, .reify_struct => zir.extraData(Zir.Inst.ReifyStruct, inst.data.extended.operand).data.node, .reify_union => zir.extraData(Zir.Inst.ReifyUnion, inst.data.extended.operand).data.node, @@ -2715,36 +2769,34 @@ pub const LazySrcLoc = struct { }; } - /// Used to sort error messages, so that they're printed in a consistent order. - /// If an error is returned, a file could not be read in order to resolve a source location. - /// In that case, `bad_file_out` is populated, and sorting is impossible. - pub fn lessThan(lhs_lazy: LazySrcLoc, rhs_lazy: LazySrcLoc, zcu: *Zcu, bad_file_out: **Zcu.File) File.GetSourceError!bool { - const lhs_src = lhs_lazy.upgradeOrLost(zcu) orelse { + pub fn order(lhs: LazySrcLoc, rhs: LazySrcLoc, zcu: *Zcu) std.math.Order { + const lhs_resolved = lhs.upgradeOrLost(zcu) orelse { // LHS source location lost, so should never be referenced. Just sort it to the end. - return false; + return .gt; }; - const rhs_src = rhs_lazy.upgradeOrLost(zcu) orelse { + const rhs_resolved = rhs.upgradeOrLost(zcu) orelse { // RHS source location lost, so should never be referenced. Just sort it to the end. - return true; + return .lt; }; - if (lhs_src.file_scope != rhs_src.file_scope) { - const lhs_path = lhs_src.file_scope.path; - const rhs_path = rhs_src.file_scope.path; - if (lhs_path.root != rhs_path.root) { - return @intFromEnum(lhs_path.root) < @intFromEnum(rhs_path.root); - } - return std.mem.order(u8, lhs_path.sub_path, rhs_path.sub_path).compare(.lt); + if (lhs_resolved.file_scope != rhs_resolved.file_scope) { + const lhs_path = lhs_resolved.file_scope.path; + const rhs_path = rhs_resolved.file_scope.path; + return std.math.order(@intFromEnum(lhs_path.root), @intFromEnum(rhs_path.root)).differ() orelse + std.mem.order(u8, lhs_path.sub_path, rhs_path.sub_path).differ().?; } - - const lhs_span = lhs_src.span(zcu) catch |err| { - bad_file_out.* = lhs_src.file_scope; - return err; + const prev_prot = zcu.comp.io.swapCancelProtection(.blocked); + defer _ = zcu.comp.io.swapCancelProtection(prev_prot); + const lhs_span = lhs_resolved.span(zcu) catch |err| { + assert(err != error.Canceled); // we're protected + // Failed to read LHS, so we'll get a transient error. Just sort it to the end. + return .gt; }; - const rhs_span = rhs_src.span(zcu) catch |err| { - bad_file_out.* = rhs_src.file_scope; - return err; + const rhs_span = rhs_resolved.span(zcu) catch |err| { + assert(err != error.Canceled); // we're protected + // Failed to read RHS, so we'll get a transient error. Just sort it to the end. + return .lt; }; - return lhs_span.main < rhs_span.main; + return std.math.order(lhs_span.main, rhs_span.main); } }; @@ -2800,6 +2852,8 @@ pub fn deinit(zcu: *Zcu) void { zcu.analysis_in_progress.deinit(gpa); zcu.failed_analysis.deinit(gpa); zcu.transitive_failed_analysis.deinit(gpa); + zcu.dependency_loops.deinit(gpa); + zcu.dependency_loop_nodes.deinit(gpa); zcu.failed_codegen.deinit(gpa); zcu.failed_types.deinit(gpa); @@ -2830,12 +2884,10 @@ pub fn deinit(zcu: *Zcu) void { zcu.potentially_outdated.deinit(gpa); zcu.outdated.deinit(gpa); - zcu.outdated_ready.deinit(gpa); + zcu.outdated_ready.funcs.deinit(gpa); + zcu.outdated_ready.other.deinit(gpa); zcu.retryable_failures.deinit(gpa); - zcu.func_body_analysis_queued.deinit(gpa); - zcu.nav_val_analysis_queued.deinit(gpa); - zcu.test_functions.deinit(gpa); for (zcu.global_assembly.values()) |s| { @@ -3063,18 +3115,24 @@ pub fn markDependeeOutdated( marked_po: enum { not_marked_po, marked_po }, dependee: InternPool.Dependee, ) !void { - log.debug("outdated dependee: {f}", .{zcu.fmtDependee(dependee)}); + const gpa = zcu.comp.gpa; + deps_log.debug("outdated dependee: {f}", .{zcu.fmtDependee(dependee)}); var it = zcu.intern_pool.dependencyIterator(dependee); + if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io); + defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io); while (it.next()) |depender| { if (zcu.outdated.getPtr(depender)) |po_dep_count| { switch (marked_po) { .not_marked_po => {}, .marked_po => { po_dep_count.* -= 1; - log.debug("outdated {f} => already outdated {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* }); + deps_log.debug("outdated {f} => already outdated {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* }); if (po_dep_count.* == 0) { - log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)}); - try zcu.outdated_ready.put(zcu.gpa, depender, {}); + deps_log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)}); + switch (depender.unwrap()) { + .func => |func| try zcu.outdated_ready.funcs.put(gpa, func, {}), + else => try zcu.outdated_ready.other.put(gpa, depender, {}), + } } }, } @@ -3090,14 +3148,17 @@ pub fn markDependeeOutdated( }, }; try zcu.outdated.putNoClobber( - zcu.gpa, + gpa, depender, new_po_dep_count, ); - log.debug("outdated {f} => new outdated {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), new_po_dep_count }); + deps_log.debug("outdated {f} => new outdated {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), new_po_dep_count }); if (new_po_dep_count == 0) { - log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)}); - try zcu.outdated_ready.put(zcu.gpa, depender, {}); + deps_log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)}); + switch (depender.unwrap()) { + .func => |func| try zcu.outdated_ready.funcs.put(gpa, func, {}), + else => try zcu.outdated_ready.other.put(gpa, depender, {}), + } } // If this is a Decl and was not previously PO, we must recursively // mark dependencies on its tyval as PO. @@ -3109,17 +3170,27 @@ pub fn markDependeeOutdated( } pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void { - log.debug("up-to-date dependee: {f}", .{zcu.fmtDependee(dependee)}); + if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io); + defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io); + return markPoDependeeUpToDateInner(zcu, dependee); +} +/// Assumes that `zcu.outdated_lock` is already held exclusively. +fn markPoDependeeUpToDateInner(zcu: *Zcu, dependee: InternPool.Dependee) !void { + const gpa = zcu.comp.gpa; + deps_log.debug("up-to-date dependee: {f}", .{zcu.fmtDependee(dependee)}); var it = zcu.intern_pool.dependencyIterator(dependee); while (it.next()) |depender| { if (zcu.outdated.getPtr(depender)) |po_dep_count| { // This depender is already outdated, but it now has one // less PO dependency! po_dep_count.* -= 1; - log.debug("up-to-date {f} => {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* }); + deps_log.debug("up-to-date {f} => {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* }); if (po_dep_count.* == 0) { - log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)}); - try zcu.outdated_ready.put(zcu.gpa, depender, {}); + deps_log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)}); + switch (depender.unwrap()) { + .func => |func| try zcu.outdated_ready.funcs.put(gpa, func, {}), + else => try zcu.outdated_ready.other.put(gpa, depender, {}), + } } continue; } @@ -3132,11 +3203,11 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void { }; if (ptr.* > 1) { ptr.* -= 1; - log.debug("up-to-date {f} => {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), ptr.* }); + deps_log.debug("up-to-date {f} => {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), ptr.* }); continue; } - log.debug("up-to-date {f} => {f} po_deps=0 (up-to-date)", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender) }); + deps_log.debug("up-to-date {f} => {f} po_deps=0 (up-to-date)", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender) }); // This dependency is no longer PO, i.e. is known to be up-to-date. assert(zcu.potentially_outdated.swapRemove(depender)); @@ -3144,139 +3215,120 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void { // as no longer PO. switch (depender.unwrap()) { .@"comptime" => {}, - .nav_val => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_val = nav }), - .nav_ty => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_ty = nav }), - .type => |ty| try zcu.markPoDependeeUpToDate(.{ .interned = ty }), - .func => |func| try zcu.markPoDependeeUpToDate(.{ .interned = func }), - .memoized_state => |stage| try zcu.markPoDependeeUpToDate(.{ .memoized_state = stage }), + .nav_val => |nav| try zcu.markPoDependeeUpToDateInner(.{ .nav_val = nav }), + .nav_ty => |nav| try zcu.markPoDependeeUpToDateInner(.{ .nav_ty = nav }), + .type_layout => |ty| try zcu.markPoDependeeUpToDateInner(.{ .type_layout = ty }), + .struct_defaults => |ty| try zcu.markPoDependeeUpToDateInner(.{ .struct_defaults = ty }), + .func => |func| try zcu.markPoDependeeUpToDateInner(.{ .func_ies = func }), + .memoized_state => |stage| try zcu.markPoDependeeUpToDateInner(.{ .memoized_state = stage }), } } } /// Given a AnalUnit which is newly outdated or PO, mark all AnalUnits which may /// in turn be PO, due to a dependency on the original AnalUnit's tyval or IES. -fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUnit) !void { +/// +/// Assumes that `zcu.outdated_lock` is already held exclusively. +fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUnit) Allocator.Error!void { + const gpa = zcu.comp.gpa; const ip = &zcu.intern_pool; const dependee: InternPool.Dependee = switch (maybe_outdated.unwrap()) { .@"comptime" => return, // analysis of a comptime decl can't outdate any dependencies .nav_val => |nav| .{ .nav_val = nav }, .nav_ty => |nav| .{ .nav_ty = nav }, - .type => |ty| .{ .interned = ty }, - .func => |func_index| .{ .interned = func_index }, // IES + .type_layout => |ty| .{ .type_layout = ty }, + .struct_defaults => |ty| .{ .struct_defaults = ty }, + .func => |func_index| .{ .func_ies = func_index }, .memoized_state => |stage| .{ .memoized_state = stage }, }; - log.debug("potentially outdated dependee: {f}", .{zcu.fmtDependee(dependee)}); + deps_log.debug("potentially outdated dependee: {f}", .{zcu.fmtDependee(dependee)}); var it = ip.dependencyIterator(dependee); while (it.next()) |po| { if (zcu.outdated.getPtr(po)) |po_dep_count| { - // This dependency is already outdated, but it now has one more PO - // dependency. + // This dependency is already outdated, but it now has one more PO dependency. if (po_dep_count.* == 0) { - _ = zcu.outdated_ready.swapRemove(po); + switch (po.unwrap()) { + .func => |func| _ = zcu.outdated_ready.funcs.swapRemove(func), + else => _ = zcu.outdated_ready.other.swapRemove(po), + } } po_dep_count.* += 1; - log.debug("po {f} => {f} [outdated] po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), po_dep_count.* }); + deps_log.debug("po {f} => {f} [outdated] po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), po_dep_count.* }); continue; } if (zcu.potentially_outdated.getPtr(po)) |n| { // There is now one more PO dependency. n.* += 1; - log.debug("po {f} => {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), n.* }); + deps_log.debug("po {f} => {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), n.* }); continue; } - try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1); - log.debug("po {f} => {f} po_deps=1", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po) }); + try zcu.potentially_outdated.putNoClobber(gpa, po, 1); + deps_log.debug("po {f} => {f} po_deps=1", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po) }); // This AnalUnit was not already PO, so we must recursively mark its dependers as also PO. try zcu.markTransitiveDependersPotentiallyOutdated(po); } } +/// Selects an outdated `AnalUnit` to analyze next. Called from the main semantic analysis loop when +/// there is no work immediately queued. The unit is chosen such that it is unlikely to require any +/// recursive analysis (all of its previously-marked dependencies are already up-to-date), because +/// recursive analysis can cause over-analysis on incremental updates. pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit { - if (!zcu.comp.config.incremental) return null; - - if (zcu.outdated.count() == 0) { - // Any units in `potentially_outdated` must just be stuck in loops with one another: none of those - // units have had any outdated dependencies so far, and all of their remaining PO deps are triggered - // by other units in `potentially_outdated`. So, we can safety assume those units up-to-date. - zcu.potentially_outdated.clearRetainingCapacity(); - log.debug("findOutdatedToAnalyze: no outdated depender", .{}); - return null; + // We prioritize functions, because the sooner they get analyzed, the sooner they can be send to + // the codegen backend and linker, which are usually running in parallel (so this can increase + // parallelism). + // TODO: perhaps we should also experiment with *avoiding* functions if the codegen/link queue + // is backed up (for instance due to a very large function). That could help minimize blocking + // on the main thread in `CodegenTaskPool.start` waiting for the linker to catch up. + if (zcu.outdated_ready.funcs.count() > 0) { + const unit: AnalUnit = .wrap(.{ .func = zcu.outdated_ready.funcs.keys()[0] }); + log.debug("findOutdatedToAnalyze: {f}", .{zcu.fmtAnalUnit(unit)}); + return unit; } - // Our goal is to find an outdated AnalUnit which itself has no outdated or - // PO dependencies. Most of the time, such an AnalUnit will exist - we track - // them in the `outdated_ready` set for efficiency. However, this is not - // necessarily the case, since the Decl dependency graph may contain loops - // via mutually recursive definitions: - // pub const A = struct { b: *B }; - // pub const B = struct { b: *A }; - // In this case, we must defer to more complex logic below. - - if (zcu.outdated_ready.count() > 0) { - const unit = zcu.outdated_ready.keys()[0]; - log.debug("findOutdatedToAnalyze: trivial {f}", .{zcu.fmtAnalUnit(unit)}); + if (zcu.outdated_ready.other.count() > 0) { + const unit = zcu.outdated_ready.other.keys()[0]; + log.debug("findOutdatedToAnalyze: {f}", .{zcu.fmtAnalUnit(unit)}); return unit; } - // There is no single AnalUnit which is ready for re-analysis. Instead, we must assume that some - // AnalUnit with PO dependencies is outdated -- e.g. in the above example we arbitrarily pick one of - // A or B. We should definitely not select a function, since a function can't be responsible for the - // loop (IES dependencies can't have loops). We should also, of course, not select a `comptime` - // declaration, since you can't depend on those! - - // The choice of this unit could have a big impact on how much total analysis we perform, since - // if analysis concludes any dependencies on its result are up-to-date, then other PO AnalUnit - // may be resolved as up-to-date. To hopefully avoid doing too much work, let's find a unit - // which the most things depend on - the idea is that this will resolve a lot of loops (but this - // is only a heuristic). - - log.debug("findOutdatedToAnalyze: no trivial ready, using heuristic; {d} outdated, {d} PO", .{ - zcu.outdated.count(), - zcu.potentially_outdated.count(), - }); - - const ip = &zcu.intern_pool; + // Usually, getting here means that everything is up-to-date, so there is no more work to do. We + // will see that `zcu.outdated` and `zcu.potentially_outdated` are both empty. + // + // However, if a previous update had a dependency loop compile error, there is a cycle in the + // dependency graph (which is usually acyclic), which can cause a scenario where no unit appears + // to be ready, because they're all waiting for the next in the loop to be up-to-date. In that + // case, we usually have to just bite the bullet and analyze one of them. An exception is if + // `zcu.outdated` is empty but `zcu.potentially_outdated` is non-empty: in that case, the only + // possible situation is a cycle where everything is actually up-to-date, so we can clear out + // `zcu.potentially_outdated` and we are done. - var chosen_unit: ?AnalUnit = null; - var chosen_unit_dependers: u32 = undefined; - - inline for (.{ zcu.outdated.keys(), zcu.potentially_outdated.keys() }) |outdated_units| { - for (outdated_units) |unit| { - var n: u32 = 0; - var it = ip.dependencyIterator(switch (unit.unwrap()) { - .func => continue, // a `func` definitely can't be causing the loop so it is a bad choice - .@"comptime" => continue, // a `comptime` block can't even be depended on so it is a terrible choice - .type => |ty| .{ .interned = ty }, - .nav_val => |nav| .{ .nav_val = nav }, - .nav_ty => |nav| .{ .nav_ty = nav }, - .memoized_state => { - // If we've hit a loop and some `.memoized_state` is outdated, we should make that choice eagerly. - // In general, it's good to resolve this early on, since -- for instance -- almost every function - // references the panic handler. - return unit; - }, - }); - while (it.next()) |_| n += 1; + if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io); + defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io); - if (chosen_unit == null or n > chosen_unit_dependers) { - chosen_unit = unit; - chosen_unit_dependers = n; - } - } + if (zcu.outdated.count() == 0) { + // Everything is up-to-date. There could be lingering entries in `zcu.potentially_outdated` + // from a dependency loop on a previous update. + zcu.potentially_outdated.clearRetainingCapacity(); + log.debug("findOutdatedToAnalyze: all up-to-date", .{}); + return null; } - log.debug("findOutdatedToAnalyze: heuristic returned '{f}' ({d} dependers)", .{ - zcu.fmtAnalUnit(chosen_unit.?), - chosen_unit_dependers, + const unit = zcu.outdated.keys()[0]; + log.debug("findOutdatedToAnalyze: dependency loop affecting {d} units, selected {f}", .{ + zcu.outdated.count(), + zcu.fmtAnalUnit(unit), }); - - return chosen_unit.?; + return unit; } /// During an incremental update, before semantic analysis, call this to flush all values from /// `retryable_failures` and mark them as outdated so they get re-analyzed. pub fn flushRetryableFailures(zcu: *Zcu) !void { - const gpa = zcu.gpa; + const comp = zcu.comp; + const gpa = comp.gpa; + if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(comp.io); + defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(comp.io); for (zcu.retryable_failures.items) |depender| { if (zcu.outdated.contains(depender)) continue; if (zcu.potentially_outdated.fetchSwapRemove(depender)) |kv| { @@ -3350,12 +3402,59 @@ pub fn mapOldZirToNew( } while (match_stack.pop()) |match_item| { - // First, a check: if the number of captures of this type has changed, we can't map it, because - // we wouldn't know how to correlate type information with the last update. - // Synchronizes with logic in `Zcu.PerThread.recreateStructType` etc. - if (old_zir.typeCapturesLen(match_item.old_inst) != new_zir.typeCapturesLen(match_item.new_inst)) { - // Don't map this type or anything within it. - continue; + // There are some properties of type declarations which cannot change across incremental + // updates. If they have, we need to ignore this mapping. These properties are essentially + // everything passed into `InternPool.getDeclaredStructType` (likewise for unions, enums, + // and opaques). + const old_tag = old_zir.instructions.items(.data)[@intFromEnum(match_item.old_inst)].extended.opcode; + const new_tag = new_zir.instructions.items(.data)[@intFromEnum(match_item.new_inst)].extended.opcode; + if (old_tag != new_tag) continue; + switch (old_tag) { + .struct_decl => { + const old = old_zir.getStructDecl(match_item.old_inst); + const new = new_zir.getStructDecl(match_item.new_inst); + if (old.captures.len != new.captures.len) continue; + if (old.field_names.len != new.field_names.len) continue; + if (old.layout != new.layout) continue; + const old_any_field_aligns = old.field_align_body_lens != null; + const old_any_field_defaults = old.field_default_body_lens != null; + const old_any_comptime_fields = old.field_comptime_bits != null; + const old_explicit_backing_int = old.backing_int_type_body != null; + const new_any_field_aligns = new.field_align_body_lens != null; + const new_any_field_defaults = new.field_default_body_lens != null; + const new_any_comptime_fields = new.field_comptime_bits != null; + const new_explicit_backing_int = new.backing_int_type_body != null; + if (old_any_field_aligns != new_any_field_aligns) continue; + if (old_any_field_defaults != new_any_field_defaults) continue; + if (old_any_comptime_fields != new_any_comptime_fields) continue; + if (old_explicit_backing_int != new_explicit_backing_int) continue; + }, + .union_decl => { + const old = old_zir.getUnionDecl(match_item.old_inst); + const new = new_zir.getUnionDecl(match_item.new_inst); + if (old.captures.len != new.captures.len) continue; + if (old.field_names.len != new.field_names.len) continue; + if (old.kind != new.kind) continue; + const old_any_field_aligns = old.field_align_body_lens != null; + const new_any_field_aligns = new.field_align_body_lens != null; + if (old_any_field_aligns != new_any_field_aligns) continue; + }, + .enum_decl => { + const old = old_zir.getEnumDecl(match_item.old_inst); + const new = new_zir.getEnumDecl(match_item.new_inst); + if (old.captures.len != new.captures.len) continue; + if (old.field_names.len != new.field_names.len) continue; + if (old.nonexhaustive != new.nonexhaustive) continue; + const old_explicit_tag_type = old.tag_type_body != null; + const new_explicit_tag_type = new.tag_type_body != null; + if (old_explicit_tag_type != new_explicit_tag_type) continue; + }, + .opaque_decl => { + const old = old_zir.getOpaqueDecl(match_item.old_inst); + const new = new_zir.getOpaqueDecl(match_item.new_inst); + if (old.captures.len != new.captures.len) continue; + }, + else => unreachable, } // Match the namespace declaration itself @@ -3377,25 +3476,21 @@ pub fn mapOldZirToNew( var comptime_decls: std.ArrayList(Zir.Inst.Index) = .empty; defer comptime_decls.deinit(gpa); - { - var old_decl_it = old_zir.declIterator(match_item.old_inst); - while (old_decl_it.next()) |old_decl_inst| { - const old_decl = old_zir.getDeclaration(old_decl_inst); - switch (old_decl.kind) { - .@"comptime" => try comptime_decls.append(gpa, old_decl_inst), - .unnamed_test => try unnamed_tests.append(gpa, old_decl_inst), - .@"test" => try named_tests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst), - .decltest => try named_decltests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst), - .@"const", .@"var" => try named_decls.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst), - } + for (old_zir.typeDecls(match_item.old_inst)) |old_decl_inst| { + const old_decl = old_zir.getDeclaration(old_decl_inst); + switch (old_decl.kind) { + .@"comptime" => try comptime_decls.append(gpa, old_decl_inst), + .unnamed_test => try unnamed_tests.append(gpa, old_decl_inst), + .@"test" => try named_tests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst), + .decltest => try named_decltests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst), + .@"const", .@"var" => try named_decls.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst), } } var unnamed_test_idx: u32 = 0; var comptime_decl_idx: u32 = 0; - var new_decl_it = new_zir.declIterator(match_item.new_inst); - while (new_decl_it.next()) |new_decl_inst| { + for (new_zir.typeDecls(match_item.new_inst)) |new_decl_inst| { const new_decl = new_zir.getDeclaration(new_decl_inst); // Attempt to match this to a declaration in the old ZIR: // * For named declarations (`const`/`var`/`fn`), we match based on name. @@ -3474,47 +3569,93 @@ pub fn mapOldZirToNew( /// The caller is responsible for ensuring the function decl itself is already /// analyzed, and for ensuring it can exist at runtime (see /// `Type.fnHasRuntimeBitsSema`). This function does *not* guarantee that the body -/// will be analyzed when it returns: for that, see `ensureFuncBodyAnalyzed`. -pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func_index: InternPool.Index) !void { +/// will be analyzed when it returns: for that, see `PerThread.ensureFuncBodyUpToDate`. +pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func: InternPool.Index) !void { + const comp = zcu.comp; + const gpa = comp.gpa; + const io = comp.io; const ip = &zcu.intern_pool; + assert(func == ip.unwrapCoercedFunc(func)); // analyze the body of the original function, not a coerced one + if (ip.setWantRuntimeFnAnalysis(io, func)) { + // This is the first reference to this function, so we must ensure it will be analyzed. + if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io); + defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io); + try zcu.outdated.ensureUnusedCapacity(gpa, 1); + try zcu.outdated_ready.funcs.ensureUnusedCapacity(gpa, 1); + zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .func = func }), 0); + zcu.outdated_ready.funcs.putAssumeCapacityNoClobber(func, {}); + } +} - const func = zcu.funcInfo(func_index); - - assert(func.ty == func.uncoerced_ty); // analyze the body of the original function, not a coerced one +pub fn ensureNavValAnalysisQueued(zcu: *Zcu, nav: InternPool.Nav.Index) !void { + const comp = zcu.comp; + const gpa = comp.gpa; + const io = comp.io; + const ip = &zcu.intern_pool; + if (ip.setWantNavAnalysis(io, nav)) { + // This is the first reference to this function, so we must ensure it will be analyzed. + if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io); + defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io); + try zcu.outdated.ensureUnusedCapacity(gpa, 2); + try zcu.outdated_ready.other.ensureUnusedCapacity(gpa, 2); + zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .nav_val = nav }), 0); + zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .nav_ty = nav }), 0); + zcu.outdated_ready.other.putAssumeCapacityNoClobber(.wrap(.{ .nav_val = nav }), {}); + zcu.outdated_ready.other.putAssumeCapacityNoClobber(.wrap(.{ .nav_ty = nav }), {}); + } +} - if (zcu.func_body_analysis_queued.contains(func_index)) return; +/// Called when an `InternPool.ComptimeUnit` is first created to mark it as outdated so that it will +/// be semantically analyzed. +pub fn queueComptimeUnitAnalysis(zcu: *Zcu, cu: InternPool.ComptimeUnit.Id) Allocator.Error!void { + const comp = zcu.comp; + const gpa = comp.gpa; + const io = comp.io; + const unit: AnalUnit = .wrap(.{ .@"comptime" = cu }); + if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(io); + defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(io); + try zcu.outdated.ensureUnusedCapacity(gpa, 1); + try zcu.outdated_ready.other.ensureUnusedCapacity(gpa, 1); + zcu.outdated.putAssumeCapacityNoClobber(unit, 0); + zcu.outdated_ready.other.putAssumeCapacityNoClobber(unit, {}); +} - if (func.analysisUnordered(ip).is_analyzed) { - if (!zcu.outdated.contains(.wrap(.{ .func = func_index })) and - !zcu.potentially_outdated.contains(.wrap(.{ .func = func_index }))) - { - // This function has been analyzed before and is definitely up-to-date. - return; +/// If `unit` was marked as outdated or porentially outdated, clears that status and returns `true`. +/// Otherwise, returns `false`. +pub fn clearOutdatedState(zcu: *Zcu, unit: AnalUnit) bool { + const io = zcu.comp.io; + if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(io); + defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(io); + if (zcu.outdated.fetchSwapRemove(unit)) |kv| { + const was_ready = switch (unit.unwrap()) { + .func => |func| zcu.outdated_ready.funcs.swapRemove(func), + else => zcu.outdated_ready.other.swapRemove(unit), + }; + if (kv.value == 0) { + assert(was_ready); + } else { + assert(!was_ready); } + return true; + } else if (zcu.potentially_outdated.swapRemove(unit)) { + return true; + } else { + return false; } - - try zcu.func_body_analysis_queued.ensureUnusedCapacity(zcu.gpa, 1); - try zcu.comp.queueJob(.{ .analyze_func = func_index }); - zcu.func_body_analysis_queued.putAssumeCapacityNoClobber(func_index, {}); } -pub fn ensureNavValAnalysisQueued(zcu: *Zcu, nav_id: InternPool.Nav.Index) !void { - const ip = &zcu.intern_pool; +/// This function takes a `*const Zcu` and `@constCast`s it so that it can be called from functions +/// in `Type` which otherwise do not modify the `Zcu`. +pub fn assertUpToDate(zcu: *const Zcu, unit: AnalUnit) void { + if (!std.debug.runtime_safety) return; - if (zcu.nav_val_analysis_queued.contains(nav_id)) return; + const io = zcu.comp.io; - if (ip.getNav(nav_id).status == .fully_resolved) { - if (!zcu.outdated.contains(.wrap(.{ .nav_val = nav_id })) and - !zcu.potentially_outdated.contains(.wrap(.{ .nav_val = nav_id }))) - { - // This `Nav` has been analyzed before and is definitely up-to-date. - return; - } - } + @constCast(zcu).outdated_lock.lockSharedUncancelable(io); + defer @constCast(zcu).outdated_lock.unlockShared(io); - try zcu.nav_val_analysis_queued.ensureUnusedCapacity(zcu.gpa, 1); - try zcu.comp.queueJob(.{ .analyze_comptime_unit = .wrap(.{ .nav_val = nav_id }) }); - zcu.nav_val_analysis_queued.putAssumeCapacityNoClobber(nav_id, {}); + assert(!zcu.outdated.contains(unit)); + assert(!zcu.potentially_outdated.contains(unit)); } pub const ImportResult = struct { @@ -3533,56 +3674,83 @@ pub const ImportResult = struct { module: ?*Package.Module, }; -/// Delete all the Export objects that are caused by this `AnalUnit`. Re-analysis of -/// this `AnalUnit` will cause them to be re-created (or not). -pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { - const gpa = zcu.gpa; +/// Prepares `unit` for re-analysis by clearing all of the following state: +/// * Compile errors associated with `unit` +/// * Compile logs associated with `unit` +/// * Exports performed by `unit` +/// * Dependencies from `unit` on other things +/// * References from `unit` to other units +/// Delete all references in `reference_table` which are caused by `unit`, and all dependencies it +/// has. Called in preparation for re-analysis, which will recreate references and dependencies. +/// Re-analysis of the `AnalUnit` will cause appropriate references to be recreated. +pub fn resetUnit(zcu: *Zcu, unit: AnalUnit) void { + const gpa = zcu.comp.gpa; - const exports_base, const exports_len = if (zcu.single_exports.fetchSwapRemove(anal_unit)) |kv| - .{ @intFromEnum(kv.value), 1 } - else if (zcu.multi_exports.fetchSwapRemove(anal_unit)) |info| - .{ info.value.index, info.value.len } - else + if (!dev.env.supports(.incremental)) { + // This is the first time `unit` is being analyzed, so there is no stale data to clear. return; + } - const exports = zcu.all_exports.items[exports_base..][0..exports_len]; + // Compile errors + if (zcu.failed_analysis.fetchSwapRemove(unit)) |kv| { + kv.value.destroy(gpa); + } else if (zcu.dependency_loop_nodes.swapRemove(unit)) { + _ = zcu.dependency_loops.swapRemove(unit); + _ = zcu.transitive_failed_analysis.swapRemove(unit); + } else { + _ = zcu.transitive_failed_analysis.swapRemove(unit); + } - // In an only-c build, we're guaranteed to never use incremental compilation, so there are - // guaranteed not to be any exports in the output file that need deleting (since we only call - // `updateExports` on flush). - // This case is needed because in some rare edge cases, `Sema` wants to add and delete exports - // within a single update. - if (dev.env.supports(.incremental)) { - for (exports, exports_base..) |exp, export_index_usize| { - const export_idx: Export.Index = @enumFromInt(export_index_usize); + // Compile logs + if (zcu.compile_logs.fetchSwapRemove(unit)) |kv| { + var opt_line_idx = kv.value.first_line.toOptional(); + while (opt_line_idx.unwrap()) |line_idx| { + zcu.free_compile_log_lines.append(gpa, line_idx) catch { + // This space will be reused eventually, so we need not propagate this error. + // Just leak it for now, and let GC reclaim it later on. + break; + }; + opt_line_idx = line_idx.get(zcu).next; + } + } + + // Exports + exports: { + const base: u32, const len: u32 = index: { + if (zcu.single_exports.fetchSwapRemove(unit)) |kv| { + break :index .{ @intFromEnum(kv.value), 1 }; + } + if (zcu.multi_exports.fetchSwapRemove(unit)) |kv| { + break :index .{ kv.value.index, kv.value.len }; + } + break :exports; + }; + for (zcu.all_exports.items[base..][0..len], base..) |exp, exp_index_usize| { + const exp_index: Export.Index = @enumFromInt(exp_index_usize); if (zcu.comp.bin_file) |lf| { lf.deleteExport(exp.exported, exp.opts.name); } - if (zcu.failed_exports.fetchSwapRemove(export_idx)) |failed_kv| { + if (zcu.failed_exports.fetchSwapRemove(exp_index)) |failed_kv| { failed_kv.value.destroy(gpa); } } + zcu.free_exports.ensureUnusedCapacity(gpa, len) catch { + // This space will be reused eventually, so we need not propagate this error. + // Just leak it for now, and let GC reclaim it later on. + break :exports; + }; + for (base..base + len) |exp_index| { + zcu.free_exports.appendAssumeCapacity(@enumFromInt(exp_index)); + } } - zcu.free_exports.ensureUnusedCapacity(gpa, exports_len) catch { - // This space will be reused eventually, so we need not propagate this error. - // Just leak it for now, and let GC reclaim it later on. - return; - }; - for (exports_base..exports_base + exports_len) |export_idx| { - zcu.free_exports.appendAssumeCapacity(@enumFromInt(export_idx)); - } -} - -/// Delete all references in `reference_table` which are caused by this `AnalUnit`. -/// Re-analysis of the `AnalUnit` will cause appropriate references to be recreated. -pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void { - const gpa = zcu.gpa; + // Dependencies + zcu.intern_pool.removeDependenciesForDepender(gpa, unit); + // References zcu.clearCachedResolvedReferences(); - unit_refs: { - const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse break :unit_refs; + const kv = zcu.reference_table.fetchSwapRemove(unit) orelse break :unit_refs; var idx = kv.value; while (idx != std.math.maxInt(u32)) { @@ -3610,9 +3778,8 @@ pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void { } } } - type_refs: { - const kv = zcu.type_reference_table.fetchSwapRemove(anal_unit) orelse break :type_refs; + const kv = zcu.type_reference_table.fetchSwapRemove(unit) orelse break :type_refs; var idx = kv.value; while (idx != std.math.maxInt(u32)) { @@ -3626,22 +3793,6 @@ pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void { } } -/// Delete all compile logs performed by this `AnalUnit`. -/// Re-analysis of the `AnalUnit` will cause logs to be rediscovered. -pub fn deleteUnitCompileLogs(zcu: *Zcu, anal_unit: AnalUnit) void { - const kv = zcu.compile_logs.fetchSwapRemove(anal_unit) orelse return; - const gpa = zcu.gpa; - var opt_line_idx = kv.value.first_line.toOptional(); - while (opt_line_idx.unwrap()) |line_idx| { - zcu.free_compile_log_lines.append(gpa, line_idx) catch { - // This space will be reused eventually, so we need not propagate this error. - // Just leak it for now, and let GC reclaim it later on. - return; - }; - opt_line_idx = line_idx.get(zcu).next; - } -} - pub fn addInlineReferenceFrame(zcu: *Zcu, frame: InlineReferenceFrame) Allocator.Error!Zcu.InlineReferenceFrame.Index { const frame_idx: InlineReferenceFrame.Index = zcu.free_inline_reference_frames.pop() orelse idx: { _ = try zcu.inline_reference_frames.addOne(zcu.gpa); @@ -3851,9 +4002,9 @@ pub const AtomicPtrAlignmentDiagnostics = struct { max_bits: u16 = undefined, }; -/// If ABI alignment of `ty` is OK for atomic operations, returns 0. -/// Otherwise returns the alignment required on a pointer for the target -/// to perform atomic operations. +/// Returns the alignment required for the target to perform atomic operations on type `ty` (that +/// is, the required align attribute on the pointer). If the ABI alignment of `ty` is sufficient, +/// returns `.none`. // TODO this function does not take into account CPU features, which can affect // this value. Audit this! pub fn atomicPtrAlignment( @@ -3908,8 +4059,7 @@ pub fn atomicPtrAlignment( return error.BadType; } -/// Returns null in the following cases: -/// * Not a struct. +/// Returns null if `ty` is not a struct. pub fn typeToStruct(zcu: *const Zcu, ty: Type) ?InternPool.LoadedStructType { if (ty.ip_index == .none) return null; const ip = &zcu.intern_pool; @@ -3936,7 +4086,6 @@ pub fn structPackedFieldBitOffset( ) u16 { const ip = &zcu.intern_pool; assert(struct_type.layout == .@"packed"); - assert(struct_type.haveLayout(ip)); var bit_sum: u64 = 0; for (0..struct_type.field_types.len) |i| { if (i == field_index) { @@ -3995,8 +4144,10 @@ pub const UnionLayout = struct { pub fn unionTagFieldIndex(zcu: *const Zcu, loaded_union: InternPool.LoadedUnionType, enum_tag: Value) ?u32 { const ip = &zcu.intern_pool; if (enum_tag.toIntern() == .none) return null; - assert(ip.typeOf(enum_tag.toIntern()) == loaded_union.enum_tag_ty); - return loaded_union.loadTagType(ip).tagValueIndex(ip, enum_tag.toIntern()); + const enum_tag_key = ip.indexToKey(enum_tag.toIntern()).enum_tag; + assert(enum_tag_key.ty == loaded_union.enum_tag_type); + const loaded_enum = ip.loadEnumType(loaded_union.enum_tag_type); + return loaded_enum.tagValueIndex(ip, enum_tag_key.int); } pub const ResolvedReference = struct { @@ -4012,13 +4163,13 @@ pub const ResolvedReference = struct { /// If an `AnalUnit` is not in the returned map, it is unreferenced. /// The returned hashmap is owned by the `Zcu`, so should not be freed by the caller. /// This hashmap is cached, so repeated calls to this function are cheap. -pub fn resolveReferences(zcu: *Zcu) !*const std.AutoArrayHashMapUnmanaged(AnalUnit, ?ResolvedReference) { +pub fn resolveReferences(zcu: *Zcu) Allocator.Error!*const std.AutoArrayHashMapUnmanaged(AnalUnit, ?ResolvedReference) { if (zcu.resolved_references == null) { zcu.resolved_references = try zcu.resolveReferencesInner(); } return &zcu.resolved_references.?; } -fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?ResolvedReference) { +fn resolveReferencesInner(zcu: *Zcu) Allocator.Error!std.AutoArrayHashMapUnmanaged(AnalUnit, ?ResolvedReference) { const gpa = zcu.gpa; const comp = zcu.comp; const ip = &zcu.intern_pool; @@ -4049,32 +4200,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R const referencer = types.values()[type_idx]; type_idx += 1; - log.debug("handle type '{f}'", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}); - - // If this type undergoes type resolution, the corresponding `AnalUnit` is automatically referenced. - const has_resolution: bool = switch (ip.indexToKey(ty)) { - .struct_type, .union_type => true, - .enum_type => |k| k != .generated_tag, - .opaque_type => false, - else => unreachable, - }; - if (has_resolution) { - // this should only be referenced by the type - const unit: AnalUnit = .wrap(.{ .type = ty }); - try units.putNoClobber(gpa, unit, referencer); - } - - // If this is a union with a generated tag, its tag type is automatically referenced. - // We don't add this reference for non-generated tags, as those will already be referenced via the union's type resolution, with a better source location. - if (zcu.typeToUnion(Type.fromInterned(ty))) |union_obj| { - const tag_ty = union_obj.enum_tag_ty; - if (tag_ty != .none) { - if (ip.indexToKey(tag_ty).enum_type == .generated_tag) { - const gop = try types.getOrPut(gpa, tag_ty); - if (!gop.found_existing) gop.value_ptr.* = referencer; - } - } - } + refs_log.debug("handle type '{f}'", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}); // Queue any decls within this type which would be automatically analyzed. // Keep in sync with analysis queueing logic in `Zcu.PerThread.ScanDeclIter.scanDecl`. @@ -4084,7 +4210,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R const unit: AnalUnit = .wrap(.{ .@"comptime" = cu }); const gop = try units.getOrPut(gpa, unit); if (!gop.found_existing) { - log.debug("type '{f}': ref comptime %{}", .{ + refs_log.debug("type '{f}': ref comptime %{}", .{ Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(ip.getComptimeUnit(cu).zir_index.resolve(ip) orelse continue), }); @@ -4118,7 +4244,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R { const gop = try units.getOrPut(gpa, .wrap(.{ .nav_val = nav_id })); if (!gop.found_existing) { - log.debug("type '{f}': ref test %{}", .{ + refs_log.debug("type '{f}': ref test %{}", .{ Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(inst_info.inst), }); @@ -4141,7 +4267,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R const unit: AnalUnit = .wrap(.{ .nav_val = nav }); const gop = try units.getOrPut(gpa, unit); if (!gop.found_existing) { - log.debug("type '{f}': ref named %{}", .{ + refs_log.debug("type '{f}': ref named %{}", .{ Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(inst_info.inst), }); @@ -4158,7 +4284,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R const unit: AnalUnit = .wrap(.{ .nav_val = nav }); const gop = try units.getOrPut(gpa, unit); if (!gop.found_existing) { - log.debug("type '{f}': ref named %{}", .{ + refs_log.debug("type '{f}': ref named %{}", .{ Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(inst_info.inst), }); @@ -4173,18 +4299,25 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R unit_idx += 1; // `nav_val` and `nav_ty` reference each other *implicitly* to save memory. + // Likewise for `type_layout` and `struct_defaults` of a struct type. queue_paired: { const other: AnalUnit = .wrap(switch (unit.unwrap()) { .nav_val => |n| .{ .nav_ty = n }, .nav_ty => |n| .{ .nav_val = n }, - .@"comptime", .type, .func, .memoized_state => break :queue_paired, + .struct_defaults => |ty| .{ .type_layout = ty }, + .type_layout => |ty| switch (ip.indexToKey(ty)) { + .struct_type => .{ .struct_defaults = ty }, + .union_type, .enum_type, .opaque_type => break :queue_paired, + else => unreachable, + }, + .@"comptime", .func, .memoized_state => break :queue_paired, }); const gop = try units.getOrPut(gpa, other); if (gop.found_existing) break :queue_paired; - gop.value_ptr.* = units.values()[unit_idx]; // same reference location + gop.value_ptr.* = units.values()[unit_idx - 1]; // same reference location } - log.debug("handle unit '{f}'", .{zcu.fmtAnalUnit(unit)}); + refs_log.debug("handle unit '{f}'", .{zcu.fmtAnalUnit(unit)}); if (zcu.reference_table.get(unit)) |first_ref_idx| { assert(first_ref_idx != std.math.maxInt(u32)); @@ -4193,7 +4326,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R const ref = zcu.all_references.items[ref_idx]; const gop = try units.getOrPut(gpa, ref.referenced); if (!gop.found_existing) { - log.debug("unit '{f}': ref unit '{f}'", .{ + refs_log.debug("unit '{f}': ref unit '{f}'", .{ zcu.fmtAnalUnit(unit), zcu.fmtAnalUnit(ref.referenced), }); @@ -4213,7 +4346,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?R const ref = zcu.all_type_references.items[ref_idx]; const gop = try types.getOrPut(gpa, ref.referenced); if (!gop.found_existing) { - log.debug("unit '{f}': ref type '{f}'", .{ + refs_log.debug("unit '{f}': ref type '{f}'", .{ zcu.fmtAnalUnit(unit), Type.fromInterned(ref.referenced).containerTypeName(ip).fmt(ip), }); @@ -4298,6 +4431,16 @@ pub fn navFileScope(zcu: *Zcu, nav: InternPool.Nav.Index) *File { return zcu.fileByIndex(zcu.navFileScopeIndex(nav)); } +pub fn navAlignment(zcu: *Zcu, nav_index: InternPool.Nav.Index) InternPool.Alignment { + const ty: Type, const alignment = switch (zcu.intern_pool.getNav(nav_index).status) { + .unresolved => unreachable, + .type_resolved => |r| .{ .fromInterned(r.type), r.alignment }, + .fully_resolved => |r| .{ Value.fromInterned(r.val).typeOf(zcu), r.alignment }, + }; + if (alignment != .none) return alignment; + return ty.abiAlignment(zcu); +} + pub fn fmtAnalUnit(zcu: *Zcu, unit: AnalUnit) std.fmt.Alt(FormatAnalUnit, formatAnalUnit) { return .{ .data = .{ .unit = unit, .zcu = zcu } }; } @@ -4305,11 +4448,7 @@ pub fn fmtDependee(zcu: *Zcu, d: InternPool.Dependee) std.fmt.Alt(FormatDependee return .{ .data = .{ .dependee = d, .zcu = zcu } }; } -const FormatAnalUnit = struct { - unit: AnalUnit, - zcu: *Zcu, -}; - +const FormatAnalUnit = struct { unit: AnalUnit, zcu: *const Zcu }; fn formatAnalUnit(data: FormatAnalUnit, writer: *Io.Writer) Io.Writer.Error!void { const zcu = data.zcu; const ip = &zcu.intern_pool; @@ -4323,9 +4462,8 @@ fn formatAnalUnit(data: FormatAnalUnit, writer: *Io.Writer) Io.Writer.Error!void return writer.print("comptime(inst=<lost> [{}])", .{@intFromEnum(cu_id)}); } }, - .nav_val => |nav| return writer.print("nav_val('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }), - .nav_ty => |nav| return writer.print("nav_ty('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }), - .type => |ty| return writer.print("ty('{f}' [{}])", .{ Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(ty) }), + .nav_val, .nav_ty => |nav, tag| return writer.print("{t}('{f}' [{}])", .{ tag, ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) }), + .type_layout, .struct_defaults => |ty, tag| return writer.print("{t}('{f}' [{}])", .{ tag, Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @intFromEnum(ty) }), .func => |func| { const nav = zcu.funcInfo(func).owner_nav; return writer.print("func('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(func) }); @@ -4334,8 +4472,7 @@ fn formatAnalUnit(data: FormatAnalUnit, writer: *Io.Writer) Io.Writer.Error!void } } -const FormatDependee = struct { dependee: InternPool.Dependee, zcu: *Zcu }; - +const FormatDependee = struct { dependee: InternPool.Dependee, zcu: *const Zcu }; fn formatDependee(data: FormatDependee, writer: *Io.Writer) Io.Writer.Error!void { const zcu = data.zcu; const ip = &zcu.intern_pool; @@ -4347,18 +4484,17 @@ fn formatDependee(data: FormatDependee, writer: *Io.Writer) Io.Writer.Error!void const file_path = zcu.fileByIndex(info.file).path; return writer.print("inst('{f}', %{d})", .{ file_path.fmt(zcu.comp), @intFromEnum(info.inst) }); }, - .nav_val => |nav| { + .nav_val, .nav_ty => |nav, tag| { const fqn = ip.getNav(nav).fqn; - return writer.print("nav_val('{f}')", .{fqn.fmt(ip)}); + return writer.print("{t}('{f}')", .{ tag, fqn.fmt(ip) }); }, - .nav_ty => |nav| { - const fqn = ip.getNav(nav).fqn; - return writer.print("nav_ty('{f}')", .{fqn.fmt(ip)}); + .type_layout, .struct_defaults => |ip_index, tag| { + const name = Type.fromInterned(ip_index).containerTypeName(ip); + return writer.print("{t}('{f}')", .{ tag, name.fmt(ip) }); }, - .interned => |ip_index| switch (ip.indexToKey(ip_index)) { - .struct_type, .union_type, .enum_type => return writer.print("type('{f}')", .{Type.fromInterned(ip_index).containerTypeName(ip).fmt(ip)}), - .func => |f| return writer.print("ies('{f}')", .{ip.getNav(f.owner_nav).fqn.fmt(ip)}), - else => unreachable, + .func_ies => |ip_index| { + const fqn = ip.getNav(ip.indexToKey(ip_index).func.owner_nav).fqn; + return writer.print("func_ies('{f}')", .{fqn.fmt(ip)}); }, .zon_file => |file| { const file_path = zcu.fileByIndex(file).path; @@ -4386,32 +4522,6 @@ fn formatDependee(data: FormatDependee, writer: *Io.Writer) Io.Writer.Error!void } } -/// Given the `InternPool.Index` of a function, set its resolved IES to `.none` if it -/// may be outdated. `Sema` should do this before ever loading a resolved IES. -pub fn maybeUnresolveIes(zcu: *Zcu, func_index: InternPool.Index) !void { - const unit = AnalUnit.wrap(.{ .func = func_index }); - if (zcu.outdated.contains(unit) or zcu.potentially_outdated.contains(unit)) { - // We're consulting the resolved IES now, but the function is outdated, so its - // IES may have changed. We have to assume the IES is outdated and set the resolved - // set back to `.none`. - // - // This will cause `PerThread.analyzeFnBody` to mark the IES as outdated when it's - // eventually hit. - // - // Since the IES needs to be resolved, the function body will now definitely need - // re-analysis (even if the IES turns out to be the same!), so mark it as - // definitely-outdated if it's only PO. - if (zcu.potentially_outdated.fetchSwapRemove(unit)) |kv| { - const gpa = zcu.gpa; - try zcu.outdated.putNoClobber(gpa, unit, kv.value); - if (kv.value == 0) { - try zcu.outdated_ready.put(gpa, unit, {}); - } - } - zcu.intern_pool.funcSetIesResolved(zcu.comp.io, func_index, .none); - } -} - pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enum) { ok, bad_arch: []const std.Target.Cpu.Arch, // value is allowed archs for cc @@ -4747,6 +4857,304 @@ fn explainWhyFileIsInModule( } } +pub fn addDependencyLoopErrors(zcu: *Zcu, eb: *std.zig.ErrorBundle.Wip) Allocator.Error!void { + const gpa = zcu.comp.gpa; + + const all_references = try zcu.resolveReferences(); + + var units: std.ArrayList(AnalUnit) = .empty; + defer units.deinit(gpa); + + // TODO: sort the dependency loops somehow to make the error bundle reproducible + for (zcu.dependency_loops.keys()) |arbitrary_unit| { + units.clearRetainingCapacity(); + + var cur = arbitrary_unit; + while (true) { + try units.append(gpa, cur); + cur = zcu.dependency_loop_nodes.get(cur).?.unit; + if (cur == arbitrary_unit) break; + } + + // `units` now contains all units in the loop. We need to pick a starting point somewhere + // along that loop to begin. We will pick whichever node has the shortest reference trace, + // because the other units may well just be referenced *by* that one! This is also likely + // to match the user's intuition for where the loop "starts". + var start_index: usize = 0; + var start_depth: u32 = depth: { + var depth: u32 = 0; + var opt_ref = all_references.get(units.items[0]) orelse { + // This dependency loop is actually unreferenced, so we don't need to emit a compile + // error at all! Move onto the next dependency loop. + continue; + }; + while (opt_ref) |ref| : (opt_ref = all_references.get(ref.referencer).?) depth += 1; + break :depth depth; + }; + for (units.items[1..], 1..) |unit, index| { + var depth: u32 = 0; + var opt_ref = all_references.get(unit).?; + while (opt_ref) |ref| : (opt_ref = all_references.get(ref.referencer).?) depth += 1; + if (depth < start_depth) { + start_index = index; + start_depth = depth; + } + } + + // Collect a reference trace for the start of the loop. + var ref_trace: std.ArrayList(std.zig.ErrorBundle.ReferenceTrace) = .empty; + defer ref_trace.deinit(gpa); + const frame_limit = zcu.comp.reference_trace orelse 0; + try zcu.populateReferenceTrace(units.items[start_index], frame_limit, eb, &ref_trace); + + if (units.items.len == 1) { + // Don't do a complicated message with multiple notes, just do a single error message. + assert(start_index == 0); + const root_msg = addDependencyLoopErrorLine(zcu, eb, units.items[start_index], ref_trace.items) catch |err| switch (err) { + error.AlreadyReported => return, // give up on the dep loop error + error.OutOfMemory => |e| return e, + }; + try eb.root_list.append(eb.gpa, root_msg); + continue; + } + + // Collect all notes first so we don't leave an incomplete root error message on `error.AlreadyReported`. + const note_buf = try gpa.alloc(std.zig.ErrorBundle.MessageIndex, units.items.len + 1); + defer gpa.free(note_buf); + note_buf[0] = addDependencyLoopErrorLine(zcu, eb, units.items[start_index], ref_trace.items) catch |err| switch (err) { + error.AlreadyReported => return, // give up on the dep loop error + error.OutOfMemory => |e| return e, + }; + for (units.items[start_index + 1 ..], note_buf[1 .. units.items.len - start_index]) |unit, *note| { + note.* = addDependencyLoopErrorLine(zcu, eb, unit, &.{}) catch |err| switch (err) { + error.AlreadyReported => return, // give up on the dep loop error + error.OutOfMemory => |e| return e, + }; + } + for (units.items[0..start_index], note_buf[units.items.len - start_index .. units.items.len]) |unit, *note| { + note.* = addDependencyLoopErrorLine(zcu, eb, unit, &.{}) catch |err| switch (err) { + error.AlreadyReported => return, // give up on the dep loop error + error.OutOfMemory => |e| return e, + }; + } + note_buf[units.items.len] = try eb.addErrorMessage(.{ + .msg = try eb.addString("eliminate any one of these dependencies to break the loop"), + .src_loc = .none, + }); + + try eb.addRootErrorMessage(.{ + .msg = try eb.printString("dependency loop with length {d}", .{units.items.len}), + .src_loc = .none, + .notes_len = @intCast(units.items.len + 1), + }); + const notes_start = try eb.reserveNotes(@intCast(units.items.len + 1)); + const notes: []std.zig.ErrorBundle.MessageIndex = @ptrCast(eb.extra.items[notes_start..]); + @memcpy(notes, note_buf); + } +} +fn addDependencyLoopErrorLine( + zcu: *Zcu, + eb: *std.zig.ErrorBundle.Wip, + source_unit: AnalUnit, + ref_trace: []const std.zig.ErrorBundle.ReferenceTrace, +) (Allocator.Error || error{AlreadyReported})!std.zig.ErrorBundle.MessageIndex { + const ip = &zcu.intern_pool; + const comp = zcu.comp; + + const fmt_source: std.fmt.Alt(FormatAnalUnit, formatDependencyLoopSourceUnit) = .{ .data = .{ + .unit = source_unit, + .zcu = zcu, + } }; + + const dep_node = zcu.dependency_loop_nodes.get(source_unit).?; + + const msg: std.zig.ErrorBundle.String = if (dep_node.unit == source_unit) switch (source_unit.unwrap()) { + .@"comptime" => unreachable, // cannot be involved in a dependency loop + .nav_ty, .nav_val => try eb.printString("{f} depends on itself here", .{fmt_source}), + .memoized_state => unreachable, // memoized_state definitely does not *directly* depend on itself + .func => try eb.printString("{f} uses its own inferred error set here", .{fmt_source}), + .type_layout => try eb.printString("{f} depends on itself {s}", .{ + fmt_source, + dep_node.reason.type_layout_reason.msg(), + }), + .struct_defaults => |ty| try eb.printString( + "default field values of '{f}' depend on themselves for initialization here", + .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}, + ), + } else switch (dep_node.unit.unwrap()) { + .@"comptime" => unreachable, // cannot be involved in a dependency loop + .nav_val => |nav| try eb.printString("{f} uses value of declaration '{f}' here", .{ + fmt_source, ip.getNav(nav).fqn.fmt(ip), + }), + .nav_ty => |nav| try eb.printString("{f} uses type of declaration '{f}' here", .{ + fmt_source, ip.getNav(nav).fqn.fmt(ip), + }), + .memoized_state => |stage| switch (stage) { + .panic => try eb.printString("{f} requires panic handler for call here", .{fmt_source}), + else => try eb.printString("{f} requires 'std.builtin' declarations here", .{fmt_source}), + }, + .func => |func| try eb.printString("{f} uses inferred error set of function '{f}' here", .{ + fmt_source, ip.getNav(zcu.funcInfo(func).owner_nav).fqn.fmt(ip), + }), + .type_layout => |ty| try eb.printString("{f} depends on type '{f}' {s}", .{ + fmt_source, + Type.fromInterned(ty).containerTypeName(ip).fmt(ip), + dep_node.reason.type_layout_reason.msg(), + }), + .struct_defaults => |ty| try eb.printString( + "{f} uses default field values of '{f}' here", + .{ fmt_source, Type.fromInterned(ty).containerTypeName(ip).fmt(ip) }, + ), + }; + + const src_loc = dep_node.reason.src.upgrade(zcu); + const source = src_loc.file_scope.getSource(zcu) catch |err| { + try Compilation.unableToLoadZcuFile(zcu, eb, src_loc.file_scope, err); + return error.AlreadyReported; + }; + const span = src_loc.span(zcu) catch |err| { + try Compilation.unableToLoadZcuFile(zcu, eb, src_loc.file_scope, err); + return error.AlreadyReported; + }; + const loc = std.zig.findLineColumn(source, span.main); + const eb_src = try eb.addSourceLocation(.{ + .src_path = try eb.printString("{f}", .{src_loc.file_scope.path.fmt(comp)}), + .span_start = span.start, + .span_main = span.main, + .span_end = span.end, + .line = @intCast(loc.line), + .column = @intCast(loc.column), + .source_line = try eb.addString(loc.source_line), + .reference_trace_len = @intCast(ref_trace.len), + }); + for (ref_trace) |rt| try eb.addReferenceTrace(rt); + return eb.addErrorMessage(.{ + .msg = msg, + .src_loc = eb_src, + }); +} +fn formatDependencyLoopSourceUnit(data: FormatAnalUnit, w: *Io.Writer) Io.Writer.Error!void { + const zcu = data.zcu; + const ip = &zcu.intern_pool; + switch (data.unit.unwrap()) { + .@"comptime" => unreachable, // cannot be involved in a dependency loop + .nav_val => |nav| try w.print("value of declaration '{f}'", .{ip.getNav(nav).fqn.fmt(ip)}), + .nav_ty => |nav| try w.print("type of declaration '{f}'", .{ip.getNav(nav).fqn.fmt(ip)}), + .memoized_state => |stage| switch (stage) { + .panic => try w.writeAll("panic handler"), + else => try w.writeAll("'std.builtin' declarations"), + }, + .type_layout => |ty| try w.print("type '{f}'", .{ + Type.fromInterned(ty).containerTypeName(ip).fmt(ip), + }), + .struct_defaults => |ty| try w.print("default field value of '{f}'", .{ + Type.fromInterned(ty).containerTypeName(ip).fmt(ip), + }), + .func => |func| try w.print("function '{f}'", .{ + ip.getNav(zcu.funcInfo(func).owner_nav).fqn.fmt(ip), + }), + } +} + +pub fn populateReferenceTrace( + zcu: *Zcu, + root: AnalUnit, + frame_limit: u32, + eb: *std.zig.ErrorBundle.Wip, + ref_trace: *std.ArrayList(std.zig.ErrorBundle.ReferenceTrace), +) Allocator.Error!void { + const ip = &zcu.intern_pool; + const gpa = zcu.comp.gpa; + + if (frame_limit == 0) return; + + const all_references = try zcu.resolveReferences(); + + var seen: std.AutoHashMapUnmanaged(InternPool.AnalUnit, void) = .empty; + defer seen.deinit(gpa); + + var referenced_by = root; + while (all_references.get(referenced_by)) |maybe_ref| { + const ref = maybe_ref orelse break; + const gop = try seen.getOrPut(gpa, ref.referencer); + if (gop.found_existing) break; + if (ref_trace.items.len < frame_limit) { + var last_call_src = ref.src; + var opt_inline_frame = ref.inline_frame; + while (opt_inline_frame.unwrap()) |inline_frame| { + const f = inline_frame.ptr(zcu).*; + const func_nav = ip.indexToKey(f.callee).func.owner_nav; + const func_name = ip.getNav(func_nav).name.toSlice(ip); + addReferenceTraceFrame(zcu, eb, ref_trace, func_name, last_call_src, true) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + error.AlreadyReported => { + // An incomplete reference trace isn't the end of the world; just cut it off. + return; + }, + }; + last_call_src = f.call_src; + opt_inline_frame = f.parent; + } + const root_name: ?[]const u8 = switch (ref.referencer.unwrap()) { + .@"comptime" => "comptime", + .nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip), + .type_layout, .struct_defaults => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip), + .func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip), + .memoized_state => null, + }; + if (root_name) |n| { + addReferenceTraceFrame(zcu, eb, ref_trace, n, last_call_src, false) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + error.AlreadyReported => { + // An incomplete reference trace isn't the end of the world; just cut it off. + return; + }, + }; + } + } + referenced_by = ref.referencer; + } + + if (seen.count() > ref_trace.items.len) { + try ref_trace.append(gpa, .{ + .decl_name = @intCast(seen.count() - ref_trace.items.len), + .src_loc = .none, + }); + } +} +fn addReferenceTraceFrame( + zcu: *Zcu, + eb: *std.zig.ErrorBundle.Wip, + ref_trace: *std.ArrayList(std.zig.ErrorBundle.ReferenceTrace), + name: []const u8, + lazy_src: Zcu.LazySrcLoc, + inlined: bool, +) error{ OutOfMemory, AlreadyReported }!void { + const gpa = zcu.gpa; + const src = lazy_src.upgrade(zcu); + const source = src.file_scope.getSource(zcu) catch |err| { + try Compilation.unableToLoadZcuFile(zcu, eb, src.file_scope, err); + return error.AlreadyReported; + }; + const span = src.span(zcu) catch |err| { + try Compilation.unableToLoadZcuFile(zcu, eb, src.file_scope, err); + return error.AlreadyReported; + }; + const loc = std.zig.findLineColumn(source, span.main); + try ref_trace.append(gpa, .{ + .decl_name = try eb.printString("{s}{s}", .{ name, if (inlined) " [inlined]" else "" }), + .src_loc = try eb.addSourceLocation(.{ + .src_path = try eb.printString("{f}", .{src.file_scope.path.fmt(zcu.comp)}), + .span_start = span.start, + .span_main = span.main, + .span_end = span.end, + .line = @intCast(loc.line), + .column = @intCast(loc.column), + .source_line = 0, + }), + }); +} + const TrackedUnitSema = struct { /// `null` means we created the node, so should end it. old_name: ?[std.Progress.Node.max_name_len]u8, diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig @@ -27,7 +27,9 @@ const introspect = @import("../introspect.zig"); const Module = @import("../Package.zig").Module; const Sema = @import("../Sema.zig"); const target_util = @import("../target.zig"); -const trace = @import("../tracy.zig").trace; +const tracy = @import("../tracy.zig"); +const trace = tracy.trace; +const traceNamed = tracy.traceNamed; const Type = @import("../Type.zig"); const Value = @import("../Value.zig"); const Zcu = @import("../Zcu.zig"); @@ -125,6 +127,329 @@ pub fn deactivate(pt: Zcu.PerThread) void { pt.zcu.intern_pool.deactivate(); } +/// Called from `Compilation.performAllTheWork`. Performs one incremental update of the ZCU: detects +/// changes to files, runs AstGen, and then enters the main semantic analysis loop, where we build +/// up a graph of declarations, functions, etc, while also sending declarations and functions to +/// codegen as they are analyzed. +pub fn update( + pt: Zcu.PerThread, + main_progress_node: std.Progress.Node, + decl_work_timer: *?Compilation.Timer, +) (Allocator.Error || Io.Cancelable)!void { + const zcu = pt.zcu; + const comp = zcu.comp; + const gpa = comp.gpa; + const io = comp.io; + + { + const tracy_trace = traceNamed(@src(), "astgen"); + defer tracy_trace.end(); + + const zir_prog_node = main_progress_node.start("AST Lowering", 0); + defer zir_prog_node.end(); + + var timer = comp.startTimer(); + defer if (timer.finish(io)) |ns| { + comp.mutex.lockUncancelable(io); + defer comp.mutex.unlock(io); + comp.time_report.?.stats.real_ns_files = ns; + }; + + var astgen_group: Io.Group = .init; + defer astgen_group.cancel(io); + + // We cannot reference `zcu.import_table` after we spawn any `workerUpdateFile` jobs, + // because on single-threaded targets the worker will be run eagerly, meaning the + // `import_table` could be mutated, and not even holding `comp.mutex` will save us. So, + // build up a list of the files to update *before* we spawn any jobs. + var astgen_work_items: std.MultiArrayList(struct { + file_index: Zcu.File.Index, + file: *Zcu.File, + }) = .empty; + defer astgen_work_items.deinit(gpa); + // Not every item in `import_table` will need updating, because some are builtin.zig + // files. However, most will, so let's just reserve sufficient capacity upfront. + try astgen_work_items.ensureTotalCapacity(gpa, zcu.import_table.count()); + for (zcu.import_table.keys()) |file_index| { + const file = zcu.fileByIndex(file_index); + if (file.is_builtin) { + // This is a `builtin.zig`, so updating is redundant. However, we want to make + // sure the file contents are still correct on disk, since it can improve the + // debugging experience better. That job only needs `file`, so we can kick it + // off right now. + astgen_group.async(io, workerUpdateBuiltinFile, .{ comp, file }); + continue; + } + astgen_work_items.appendAssumeCapacity(.{ + .file_index = file_index, + .file = file, + }); + } + + // Now that we're not going to touch `zcu.import_table` again, we can spawn `workerUpdateFile` jobs. + for (astgen_work_items.items(.file_index), astgen_work_items.items(.file)) |file_index, file| { + astgen_group.async(io, workerUpdateFile, .{ + comp, file, file_index, zir_prog_node, &astgen_group, + }); + } + + // On the other hand, it's fine to directly iterate `zcu.embed_table.keys()` here + // because `workerUpdateEmbedFile` can't invalidate it. The different here is that one + // `@embedFile` can't trigger analysis of a new `@embedFile`! + for (0.., zcu.embed_table.keys()) |ef_index_usize, ef| { + const ef_index: Zcu.EmbedFile.Index = @enumFromInt(ef_index_usize); + astgen_group.async(io, workerUpdateEmbedFile, .{ + comp, ef_index, ef, + }); + } + + try astgen_group.await(io); + } + + // On an incremental update, a source file might become "dead", in that all imports of + // the file were removed. This could even change what module the file belongs to! As such, + // we do a traversal over the files, to figure out which ones are alive and the modules + // they belong to. + const any_fatal_files = try pt.computeAliveFiles(); + + // If the cache mode is `whole`, add every alive source file to the manifest. + switch (comp.cache_use) { + .whole => |whole| if (whole.cache_manifest) |man| { + for (zcu.alive_files.keys()) |file_index| { + const file = zcu.fileByIndex(file_index); + + switch (file.status) { + .never_loaded => unreachable, // AstGen tried to load it + .retryable_failure => continue, // the file cannot be read; this is a guaranteed error + .astgen_failure, .success => {}, // the file was read successfully + } + + const path = try file.path.toAbsolute(comp.dirs, gpa); + defer gpa.free(path); + + const result = res: { + try whole.cache_manifest_mutex.lock(io); + defer whole.cache_manifest_mutex.unlock(io); + if (file.source) |source| { + break :res man.addFilePostContents(path, source, file.stat); + } else { + break :res man.addFilePost(path); + } + }; + result catch |err| switch (err) { + error.OutOfMemory => |e| return e, + else => { + try pt.reportRetryableFileError(file_index, "unable to update cache: {s}", .{@errorName(err)}); + continue; + }, + }; + } + }, + .none, .incremental => {}, + } + + if (comp.time_report) |*tr| { + tr.stats.n_reachable_files = @intCast(zcu.alive_files.count()); + } + + if (any_fatal_files or + zcu.multi_module_err != null or + zcu.failed_imports.items.len > 0 or + comp.alloc_failure_occurred) + { + // We give up right now! No updating of ZIR refs, no nothing. The idea is that this prevents + // us from invalidating lots of incremental dependencies due to files with e.g. parse errors. + // However, this means our analysis data is invalid, so we want to omit all analysis errors. + zcu.skip_analysis_this_update = true; + return; + } + + if (comp.config.incremental) { + const update_zir_refs_node = main_progress_node.start("Update ZIR References", 0); + defer update_zir_refs_node.end(); + try pt.updateZirRefs(); + } + + try zcu.flushRetryableFailures(); + + if (!zcu.backendSupportsFeature(.separate_thread)) { + // Close the ZCU task queue. Prelink may still be running, but the closed + // queue will cause the linker task to exit once prelink finishes. The + // closed queue also communicates to `enqueueZcu` that it should wait for + // the linker task to finish and then run ZCU tasks serially. + comp.link_queue.finishZcuQueue(comp); + } + + zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0); + if (comp.bin_file != null) { + zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0); + } + // We increment `pending_codegen_jobs` so that it doesn't reach 0 until after analysis finishes. + // That prevents the "Code Generation" node from constantly disappearing and reappearing when + // we're probably going to analyze more functions at some point. + assert(zcu.pending_codegen_jobs.swap(1, .monotonic) == 0); // don't let this become 0 until analysis finishes + + defer { + zcu.sema_prog_node.end(); + zcu.sema_prog_node = .none; + if (zcu.pending_codegen_jobs.fetchSub(1, .monotonic) == 1) { + // Decremented to 0, so all done. + zcu.codegen_prog_node.end(); + zcu.codegen_prog_node = .none; + } + } + + // Start the timer for the "decls" part of the pipeline (Sema, CodeGen, link). + decl_work_timer.* = comp.startTimer(); + + // To kick off semantic analysis, populate the root source file of any module we have marked + // as an analysis root. Declarations in these files which want eager analysis---those being + // `comptime` declarations, any declarations marked `export`, and `test` declarations in the + // main module if this is a test compilation---become referenced, and so will be picked up + // up by the main semantic analysis loop below. + for (zcu.analysisRoots()) |analysis_root_mod| { + const analysis_root_file = zcu.module_roots.get(analysis_root_mod).?.unwrap().?; + try pt.ensureFilePopulated(analysis_root_file); + } + + // This is the main semantic analysis loop, which is essentially the main loop of the whole + // Zig compilation pipeline. It selects some `AnalUnit` which we know needs to be analyzed, + // and analyzes it, which may in turn discover more `AnalUnit`s which we need to analyze. + while (try zcu.findOutdatedToAnalyze()) |unit| { + const tracy_trace = traceNamed(@src(), "analyze_outdated"); + defer tracy_trace.end(); + + const maybe_err: Zcu.SemaError!void = switch (unit.unwrap()) { + .@"comptime" => |cu| pt.ensureComptimeUnitUpToDate(cu), + .nav_ty => |nav| pt.ensureNavTypeUpToDate(nav, null), + .nav_val => |nav| pt.ensureNavValUpToDate(nav, null), + .type_layout => |ty| pt.ensureTypeLayoutUpToDate(.fromInterned(ty), null), + .struct_defaults => |ty| res: { + // Unlike the other functions, this one requires that the type layout is resolved first. + pt.ensureTypeLayoutUpToDate(.fromInterned(ty), null) catch |err| switch (err) { + error.OutOfMemory, + error.Canceled, + => |e| return e, + + error.AnalysisFail => {}, // already reported + }; + break :res pt.ensureStructDefaultsUpToDate(.fromInterned(ty), null); + }, + .memoized_state => |stage| pt.ensureMemoizedStateUpToDate(stage, null), + .func => |func| pt.ensureFuncBodyUpToDate(func, null), + }; + maybe_err catch |err| switch (err) { + error.OutOfMemory, + error.Canceled, + => |e| return e, + + error.AnalysisFail => {}, // already reported + }; + } +} +fn workerUpdateBuiltinFile(comp: *Compilation, file: *Zcu.File) void { + Builtin.updateFileOnDisk(file, comp) catch |err| comp.lockAndSetMiscFailure( + .write_builtin_zig, + "unable to write '{f}': {s}", + .{ file.path.fmt(comp), @errorName(err) }, + ); +} +fn workerUpdateFile( + comp: *Compilation, + file: *Zcu.File, + file_index: Zcu.File.Index, + prog_node: std.Progress.Node, + group: *Io.Group, +) void { + const io = comp.io; + const tid: Zcu.PerThread.Id = .acquire(io); + defer tid.release(io); + + const child_prog_node = prog_node.start(std.fs.path.basename(file.path.sub_path), 0); + defer child_prog_node.end(); + + const pt: Zcu.PerThread = .activate(comp.zcu.?, tid); + defer pt.deactivate(); + pt.updateFile(file_index, file) catch |err| { + pt.reportRetryableFileError(file_index, "unable to load '{s}': {s}", .{ std.fs.path.basename(file.path.sub_path), @errorName(err) }) catch |oom| switch (oom) { + error.OutOfMemory => { + comp.mutex.lockUncancelable(io); + defer comp.mutex.unlock(io); + comp.setAllocFailure(); + }, + }; + return; + }; + + switch (file.getMode()) { + .zig => {}, // continue to logic below + .zon => return, // ZON can't import anything so we're done + } + + // Discover all imports in the file. Imports of modules we ignore for now since we don't + // know which module we're in, but imports of file paths might need us to queue up other + // AstGen jobs. + const imports_index = file.zir.?.extra[@intFromEnum(Zir.ExtraIndex.imports)]; + if (imports_index != 0) { + const extra = file.zir.?.extraData(Zir.Inst.Imports, imports_index); + var import_i: u32 = 0; + var extra_index = extra.end; + + while (import_i < extra.data.imports_len) : (import_i += 1) { + const item = file.zir.?.extraData(Zir.Inst.Imports.Item, extra_index); + extra_index = item.end; + + const import_path = file.zir.?.nullTerminatedString(item.data.name); + + if (pt.discoverImport(file.path, import_path)) |res| switch (res) { + .module, .existing_file => {}, + .new_file => |new| { + group.async(io, workerUpdateFile, .{ + comp, new.file, new.index, prog_node, group, + }); + }, + } else |err| switch (err) { + error.OutOfMemory => { + comp.mutex.lockUncancelable(io); + defer comp.mutex.unlock(io); + comp.setAllocFailure(); + }, + } + } + } +} +fn workerUpdateEmbedFile(comp: *Compilation, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) void { + const io = comp.io; + const tid: Zcu.PerThread.Id = .acquire(io); + defer tid.release(io); + detectEmbedFileUpdate(comp, tid, ef_index, ef) catch |err| switch (err) { + error.OutOfMemory => { + comp.mutex.lockUncancelable(io); + defer comp.mutex.unlock(io); + comp.setAllocFailure(); + }, + }; +} +fn detectEmbedFileUpdate(comp: *Compilation, tid: Zcu.PerThread.Id, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) !void { + const io = comp.io; + const zcu = comp.zcu.?; + const pt: Zcu.PerThread = .activate(zcu, tid); + defer pt.deactivate(); + + const old_val = ef.val; + const old_err = ef.err; + + try pt.updateEmbedFile(ef, null); + + if (ef.val != .none and ef.val == old_val) return; // success, value unchanged + if (ef.val == .none and old_val == .none and ef.err == old_err) return; // failure, error unchanged + + comp.mutex.lockUncancelable(io); + defer comp.mutex.unlock(io); + + try zcu.markDependeeOutdated(.not_marked_po, .{ .embed_file = ef_index }); +} + fn deinitFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) void { const zcu = pt.zcu; const gpa = zcu.gpa; @@ -156,8 +481,8 @@ pub fn updateFile( ) !void { dev.check(.ast_gen); - const tracy = trace(@src()); - defer tracy.end(); + const tracy_trace = trace(@src()); + defer tracy_trace.end(); const zcu = pt.zcu; const comp = zcu.comp; @@ -484,7 +809,7 @@ fn cleanupUpdatedFiles(gpa: Allocator, updated_files: *std.AutoArrayHashMapUnman updated_files.deinit(gpa); } -pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { +fn updateZirRefs(pt: Zcu.PerThread) (Io.Cancelable || Allocator.Error)!void { assert(pt.tid == .main); const zcu = pt.zcu; const comp = zcu.comp; @@ -566,7 +891,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { const old_line = old_zir.getDeclaration(old_inst).src_line; const new_line = new_zir.getDeclaration(new_inst).src_line; if (old_line != new_line) { - try comp.queueJob(.{ .update_line_number = tracked_inst_index }); + try comp.link_queue.enqueueZcu(comp, pt.tid, .{ .debug_update_line_number = tracked_inst_index }); } }, else => {}, @@ -598,44 +923,38 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { // Value is whether the declaration is `pub`. var old_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, bool) = .empty; defer old_names.deinit(zcu.gpa); - { - var it = old_zir.declIterator(old_inst); - while (it.next()) |decl_inst| { - const old_decl = old_zir.getDeclaration(decl_inst); - if (old_decl.name == .empty) continue; - const name_ip = try zcu.intern_pool.getOrPutString( - zcu.gpa, - io, - pt.tid, - old_zir.nullTerminatedString(old_decl.name), - .no_embedded_nulls, - ); - try old_names.put(zcu.gpa, name_ip, old_decl.is_pub); - } + for (old_zir.typeDecls(old_inst)) |decl_inst| { + const old_decl = old_zir.getDeclaration(decl_inst); + if (old_decl.name == .empty) continue; + const name_ip = try zcu.intern_pool.getOrPutString( + zcu.gpa, + io, + pt.tid, + old_zir.nullTerminatedString(old_decl.name), + .no_embedded_nulls, + ); + try old_names.put(zcu.gpa, name_ip, old_decl.is_pub); } var any_change = false; - { - var it = new_zir.declIterator(new_inst); - while (it.next()) |decl_inst| { - const new_decl = new_zir.getDeclaration(decl_inst); - if (new_decl.name == .empty) continue; - const name_ip = try zcu.intern_pool.getOrPutString( - zcu.gpa, - io, - pt.tid, - new_zir.nullTerminatedString(new_decl.name), - .no_embedded_nulls, - ); - if (old_names.fetchSwapRemove(name_ip)) |kv| { - if (kv.value == new_decl.is_pub) continue; - } - // Name added, or changed whether it's pub - any_change = true; - try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace_name = .{ - .namespace = tracked_inst_index, - .name = name_ip, - } }); + for (new_zir.typeDecls(new_inst)) |decl_inst| { + const new_decl = new_zir.getDeclaration(decl_inst); + if (new_decl.name == .empty) continue; + const name_ip = try zcu.intern_pool.getOrPutString( + zcu.gpa, + io, + pt.tid, + new_zir.nullTerminatedString(new_decl.name), + .no_embedded_nulls, + ); + if (old_names.fetchSwapRemove(name_ip)) |kv| { + if (kv.value == new_decl.is_pub) continue; } + // Name added, or changed whether it's pub + any_change = true; + try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace_name = .{ + .namespace = tracked_inst_index, + .name = name_ip, + } }); } // The only elements remaining in `old_names` now are any names which were removed. for (old_names.keys()) |name_ip| { @@ -674,32 +993,74 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { } } -/// Ensures that `zcu.fileRootType` on this `file_index` gives an up-to-date answer. -/// Returns `error.AnalysisFail` if the file has an error. -pub fn ensureFileAnalyzed(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { - const file_root_type = pt.zcu.fileRootType(file_index); - if (file_root_type != .none) { - if (pt.ensureTypeUpToDate(file_root_type)) |_| { - return; - } else |err| switch (err) { - error.AnalysisFail => { - // The file's root `struct_decl` has, at some point, been lost, because the file failed AstGen. - // Clear `file_root_type`, and try the `semaFile` call below, in case the instruction has since - // been discovered under a new `TrackedInst.Index`. - pt.zcu.setFileRootType(file_index, .none); - }, - else => |e| return e, - } - } - return pt.semaFile(file_index); +/// Ensures that `zcu.fileRootType` on this `file_index` is populated (not `.none`). This implies +/// that the file's namespace is scanned, discovering declarations. +/// +/// Typical Zig compilations begin by claling this function on the root source file of the standard +/// library, `lib/std/std.zig`. The resulting namespace scan discovers a `comptime` declaration in +/// that file, which is queued for analysis, and everything goes from there. +pub fn ensureFilePopulated(pt: Zcu.PerThread, file_index: Zcu.File.Index) (Allocator.Error || Io.Cancelable)!void { + dev.check(.sema); + + const tracy_trace = trace(@src()); + defer tracy_trace.end(); + + const zcu = pt.zcu; + const comp = zcu.comp; + const io = comp.io; + const gpa = comp.gpa; + const ip = &zcu.intern_pool; + + if (zcu.fileRootType(file_index) != .none) return; // already good + + if (zcu.comp.time_report) |*tr| tr.stats.n_imported_files += 1; + + const file = zcu.fileByIndex(file_index); + assert(file.getMode() == .zig); + const struct_decl = file.zir.?.getStructDecl(.main_struct_inst); + const tracked_inst = try ip.trackZir(gpa, io, pt.tid, .{ + .file = file_index, + .inst = .main_struct_inst, + }); + const wip: InternPool.WipContainerType = switch (try ip.getDeclaredStructType(gpa, io, pt.tid, .{ + .zir_index = tracked_inst, + .captures = &.{}, + .fields_len = @intCast(struct_decl.field_names.len), + .layout = struct_decl.layout, + .any_comptime_fields = struct_decl.field_comptime_bits != null, + .any_field_defaults = struct_decl.field_default_body_lens != null, + .any_field_aligns = struct_decl.field_align_body_lens != null, + .packed_backing_mode = if (struct_decl.backing_int_type_body != null) .explicit else .auto, + })) { + .existing => unreachable, // it would have been set as `zcu.fileRootType` already + .wip => |wip| wip, + }; + errdefer wip.cancel(ip, pt.tid); + + wip.setName(ip, try file.internFullyQualifiedName(pt), .none); + const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ + .parent = .none, + .owner_type = wip.index, + .file_scope = file_index, + .generation = zcu.generation, + }); + errdefer pt.destroyNamespace(new_namespace_index); + try pt.scanNamespace(new_namespace_index, struct_decl.decls); + if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); + zcu.setFileRootType(file_index, wip.finish(ip, new_namespace_index)); } /// Ensures that all memoized state on `Zcu` is up-to-date, performing re-analysis if necessary. /// Returns `error.AnalysisFail` if an analysis error is encountered; the caller is free to ignore /// this, since the error is already registered, but it must not use the value of memoized fields. -pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.MemoizedStateStage) Zcu.SemaError!void { - const tracy = trace(@src()); - defer tracy.end(); +pub fn ensureMemoizedStateUpToDate( + pt: Zcu.PerThread, + stage: InternPool.MemoizedStateStage, + /// `null` is valid only for the "root" analysis, i.e. called from `Compilation.processOneJob`. + reason: ?*const Zcu.DependencyReason, +) Zcu.SemaError!void { + const tracy_trace = trace(@src()); + defer tracy_trace.end(); const zcu = pt.zcu; const gpa = zcu.gpa; @@ -710,19 +1071,11 @@ pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.Memoized assert(!zcu.analysis_in_progress.contains(unit)); - const was_outdated = zcu.outdated.swapRemove(unit) or zcu.potentially_outdated.swapRemove(unit); + const was_outdated = zcu.clearOutdatedState(unit); const prev_failed = zcu.failed_analysis.contains(unit) or zcu.transitive_failed_analysis.contains(unit); if (was_outdated) { - dev.check(.incremental); - _ = zcu.outdated_ready.swapRemove(unit); - // No need for `deleteUnitExports` because we never export anything. - zcu.deleteUnitReferences(unit); - zcu.deleteUnitCompileLogs(unit); - if (zcu.failed_analysis.fetchSwapRemove(unit)) |kv| { - kv.value.destroy(gpa); - } - _ = zcu.transitive_failed_analysis.swapRemove(unit); + zcu.resetUnit(unit); } else { if (prev_failed) return error.AnalysisFail; // We use an arbitrary element to check if the state has been resolved yet. @@ -741,7 +1094,7 @@ pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.Memoized info.deps.clearRetainingCapacity(); } - const any_changed: bool, const new_failed: bool = if (pt.analyzeMemoizedState(stage)) |any_changed| + const any_changed: bool, const new_failed: bool = if (pt.analyzeMemoizedState(stage, reason)) |any_changed| .{ any_changed or prev_failed, false } else |err| switch (err) { error.AnalysisFail => res: { @@ -774,39 +1127,20 @@ pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.Memoized if (new_failed) return error.AnalysisFail; } -fn analyzeMemoizedState(pt: Zcu.PerThread, stage: InternPool.MemoizedStateStage) Zcu.CompileError!bool { +fn analyzeMemoizedState( + pt: Zcu.PerThread, + stage: InternPool.MemoizedStateStage, + reason: ?*const Zcu.DependencyReason, +) Zcu.CompileError!bool { const zcu = pt.zcu; - const ip = &zcu.intern_pool; const comp = zcu.comp; const gpa = comp.gpa; - const io = comp.io; const unit: AnalUnit = .wrap(.{ .memoized_state = stage }); - try zcu.analysis_in_progress.putNoClobber(gpa, unit, {}); + try zcu.analysis_in_progress.putNoClobber(gpa, unit, reason); defer assert(zcu.analysis_in_progress.swapRemove(unit)); - // Before we begin, collect: - // * The type `std`, and its namespace - // * The type `std.builtin`, and its namespace - // * A semi-reasonable source location - const std_file_index = zcu.module_roots.get(zcu.std_mod).?.unwrap().?; - try pt.ensureFileAnalyzed(std_file_index); - const std_type: Type = .fromInterned(zcu.fileRootType(std_file_index)); - const std_namespace = std_type.getNamespaceIndex(zcu); - try pt.ensureNamespaceUpToDate(std_namespace); - const builtin_str = try ip.getOrPutString(gpa, io, pt.tid, "builtin", .no_embedded_nulls); - const builtin_nav = zcu.namespacePtr(std_namespace).pub_decls.getKeyAdapted(builtin_str, Zcu.Namespace.NameAdapter{ .zcu = zcu }) orelse - @panic("lib/std.zig is corrupt and missing 'builtin'"); - try pt.ensureNavValUpToDate(builtin_nav); - const builtin_type: Type = .fromInterned(ip.getNav(builtin_nav).status.fully_resolved.val); - const builtin_namespace = builtin_type.getNamespaceIndex(zcu); - try pt.ensureNamespaceUpToDate(builtin_namespace); - const src: Zcu.LazySrcLoc = .{ - .base_node_inst = builtin_type.typeDeclInst(zcu).?, - .offset = .{ .byte_abs = 0 }, - }; - var analysis_arena: std.heap.ArenaAllocator = .init(gpa); defer analysis_arena.deinit(); @@ -827,30 +1161,15 @@ fn analyzeMemoizedState(pt: Zcu.PerThread, stage: InternPool.MemoizedStateStage) }; defer sema.deinit(); - var block: Sema.Block = .{ - .parent = null, - .sema = &sema, - .namespace = std_namespace, - .instructions = .{}, - .inlining = null, - .comptime_reason = .{ .reason = .{ - .src = src, - .r = .{ .simple = .type }, - } }, - .src_base_inst = src.base_node_inst, - .type_name_ctx = .empty, - }; - defer block.instructions.deinit(gpa); - - return sema.analyzeMemoizedState(&block, src, builtin_namespace, stage); + return sema.analyzeMemoizedState(stage); } /// Ensures that the state of the given `ComptimeUnit` is fully up-to-date, performing re-analysis /// if necessary. Returns `error.AnalysisFail` if an analysis error is encountered; the caller is /// free to ignore this, since the error is already registered. pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu.SemaError!void { - const tracy = trace(@src()); - defer tracy.end(); + const tracy_trace = trace(@src()); + defer tracy_trace.end(); const zcu = pt.zcu; const gpa = zcu.gpa; @@ -870,22 +1189,10 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`. - const was_outdated = zcu.outdated.swapRemove(anal_unit) or - zcu.potentially_outdated.swapRemove(anal_unit); + const was_outdated = zcu.clearOutdatedState(anal_unit); if (was_outdated) { - _ = zcu.outdated_ready.swapRemove(anal_unit); - // `was_outdated` can be true in the initial update for comptime units, so this isn't a `dev.check`. - if (dev.env.supports(.incremental)) { - zcu.deleteUnitExports(anal_unit); - zcu.deleteUnitReferences(anal_unit); - zcu.deleteUnitCompileLogs(anal_unit); - if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { - kv.value.destroy(gpa); - } - _ = zcu.transitive_failed_analysis.swapRemove(anal_unit); - zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit); - } + zcu.resetUnit(anal_unit); } else { // We can trust the current information about this unit. if (zcu.failed_analysis.contains(anal_unit)) return error.AnalysisFail; @@ -950,7 +1257,7 @@ fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu const file = zcu.fileByIndex(inst_resolved.file); const zir = file.zir.?; - try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {}); + try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, null); defer assert(zcu.analysis_in_progress.swapRemove(anal_unit)); var analysis_arena: std.heap.ArenaAllocator = .init(gpa); @@ -980,7 +1287,7 @@ fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu .parent = null, .sema = &sema, .namespace = comptime_unit.namespace, - .instructions = .{}, + .instructions = .empty, .inlining = null, .comptime_reason = .{ .reason = .{ .src = .{ @@ -1012,33 +1319,262 @@ fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu try sema.flushExports(); } +/// Ensures that the layout of the given `struct`, `union`, or `enum` type is fully up-to-date, +/// performing re-analysis if necessary. Asserts that `ty` is a struct (not a tuple!), union, or +/// enum type. Returns `error.AnalysisFail` if an analysis error is encountered during type +/// resolution; the caller is free to ignore this, since the error is already registered. +pub fn ensureTypeLayoutUpToDate( + pt: Zcu.PerThread, + ty: Type, + /// `null` is valid only for the "root" analysis, i.e. called from `Compilation.processOneJob`. + reason: ?*const Zcu.DependencyReason, +) Zcu.SemaError!void { + const tracy_trace = trace(@src()); + defer tracy_trace.end(); + + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const comp = zcu.comp; + const gpa = comp.gpa; + + const anal_unit: AnalUnit = .wrap(.{ .type_layout = ty.toIntern() }); + + log.debug("ensureTypeLayoutUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)}); + + assert(!zcu.analysis_in_progress.contains(anal_unit)); + + const was_outdated: bool = outdated: { + if (zcu.clearOutdatedState(anal_unit)) break :outdated true; + if (ip.setWantTypeLayout(comp.io, ty.toIntern())) { + // We'll analyze the layout for the first time, but if this is a struct type then its + // default field values also need to be analyzed. + if (ip.indexToKey(ty.toIntern()) == .struct_type) { + if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io); + defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io); + try zcu.outdated.ensureUnusedCapacity(gpa, 1); + try zcu.outdated_ready.other.ensureUnusedCapacity(gpa, 1); + zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .struct_defaults = ty.toIntern() }), 0); + zcu.outdated_ready.other.putAssumeCapacityNoClobber(.wrap(.{ .struct_defaults = ty.toIntern() }), {}); + } + break :outdated true; + } + break :outdated false; + }; + + if (was_outdated) { + zcu.resetUnit(anal_unit); + // For types, we already know that we have to invalidate all dependees. + // TODO: we actually *could* detect whether everything was the same. should we bother? + try zcu.markDependeeOutdated(.marked_po, .{ .type_layout = ty.toIntern() }); + } else { + // We can trust the current information about this unit. + if (zcu.failed_analysis.contains(anal_unit)) return error.AnalysisFail; + if (zcu.transitive_failed_analysis.contains(anal_unit)) return error.AnalysisFail; + return; + } + + if (comp.debugIncremental()) { + const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit); + info.last_update_gen = zcu.generation; + info.deps.clearRetainingCapacity(); + } + + const unit_tracking = zcu.trackUnitSema(ty.containerTypeName(ip).toSlice(ip), null); + defer unit_tracking.end(zcu); + + try zcu.analysis_in_progress.put(gpa, anal_unit, reason); + defer assert(zcu.analysis_in_progress.swapRemove(anal_unit)); + + var analysis_arena: std.heap.ArenaAllocator = .init(gpa); + defer analysis_arena.deinit(); + + var comptime_err_ret_trace: std.array_list.Managed(Zcu.LazySrcLoc) = .init(gpa); + defer comptime_err_ret_trace.deinit(); + + const file = zcu.namespacePtr(ty.getNamespaceIndex(zcu)).fileScope(zcu); + + var sema: Sema = .{ + .pt = pt, + .gpa = gpa, + .arena = analysis_arena.allocator(), + .code = file.zir.?, + .owner = anal_unit, + .func_index = .none, + .func_is_naked = false, + .fn_ret_ty = .void, + .fn_ret_ty_ies = null, + .comptime_err_ret_trace = &comptime_err_ret_trace, + }; + defer sema.deinit(); + + const result = switch (ty.zigTypeTag(zcu)) { + .@"enum" => Sema.type_resolution.resolveEnumLayout(&sema, ty), + .@"struct" => Sema.type_resolution.resolveStructLayout(&sema, ty), + .@"union" => Sema.type_resolution.resolveUnionLayout(&sema, ty), + else => unreachable, + }; + const new_failed: bool = if (result) failed: { + break :failed false; + } else |err| switch (err) { + error.AnalysisFail => failed: { + if (!zcu.failed_analysis.contains(anal_unit)) { + // If this unit caused the error, it would have an entry in `failed_analysis`. + // Since it does not, this must be a transitive failure. + try zcu.transitive_failed_analysis.put(gpa, anal_unit, {}); + log.debug("mark transitive analysis failure for {f}", .{zcu.fmtAnalUnit(anal_unit)}); + } + break :failed true; + }, + error.OutOfMemory, + error.Canceled, + => |e| return e, + error.ComptimeReturn => unreachable, + error.ComptimeBreak => unreachable, + }; + + sema.flushExports() catch |err| switch (err) { + error.OutOfMemory => |e| return e, + }; + + // We don't need to `markDependeeOutdated`/`markPoDependeeUpToDate` here, because we already + // marked the layout as outdated at the top of this function. However, we do need to tell the + // debug info logic in the backend about this type. + comp.link_prog_node.increaseEstimatedTotalItems(1); + try comp.link_queue.enqueueZcu(comp, pt.tid, .{ .debug_update_container_type = .{ + .ty = ty.toIntern(), + .success = !new_failed, + } }); + + if (new_failed) return error.AnalysisFail; +} + +/// Ensures that the default field values of the given `struct` type are fully up-to-date, +/// performing re-analysis if necessary. Asserts that `ty` is a struct (not a tuple!) type. Unlike +/// the other "ensure X up to date" functions, this particular function also asserts that the +/// *layout* of `ty` is *already* up-to-date (though it is okay for that resolution to have failed). +/// Returns `error.AnalysisFail` if an analysis error is encountered while resolving the default +/// field values; the caller is free to ignore this, since the error is already registered. +pub fn ensureStructDefaultsUpToDate( + pt: Zcu.PerThread, + ty: Type, + /// `null` is valid only for the "root" analysis, i.e. called from `Compilation.processOneJob`. + reason: ?*const Zcu.DependencyReason, +) Zcu.SemaError!void { + const tracy_trace = trace(@src()); + defer tracy_trace.end(); + + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const comp = zcu.comp; + const gpa = comp.gpa; + + assert(ip.indexToKey(ty.toIntern()) == .struct_type); + + const anal_unit: AnalUnit = .wrap(.{ .struct_defaults = ty.toIntern() }); + + log.debug("ensureStructDefaultsUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)}); + + assert(!zcu.analysis_in_progress.contains(anal_unit)); + + const was_outdated: bool = outdated: { + if (zcu.clearOutdatedState(anal_unit)) break :outdated true; + // The type layout should already be marked as "wanted" by this point, because a struct's + // layout must always be analyzed before its default values are. + assert(!ip.setWantTypeLayout(comp.io, ty.toIntern())); + break :outdated false; + }; + + if (was_outdated) { + zcu.resetUnit(anal_unit); + // For types, we already know that we have to invalidate all dependees. + // TODO: we actually *could* detect whether everything was the same. should we bother? + try zcu.markDependeeOutdated(.marked_po, .{ .struct_defaults = ty.toIntern() }); + } else { + // We can trust the current information about this unit. + if (zcu.failed_analysis.contains(anal_unit)) return error.AnalysisFail; + if (zcu.transitive_failed_analysis.contains(anal_unit)) return error.AnalysisFail; + return; + } + + if (zcu.comp.debugIncremental()) { + const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit); + info.last_update_gen = zcu.generation; + info.deps.clearRetainingCapacity(); + } + + const unit_tracking = zcu.trackUnitSema(ty.containerTypeName(ip).toSlice(ip), null); + defer unit_tracking.end(zcu); + + try zcu.analysis_in_progress.put(gpa, anal_unit, reason); + defer assert(zcu.analysis_in_progress.swapRemove(anal_unit)); + + var analysis_arena: std.heap.ArenaAllocator = .init(gpa); + defer analysis_arena.deinit(); + + var comptime_err_ret_trace: std.array_list.Managed(Zcu.LazySrcLoc) = .init(gpa); + defer comptime_err_ret_trace.deinit(); + + const file = zcu.namespacePtr(ty.getNamespaceIndex(zcu)).fileScope(zcu); + + var sema: Sema = .{ + .pt = pt, + .gpa = gpa, + .arena = analysis_arena.allocator(), + .code = file.zir.?, + .owner = anal_unit, + .func_index = .none, + .func_is_naked = false, + .fn_ret_ty = .void, + .fn_ret_ty_ies = null, + .comptime_err_ret_trace = &comptime_err_ret_trace, + }; + defer sema.deinit(); + + const new_failed: bool = if (Sema.type_resolution.resolveStructDefaults(&sema, ty)) failed: { + break :failed false; + } else |err| switch (err) { + error.AnalysisFail => failed: { + if (!zcu.failed_analysis.contains(anal_unit)) { + // If this unit caused the error, it would have an entry in `failed_analysis`. + // Since it does not, this must be a transitive failure. + try zcu.transitive_failed_analysis.put(gpa, anal_unit, {}); + log.debug("mark transitive analysis failure for {f}", .{zcu.fmtAnalUnit(anal_unit)}); + } + break :failed true; + }, + error.OutOfMemory, + error.Canceled, + => |e| return e, + error.ComptimeReturn => unreachable, + error.ComptimeBreak => unreachable, + }; + + sema.flushExports() catch |err| switch (err) { + error.OutOfMemory => |e| return e, + }; + + // We don't need to `markDependeeOutdated`/`markPoDependeeUpToDate` here, because we already + // marked the struct defaults as outdated at the top of this function. + + if (new_failed) return error.AnalysisFail; +} + /// Ensures that the resolved value of the given `Nav` is fully up-to-date, performing re-analysis /// if necessary. Returns `error.AnalysisFail` if an analysis error is encountered; the caller is /// free to ignore this, since the error is already registered. -pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.SemaError!void { - const tracy = trace(@src()); - defer tracy.end(); - - // TODO: document this elsewhere mlugg! - // For my own benefit, here's how a namespace update for a normal (non-file-root) type works: - // `const S = struct { ... };` - // We are adding or removing a declaration within this `struct`. - // * `S` registers a dependency on `.{ .src_hash = (declaration of S) }` - // * Any change to the `struct` body -- including changing a declaration -- invalidates this - // * `S` is re-analyzed, but notes: - // * there is an existing struct instance (at this `TrackedInst` with these captures) - // * the struct's resolution is up-to-date (because nothing about the fields changed) - // * so, it uses the same `struct` - // * but this doesn't stop it from updating the namespace! - // * we basically do `scanDecls`, updating the namespace as needed - // * so everyone lived happily ever after +pub fn ensureNavValUpToDate( + pt: Zcu.PerThread, + nav_id: InternPool.Nav.Index, + /// `null` is valid only for the "root" analysis, i.e. called from `Compilation.processOneJob`. + reason: ?*const Zcu.DependencyReason, +) Zcu.SemaError!void { + const tracy_trace = trace(@src()); + defer tracy_trace.end(); const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; - _ = zcu.nav_val_analysis_queued.swapRemove(nav_id); - const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_id }); const nav = ip.getNav(nav_id); @@ -1046,6 +1582,8 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu assert(!zcu.analysis_in_progress.contains(anal_unit)); + try zcu.ensureNavValAnalysisQueued(nav_id); + // Determine whether or not this `Nav`'s value is outdated. This also includes checking if the // status is `.unresolved`, which indicates that the value is outdated because it has *never* // been analyzed so far. @@ -1055,30 +1593,18 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`. - const was_outdated = zcu.outdated.swapRemove(anal_unit) or - zcu.potentially_outdated.swapRemove(anal_unit); + const was_outdated = zcu.clearOutdatedState(anal_unit); const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit); if (was_outdated) { - dev.check(.incremental); - _ = zcu.outdated_ready.swapRemove(anal_unit); - zcu.deleteUnitExports(anal_unit); - zcu.deleteUnitReferences(anal_unit); - zcu.deleteUnitCompileLogs(anal_unit); - if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { - kv.value.destroy(gpa); - } - _ = zcu.transitive_failed_analysis.swapRemove(anal_unit); - ip.removeDependenciesForDepender(gpa, anal_unit); + zcu.resetUnit(anal_unit); } else { // We can trust the current information about this unit. if (prev_failed) return error.AnalysisFail; - switch (nav.status) { - .unresolved, .type_resolved => {}, - .fully_resolved => return, - } + assert(nav.status == .fully_resolved); + return; } if (zcu.comp.debugIncremental()) { @@ -1090,7 +1616,7 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu const unit_tracking = zcu.trackUnitSema(nav.fqn.toSlice(ip), nav.srcInst(ip)); defer unit_tracking.end(zcu); - const invalidate_value: bool, const new_failed: bool = if (pt.analyzeNavVal(nav_id)) |result| res: { + const invalidate_value: bool, const new_failed: bool = if (pt.analyzeNavVal(nav_id, reason)) |result| res: { break :res .{ // If the unit has gone from failed to success, we still need to invalidate the dependencies. result.val_changed or prev_failed, @@ -1134,39 +1660,14 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu } } - // If there isn't a type annotation, then we have also just resolved the type. That means the - // the type is up-to-date, so it won't have the chance to mark its own dependency on the value; - // we must do that ourselves. - type_deps_on_val: { - const inst_resolved = nav.analysis.?.zir_index.resolveFull(ip) orelse break :type_deps_on_val; - const file = zcu.fileByIndex(inst_resolved.file); - const zir_decl = file.zir.?.getDeclaration(inst_resolved.inst); - if (zir_decl.type_body != null) break :type_deps_on_val; - // The type does indeed depend on the value. We are responsible for populating all state of - // the `nav_ty`, including exports, references, errors, and dependencies. - const ty_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id }); - const ty_was_outdated = zcu.outdated.swapRemove(ty_unit) or - zcu.potentially_outdated.swapRemove(ty_unit); - if (ty_was_outdated) { - _ = zcu.outdated_ready.swapRemove(ty_unit); - zcu.deleteUnitExports(ty_unit); - zcu.deleteUnitReferences(ty_unit); - zcu.deleteUnitCompileLogs(ty_unit); - if (zcu.failed_analysis.fetchSwapRemove(ty_unit)) |kv| { - kv.value.destroy(gpa); - } - _ = zcu.transitive_failed_analysis.swapRemove(ty_unit); - ip.removeDependenciesForDepender(gpa, ty_unit); - } - try pt.addDependency(ty_unit, .{ .nav_val = nav_id }); - if (new_failed) try zcu.transitive_failed_analysis.put(gpa, ty_unit, {}); - if (ty_was_outdated) try zcu.markDependeeOutdated(.marked_po, .{ .nav_ty = nav_id }); - } - if (new_failed) return error.AnalysisFail; } -fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!struct { val_changed: bool } { +fn analyzeNavVal( + pt: Zcu.PerThread, + nav_id: InternPool.Nav.Index, + reason: ?*const Zcu.DependencyReason, +) Zcu.CompileError!struct { val_changed: bool } { const zcu = pt.zcu; const ip = &zcu.intern_pool; const comp = zcu.comp; @@ -1183,16 +1684,8 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr const zir = file.zir.?; const zir_decl = zir.getDeclaration(inst_resolved.inst); - try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {}); - errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit); - - // If there's no type body, we are also resolving the type here. - if (zir_decl.type_body == null) { - try zcu.analysis_in_progress.putNoClobber(gpa, .wrap(.{ .nav_ty = nav_id }), {}); - } - errdefer if (zir_decl.type_body == null) { - _ = zcu.analysis_in_progress.swapRemove(.wrap(.{ .nav_ty = nav_id })); - }; + try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, reason); + defer assert(zcu.analysis_in_progress.swapRemove(anal_unit)); var analysis_arena: std.heap.ArenaAllocator = .init(gpa); defer analysis_arena.deinit(); @@ -1225,7 +1718,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr .parent = null, .sema = &sema, .namespace = old_nav.analysis.?.namespace, - .instructions = .{}, + .instructions = .empty, .inlining = null, .comptime_reason = undefined, // set below .src_base_inst = old_nav.analysis.?.zir_index, @@ -1246,9 +1739,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr const maybe_ty: ?Type = if (zir_decl.type_body != null) ty: { // Since we have a type body, the type is resolved separately! - // Of course, we need to make sure we depend on it properly. - try sema.declareDependency(.{ .nav_ty = nav_id }); - try pt.ensureNavTypeUpToDate(nav_id); + try sema.ensureNavResolved(&block, init_src, nav_id, .type); break :ty .fromInterned(ip.getNav(nav_id).typeOf(ip)); } else null; @@ -1271,9 +1762,6 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr const nav_ty: Type = maybe_ty orelse final_val.?.typeOf(zcu); - // First, we must resolve the declaration's type. To do this, we analyze the type body if available, - // or otherwise, we analyze the value body, populating `early_val` in the process. - const is_const = is_const: switch (zir_decl.kind) { .@"comptime" => unreachable, // this is not a Nav .unnamed_test, .@"test", .decltest => { @@ -1360,7 +1848,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr // This resolves the type of the resolved value, not that value itself. If `nav_val` is a struct type, // this resolves the type `type` (which needs no resolution), not the struct itself. - try nav_ty.resolveLayout(pt); + try sema.ensureLayoutResolved(nav_ty, block.nodeOffset(.zero), if (zir_decl.kind == .@"var") .variable else .constant); const queue_linker_work, const is_owned_fn = switch (ip.indexToKey(nav_val.toIntern())) { .func => |f| .{ true, f.owner_nav == nav_id }, // note that this lets function aliases reach codegen @@ -1377,23 +1865,47 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr if (zir_decl.align_body != null and !target_util.supportsFunctionAlignment(zcu.getTarget())) { return sema.fail(&block, align_src, "target does not support function alignment", .{}); } - } else if (try nav_ty.comptimeOnlySema(pt)) { + } else if (nav_ty.comptimeOnly(zcu)) { // alignment, linksection, addrspace annotations are not allowed for comptime-only types. - const reason: []const u8 = switch (ip.indexToKey(nav_val.toIntern())) { + const cannot_align_reason: []const u8 = switch (ip.indexToKey(nav_val.toIntern())) { .func => "function alias", // slightly clearer message, since you *can* specify these on function *declarations* else => "comptime-only type", }; if (zir_decl.align_body != null) { - return sema.fail(&block, align_src, "cannot specify alignment of {s}", .{reason}); + return sema.fail(&block, align_src, "cannot specify alignment of {s}", .{cannot_align_reason}); } if (zir_decl.linksection_body != null) { - return sema.fail(&block, section_src, "cannot specify linksection of {s}", .{reason}); + return sema.fail(&block, section_src, "cannot specify linksection of {s}", .{cannot_align_reason}); } if (zir_decl.addrspace_body != null) { - return sema.fail(&block, addrspace_src, "cannot specify addrspace of {s}", .{reason}); + return sema.fail(&block, addrspace_src, "cannot specify addrspace of {s}", .{cannot_align_reason}); } } + // We're about to resolve the value of the Nav. This causes the information about what the value + // was last update to be lost; therefore, if the `nav_ty` is currently out of date, it would + // incorrectly think it was unchanged when eventually analyzed. To avoid this, we need to detect + // that case and invalidate the dependee right now. + if (zcu.clearOutdatedState(.wrap(.{ .nav_ty = nav_id }))) { + assert(zir_decl.type_body == null); // otherwise we already resolved it with `Sema.ensureNavResolved` + zcu.resetUnit(.wrap(.{ .nav_ty = nav_id })); + try pt.addDependency(.wrap(.{ .nav_ty = nav_id }), .{ .nav_val = nav_id }); // inferred type depends on the value (that's us!) + if (comp.debugIncremental()) { + const info = try zcu.incremental_debug_state.getUnitInfo(gpa, .wrap(.{ .nav_ty = nav_id })); + info.last_update_gen = zcu.generation; + info.deps.clearRetainingCapacity(); + } + const type_changed: bool = switch (old_nav.status) { + .unresolved => true, + .type_resolved => |old| old.type != nav_ty.toIntern(), + .fully_resolved => |old| ip.typeOf(old.val) != nav_ty.toIntern(), + }; + if (type_changed) { + try zcu.markDependeeOutdated(.marked_po, .{ .nav_ty = nav_id }); + } else { + try zcu.markPoDependeeUpToDate(.{ .nav_ty = nav_id }); + } + } ip.resolveNavValue(io, nav_id, .{ .val = nav_val.toIntern(), .is_const = is_const, @@ -1402,17 +1914,11 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr .@"addrspace" = modifiers.@"addrspace", }); - // Mark the unit as completed before evaluating the export! - assert(zcu.analysis_in_progress.swapRemove(anal_unit)); - if (zir_decl.type_body == null) { - assert(zcu.analysis_in_progress.swapRemove(.wrap(.{ .nav_ty = nav_id }))); - } - if (zir_decl.linkage == .@"export") { const export_src = block.src(.{ .token_offset = @enumFromInt(@intFromBool(zir_decl.is_pub)) }); const name_slice = zir.nullTerminatedString(zir_decl.name); const name_ip = try ip.getOrPutString(gpa, io, pt.tid, name_slice, .no_embedded_nulls); - try sema.analyzeExport(&block, export_src, .{ .name = name_ip }, nav_id); + try sema.analyzeExportSelfNav(&block, export_src, name_ip); } try sema.flushExports(); @@ -1420,25 +1926,37 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr queue_codegen: { if (!queue_linker_work) break :queue_codegen; - if (!try nav_ty.hasRuntimeBitsSema(pt)) { - if (zcu.comp.config.use_llvm) break :queue_codegen; + if (!nav_ty.hasRuntimeBits(zcu)) { + if (comp.config.use_llvm) break :queue_codegen; if (file.mod.?.strip) break :queue_codegen; } - // This job depends on any resolve_type_fully jobs queued up before it. - zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); - try zcu.comp.queueJob(.{ .link_nav = nav_id }); + comp.link_prog_node.increaseEstimatedTotalItems(1); + try comp.link_queue.enqueueZcu(comp, pt.tid, .{ .link_nav = nav_id }); } - switch (old_nav.status) { - .unresolved, .type_resolved => return .{ .val_changed = true }, - .fully_resolved => |old| return .{ .val_changed = old.val != nav_val.toIntern() }, + if (comp.config.is_test and zcu.test_functions.contains(nav_id)) { + // We just analyzed a test function's "value" (essentially its signature); now we need to + // implicitly reference the function *body*. `Zcu.resolveReferences` knows about this rule, + // so we don't need to mark an explicit reference, but we do need to make sure that the test + // body will actually get analyzed! + try zcu.ensureFuncBodyAnalysisQueued(nav_val.toIntern()); } + + return switch (old_nav.status) { + .unresolved, .type_resolved => .{ .val_changed = true }, + .fully_resolved => |old| .{ .val_changed = old.val != nav_val.toIntern() }, + }; } -pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.SemaError!void { - const tracy = trace(@src()); - defer tracy.end(); +pub fn ensureNavTypeUpToDate( + pt: Zcu.PerThread, + nav_id: InternPool.Nav.Index, + /// `null` is valid only for the "root" analysis, i.e. called from `Compilation.processOneJob`. + reason: ?*const Zcu.DependencyReason, +) Zcu.SemaError!void { + const tracy_trace = trace(@src()); + defer tracy_trace.end(); const zcu = pt.zcu; const gpa = zcu.gpa; @@ -1451,17 +1969,7 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc assert(!zcu.analysis_in_progress.contains(anal_unit)); - const type_resolved_by_value: bool = from_val: { - const analysis = nav.analysis orelse break :from_val false; - const inst_resolved = analysis.zir_index.resolveFull(ip) orelse break :from_val false; - const file = zcu.fileByIndex(inst_resolved.file); - const zir_decl = file.zir.?.getDeclaration(inst_resolved.inst); - break :from_val zir_decl.type_body == null; - }; - if (type_resolved_by_value) { - // Logic at the end of `ensureNavValUpToDate` is directly responsible for populating our state. - return pt.ensureNavValUpToDate(nav_id); - } + try zcu.ensureNavValAnalysisQueued(nav_id); // Determine whether or not this `Nav`'s type is outdated. This also includes checking if the // status is `.unresolved`, which indicates that the value is outdated because it has *never* @@ -1472,30 +1980,18 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`. - const was_outdated = zcu.outdated.swapRemove(anal_unit) or - zcu.potentially_outdated.swapRemove(anal_unit); + const was_outdated = zcu.clearOutdatedState(anal_unit); const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit); if (was_outdated) { - dev.check(.incremental); - _ = zcu.outdated_ready.swapRemove(anal_unit); - zcu.deleteUnitExports(anal_unit); - zcu.deleteUnitReferences(anal_unit); - zcu.deleteUnitCompileLogs(anal_unit); - if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { - kv.value.destroy(gpa); - } - _ = zcu.transitive_failed_analysis.swapRemove(anal_unit); - ip.removeDependenciesForDepender(gpa, anal_unit); + zcu.resetUnit(anal_unit); } else { // We can trust the current information about this unit. if (prev_failed) return error.AnalysisFail; - switch (nav.status) { - .unresolved => {}, - .type_resolved, .fully_resolved => return, - } + assert(nav.status != .unresolved); + return; } if (zcu.comp.debugIncremental()) { @@ -1507,7 +2003,7 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc const unit_tracking = zcu.trackUnitSema(nav.fqn.toSlice(ip), nav.srcInst(ip)); defer unit_tracking.end(zcu); - const invalidate_type: bool, const new_failed: bool = if (pt.analyzeNavType(nav_id)) |result| res: { + const invalidate_type: bool, const new_failed: bool = if (pt.analyzeNavType(nav_id, reason)) |result| res: { break :res .{ // If the unit has gone from failed to success, we still need to invalidate the dependencies. result.type_changed or prev_failed, @@ -1554,7 +2050,11 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc if (new_failed) return error.AnalysisFail; } -fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!struct { type_changed: bool } { +fn analyzeNavType( + pt: Zcu.PerThread, + nav_id: InternPool.Nav.Index, + reason: ?*const Zcu.DependencyReason, +) Zcu.CompileError!struct { type_changed: bool } { const zcu = pt.zcu; const comp = zcu.comp; const gpa = comp.gpa; @@ -1570,11 +2070,10 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr const file = zcu.fileByIndex(inst_resolved.file); const zir = file.zir.?; - try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {}); + try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, reason); defer assert(zcu.analysis_in_progress.swapRemove(anal_unit)); const zir_decl = zir.getDeclaration(inst_resolved.inst); - const type_body = zir_decl.type_body.?; var analysis_arena: std.heap.ArenaAllocator = .init(gpa); defer analysis_arena.deinit(); @@ -1607,7 +2106,7 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr .parent = null, .sema = &sema, .namespace = old_nav.analysis.?.namespace, - .instructions = .{}, + .instructions = .empty, .inlining = null, .comptime_reason = undefined, // set below .src_base_inst = old_nav.analysis.?.zir_index, @@ -1616,6 +2115,34 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr defer block.instructions.deinit(gpa); const ty_src = block.src(.{ .node_offset_var_decl_ty = .zero }); + const init_src = block.src(.{ .node_offset_var_decl_init = .zero }); + + const type_body = zir_decl.type_body orelse { + // There is no type annotation, so we just need to use the declaration's value. + // If the value had already been re-analyzed, it would have resolved the `nav_ty` unit as + // either outdated or up-to-date. So we know that `old_nav` does contain information from + // the previous update. As such, after this call, we will be able to determine whether the + // type changed. + try sema.ensureNavResolved(&block, init_src, nav_id, .fully); + const new = ip.getNav(nav_id).status.fully_resolved; + const new_is_extern_decl = ip.indexToKey(new.val) == .@"extern"; + const changed = switch (old_nav.status) { + .unresolved => true, + .type_resolved => |r| r.type != ip.typeOf(new.val) or + r.alignment != new.alignment or + r.@"linksection" != new.@"linksection" or + r.@"addrspace" != new.@"addrspace" or + r.is_const != new.is_const or + r.is_extern_decl != new_is_extern_decl, + .fully_resolved => |r| ip.typeOf(r.val) != ip.typeOf(new.val) or + r.alignment != new.alignment or + r.@"linksection" != new.@"linksection" or + r.@"addrspace" != new.@"addrspace" or + r.is_const != new.is_const or + (old_nav.getExtern(ip) != null) != new_is_extern_decl, + }; + return .{ .type_changed = changed }; + }; block.comptime_reason = .{ .reason = .{ .src = ty_src, @@ -1628,7 +2155,7 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr break :ty .fromInterned(type_ref.toInterned().?); }; - try resolved_ty.resolveLayout(pt); + try sema.ensureLayoutResolved(resolved_ty, block.nodeOffset(.zero), if (zir_decl.kind == .@"var") .variable else .constant); // In the case where the type is specified, this function is also responsible for resolving // the pointer modifiers, i.e. alignment, linksection, addrspace. @@ -1678,18 +2205,24 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr return .{ .type_changed = true }; } -pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!void { +/// If `func_index` is not a runtime function (e.g. it has a comptime-only parameter type) then it +/// is still valid to call this function and use its `func_body` unit in general---analysis of the +/// runtime function body will simply fail. +pub fn ensureFuncBodyUpToDate( + pt: Zcu.PerThread, + func_index: InternPool.Index, + /// `null` is valid only for the "root" analysis, i.e. called from `Compilation.processOneJob`. + reason: ?*const Zcu.DependencyReason, +) Zcu.SemaError!void { dev.check(.sema); - const tracy = trace(@src()); - defer tracy.end(); + const tracy_trace = trace(@src()); + defer tracy_trace.end(); const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; - _ = zcu.func_body_analysis_queued.swapRemove(func_index); - const anal_unit: AnalUnit = .wrap(.{ .func = func_index }); log.debug("ensureFuncBodyUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)}); @@ -1700,27 +2233,17 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Z assert(func.ty == func.uncoerced_ty); // analyze the body of the original function, not a coerced one - const was_outdated = zcu.outdated.swapRemove(anal_unit) or - zcu.potentially_outdated.swapRemove(anal_unit); + const was_outdated = zcu.clearOutdatedState(anal_unit) or + ip.setWantRuntimeFnAnalysis(zcu.comp.io, func_index); const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit); if (was_outdated) { - dev.check(.incremental); - _ = zcu.outdated_ready.swapRemove(anal_unit); - zcu.deleteUnitExports(anal_unit); - zcu.deleteUnitReferences(anal_unit); - zcu.deleteUnitCompileLogs(anal_unit); - if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { - kv.value.destroy(gpa); - } - _ = zcu.transitive_failed_analysis.swapRemove(anal_unit); + zcu.resetUnit(anal_unit); } else { // We can trust the current information about this function. - if (prev_failed) { - return error.AnalysisFail; - } - if (func.analysisUnordered(ip).is_analyzed) return; + if (prev_failed) return error.AnalysisFail; + return; } if (zcu.comp.debugIncremental()) { @@ -1736,7 +2259,7 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Z ); defer unit_tracking.end(zcu); - const ies_outdated, const new_failed = if (pt.analyzeFuncBody(func_index)) |result| + const ies_outdated, const new_failed = if (pt.analyzeFuncBody(func_index, reason)) |result| .{ prev_failed or result.ies_outdated, false } else |err| switch (err) { error.AnalysisFail => res: { @@ -1765,9 +2288,9 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Z if (was_outdated) { if (ies_outdated) { - try zcu.markDependeeOutdated(.marked_po, .{ .interned = func_index }); + try zcu.markDependeeOutdated(.marked_po, .{ .func_ies = func_index }); } else { - try zcu.markPoDependeeUpToDate(.{ .interned = func_index }); + try zcu.markPoDependeeUpToDate(.{ .func_ies = func_index }); } } @@ -1777,6 +2300,7 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Z fn analyzeFuncBody( pt: Zcu.PerThread, func_index: InternPool.Index, + reason: ?*const Zcu.DependencyReason, ) Zcu.SemaError!struct { ies_outdated: bool } { const zcu = pt.zcu; const gpa = zcu.gpa; @@ -1785,29 +2309,6 @@ fn analyzeFuncBody( const func = zcu.funcInfo(func_index); const anal_unit = AnalUnit.wrap(.{ .func = func_index }); - // Make sure that this function is still owned by the same `Nav`. Otherwise, analyzing - // it would be a waste of time in the best case, and could cause codegen to give bogus - // results in the worst case. - - if (func.generic_owner == .none) { - // Among another things, this ensures that the function's `zir_body_inst` is correct. - try pt.ensureNavValUpToDate(func.owner_nav); - if (ip.getNav(func.owner_nav).status.fully_resolved.val != func_index) { - // This function is no longer referenced! There's no point in re-analyzing it. - // Just mark a transitive failure and move on. - return error.AnalysisFail; - } - } else { - const go_nav = zcu.funcInfo(func.generic_owner).owner_nav; - // Among another things, this ensures that the function's `zir_body_inst` is correct. - try pt.ensureNavValUpToDate(go_nav); - if (ip.getNav(go_nav).status.fully_resolved.val != func.generic_owner) { - // The generic owner is no longer referenced, so this function is also unreferenced. - // There's no point in re-analyzing it. Just mark a transitive failure and move on. - return error.AnalysisFail; - } - } - // We'll want to remember what the IES used to be before the update for // dependency invalidation purposes. const old_resolved_ies = if (func.analysisUnordered(ip).inferred_error_set) @@ -1817,8 +2318,9 @@ fn analyzeFuncBody( log.debug("analyze and generate fn body {f}", .{zcu.fmtAnalUnit(anal_unit)}); - var air = try pt.analyzeFnBodyInner(func_index); - errdefer air.deinit(gpa); + var air = try pt.analyzeFuncBodyInner(func_index, reason); + var air_owned = true; + defer if (air_owned) air.deinit(gpa); const ies_outdated = !func.analysisUnordered(ip).inferred_error_set or func.resolvedErrorSetUnordered(ip) != old_resolved_ies; @@ -1828,103 +2330,24 @@ fn analyzeFuncBody( const dump_air = build_options.enable_debug_extensions and comp.verbose_air; const dump_llvm_ir = build_options.enable_debug_extensions and (comp.verbose_llvm_ir != null or comp.verbose_llvm_bc != null); - if (comp.bin_file == null and zcu.llvm_object == null and !dump_air and !dump_llvm_ir) { - air.deinit(gpa); - return .{ .ies_outdated = ies_outdated }; - } - - // This job depends on any resolve_type_fully jobs queued up before it. - zcu.codegen_prog_node.increaseEstimatedTotalItems(1); - comp.link_prog_node.increaseEstimatedTotalItems(1); - try comp.queueJob(.{ .codegen_func = .{ - .func = func_index, - .air = air, - } }); - - return .{ .ies_outdated = ies_outdated }; -} - -pub fn semaMod(pt: Zcu.PerThread, mod: *Module) !void { - dev.check(.sema); - const file_index = pt.zcu.module_roots.get(mod).?.unwrap().?; - const root_type = pt.zcu.fileRootType(file_index); - if (root_type == .none) { - return pt.semaFile(file_index); - } -} - -fn createFileRootStruct( - pt: Zcu.PerThread, - file_index: Zcu.File.Index, - namespace_index: Zcu.Namespace.Index, - replace_existing: bool, -) Allocator.Error!InternPool.Index { - const zcu = pt.zcu; - const gpa = zcu.gpa; - const io = zcu.comp.io; - const ip = &zcu.intern_pool; - const file = zcu.fileByIndex(file_index); - const extended = file.zir.?.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended; - assert(extended.opcode == .struct_decl); - const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); - assert(!small.has_captures_len); - assert(!small.has_backing_int); - assert(small.layout == .auto); - var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len; - const fields_len = if (small.has_fields_len) blk: { - const fields_len = file.zir.?.extra[extra_index]; - extra_index += 1; - break :blk fields_len; - } else 0; - const decls_len = if (small.has_decls_len) blk: { - const decls_len = file.zir.?.extra[extra_index]; - extra_index += 1; - break :blk decls_len; - } else 0; - const decls = file.zir.?.bodySlice(extra_index, decls_len); - extra_index += decls_len; + if (comp.bin_file != null or zcu.llvm_object != null or dump_air or dump_llvm_ir) { + zcu.codegen_prog_node.increaseEstimatedTotalItems(1); + comp.link_prog_node.increaseEstimatedTotalItems(1); - const tracked_inst = try ip.trackZir(gpa, io, pt.tid, .{ - .file = file_index, - .inst = .main_struct_inst, - }); - const wip_ty = switch (try ip.getStructType(gpa, io, pt.tid, .{ - .layout = .auto, - .fields_len = fields_len, - .known_non_opv = small.known_non_opv, - .requires_comptime = if (small.known_comptime_only) .yes else .unknown, - .any_comptime_fields = small.any_comptime_fields, - .any_default_inits = small.any_default_inits, - .inits_resolved = false, - .any_aligned_fields = small.any_aligned_fields, - .key = .{ .declared = .{ - .zir_index = tracked_inst, - .captures = &.{}, - } }, - }, replace_existing)) { - .existing => unreachable, // we wouldn't be analysing the file root if this type existed - .wip => |wip| wip, - }; - errdefer wip_ty.cancel(ip, pt.tid); + // Some linkers need to refer to the AIR. In that case, the linker is not running + // concurrently, so we'll just keep ownership of the AIR for ourselves instead of + // letting the codegen job destroy it. + const disown_air = zcu.backendSupportsFeature(.separate_thread); - wip_ty.setName(ip, try file.internFullyQualifiedName(pt), .none); - ip.namespacePtr(namespace_index).owner_type = wip_ty.index; + // Begin the codegen task. If the codegen/link queue is backed up, this might + // block until the linker is able to process some tasks. + const codegen_task = try zcu.codegen_task_pool.start(zcu, func_index, &air, disown_air); + if (disown_air) air_owned = false; - if (zcu.comp.config.incremental) { - try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst }); + try comp.link_queue.enqueueZcu(comp, pt.tid, .{ .link_func = codegen_task }); } - try pt.scanNamespace(namespace_index, decls); - try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); - codegen_type: { - if (file.mod.?.strip) break :codegen_type; - // This job depends on any resolve_type_fully jobs queued up before it. - zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); - try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); - } - zcu.setFileRootType(file_index, wip_ty.index); - if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); - return wip_ty.finish(ip, namespace_index); + return .{ .ies_outdated = ies_outdated }; } /// Re-scan the namespace of a file's root struct type on an incremental update. @@ -1945,48 +2368,11 @@ fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator. }); const namespace_index = Type.fromInterned(file_root_type).getNamespaceIndex(zcu); - const decls = decls: { - const extended = file.zir.?.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended; - const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); - - var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len; - extra_index += @intFromBool(small.has_fields_len); - const decls_len = if (small.has_decls_len) blk: { - const decls_len = file.zir.?.extra[extra_index]; - extra_index += 1; - break :blk decls_len; - } else 0; - break :decls file.zir.?.bodySlice(extra_index, decls_len); - }; + const decls = file.zir.?.getStructDecl(.main_struct_inst).decls; try pt.scanNamespace(namespace_index, decls); zcu.namespacePtr(namespace_index).generation = zcu.generation; } -fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { - const tracy = trace(@src()); - defer tracy.end(); - - const zcu = pt.zcu; - const file = zcu.fileByIndex(file_index); - assert(file.getMode() == .zig); - assert(zcu.fileRootType(file_index) == .none); - - assert(file.zir != null); - - const new_namespace_index = try pt.createNamespace(.{ - .parent = .none, - .owner_type = undefined, // set in `createFileRootStruct` - .file_scope = file_index, - .generation = zcu.generation, - }); - const struct_ty = try pt.createFileRootStruct(file_index, new_namespace_index, false); - errdefer zcu.intern_pool.remove(pt.tid, struct_ty); - - if (zcu.comp.time_report) |*tr| { - tr.stats.n_imported_files += 1; - } -} - /// Called by AstGen worker threads when an import is seen. If `new_file` is returned, the caller is /// then responsible for queueing a new AstGen job for the new file. /// Assumes that `comp.mutex` is NOT locked. It will be locked by this function where necessary. @@ -2214,7 +2600,7 @@ pub fn populateModuleRootTable(pt: Zcu.PerThread) error{ /// modify `pt.zcu.skip_analysis_this_update`. /// /// If an error is returned, `pt.zcu.alive_files` might contain undefined values. -pub fn computeAliveFiles(pt: Zcu.PerThread) Allocator.Error!bool { +fn computeAliveFiles(pt: Zcu.PerThread) Allocator.Error!bool { const zcu = pt.zcu; const comp = zcu.comp; const gpa = zcu.gpa; @@ -2655,8 +3041,8 @@ pub fn scanNamespace( namespace_index: Zcu.Namespace.Index, decls: []const Zir.Inst.Index, ) Allocator.Error!void { - const tracy = trace(@src()); - defer tracy.end(); + const tracy_trace = trace(@src()); + defer tracy_trace.end(); const zcu = pt.zcu; const ip = &zcu.intern_pool; @@ -2752,8 +3138,8 @@ const ScanDeclIter = struct { } fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void { - const tracy = trace(@src()); - defer tracy.end(); + const tracy_trace = trace(@src()); + defer tracy_trace.end(); const pt = iter.pt; const zcu = pt.zcu; @@ -2806,89 +3192,76 @@ const ScanDeclIter = struct { const existing_unit = iter.existing_by_inst.get(tracked_inst); - const unit, const want_analysis = switch (decl.kind) { - .@"comptime" => unit: { - const cu = if (existing_unit) |eu| - eu.unwrap().@"comptime" - else - try ip.createComptimeUnit(gpa, io, pt.tid, tracked_inst, namespace_index); - - const unit: AnalUnit = .wrap(.{ .@"comptime" = cu }); - + const name = maybe_name.unwrap() orelse { + // Only `comptime` declarations are unnamed. + assert(decl.kind == .@"comptime"); + if (existing_unit) |unit| { + try namespace.comptime_decls.append(gpa, unit.unwrap().@"comptime"); + } else { + const cu = try ip.createComptimeUnit(gpa, io, pt.tid, tracked_inst, namespace_index); + try zcu.queueComptimeUnitAnalysis(cu); try namespace.comptime_decls.append(gpa, cu); + } + return; + }; - if (existing_unit == null) { - // For a `comptime` declaration, whether to analyze is based solely on whether the unit - // is outdated. So, add this fresh one to `outdated` and `outdated_ready`. - try zcu.outdated.ensureUnusedCapacity(gpa, 1); - try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1); - zcu.outdated.putAssumeCapacityNoClobber(unit, 0); - zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {}); - } + const fqn = try namespace.internFullyQualifiedName(ip, gpa, io, pt.tid, name); + + const nav = if (existing_unit) |unit| nav: { + const nav = unit.unwrap().nav_val; + assert(ip.getNav(nav).name == name); + assert(ip.getNav(nav).fqn == fqn); + break :nav nav; + } else nav: { + const nav = try ip.createDeclNav(gpa, io, pt.tid, name, fqn, tracked_inst, namespace_index); + if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newNav(zcu, nav); + break :nav nav; + }; - break :unit .{ unit, true }; + const want_analysis: bool = switch (decl.kind) { + .@"comptime" => unreachable, + .unnamed_test, .@"test", .decltest => a: { + const is_named = decl.kind != .unnamed_test; + try namespace.test_decls.append(gpa, nav); + // TODO: incremental compilation! + // * remove from `test_functions` if no longer matching filter + // * add to `test_functions` if newly passing filter + // This logic is unaware of incremental: we'll end up with duplicates. + // Perhaps we should add all test indiscriminately and filter at the end of the update. + if (!comp.config.is_test) break :a false; + if (file.mod != zcu.main_mod) break :a false; + if (is_named and comp.test_filters.len > 0) { + const fqn_slice = fqn.toSlice(ip); + for (comp.test_filters) |test_filter| { + if (std.mem.indexOf(u8, fqn_slice, test_filter) != null) break; + } else break :a false; + } + try zcu.test_functions.put(gpa, nav, {}); + break :a true; }, - else => unit: { - const name = maybe_name.unwrap().?; - const fqn = try namespace.internFullyQualifiedName(ip, gpa, io, pt.tid, name); - const nav = if (existing_unit) |eu| eu.unwrap().nav_val else nav: { - const nav = try ip.createDeclNav(gpa, io, pt.tid, name, fqn, tracked_inst, namespace_index); - if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newNav(zcu, nav); - break :nav nav; - }; - - const unit: AnalUnit = .wrap(.{ .nav_val = nav }); - - assert(ip.getNav(nav).name == name); - assert(ip.getNav(nav).fqn == fqn); - - const want_analysis = switch (decl.kind) { - .@"comptime" => unreachable, - .unnamed_test, .@"test", .decltest => a: { - const is_named = decl.kind != .unnamed_test; - try namespace.test_decls.append(gpa, nav); - // TODO: incremental compilation! - // * remove from `test_functions` if no longer matching filter - // * add to `test_functions` if newly passing filter - // This logic is unaware of incremental: we'll end up with duplicates. - // Perhaps we should add all test indiscriminately and filter at the end of the update. - if (!comp.config.is_test) break :a false; - if (file.mod != zcu.main_mod) break :a false; - if (is_named and comp.test_filters.len > 0) { - const fqn_slice = fqn.toSlice(ip); - for (comp.test_filters) |test_filter| { - if (std.mem.indexOf(u8, fqn_slice, test_filter) != null) break; - } else break :a false; - } - try zcu.test_functions.put(gpa, nav, {}); - break :a true; - }, - .@"const", .@"var" => a: { - if (decl.is_pub) { - try namespace.pub_decls.putContext(gpa, nav, {}, .{ .zcu = zcu }); - } else { - try namespace.priv_decls.putContext(gpa, nav, {}, .{ .zcu = zcu }); - } - break :a false; - }, - }; - break :unit .{ unit, want_analysis }; + .@"const", .@"var" => a: { + if (decl.is_pub) { + try namespace.pub_decls.putContext(gpa, nav, {}, .{ .zcu = zcu }); + } else { + try namespace.priv_decls.putContext(gpa, nav, {}, .{ .zcu = zcu }); + } + break :a false; }, }; - if (existing_unit == null and (want_analysis or decl.linkage == .@"export")) { - log.debug( - "scanDecl queue analyze_comptime_unit file='{s}' unit={f}", - .{ namespace.fileScope(zcu).sub_file_path, zcu.fmtAnalUnit(unit) }, - ); - try comp.queueJob(.{ .analyze_comptime_unit = unit }); + if (want_analysis or decl.linkage == .@"export") { + try zcu.ensureNavValAnalysisQueued(nav); } } }; -fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!Air { - const tracy = trace(@src()); - defer tracy.end(); +fn analyzeFuncBodyInner( + pt: Zcu.PerThread, + func_index: InternPool.Index, + reason: ?*const Zcu.DependencyReason, +) Zcu.SemaError!Air { + const tracy_trace = trace(@src()); + defer tracy_trace.end(); const zcu = pt.zcu; const comp = zcu.comp; @@ -2898,17 +3271,18 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE const anal_unit = AnalUnit.wrap(.{ .func = func_index }); const func = zcu.funcInfo(func_index); - const inst_info = func.zir_body_inst.resolveFull(ip) orelse return error.AnalysisFail; - const file = zcu.fileByIndex(inst_info.file); - const zir = file.zir.?; - try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {}); - errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit); + // This is the `Nav` corresponding to the `declaration` instruction which the function or its generic owner originates from. + const decl_analysis = if (func.generic_owner == .none) + ip.getNav(func.owner_nav).analysis.? + else + ip.getNav(zcu.funcInfo(func.generic_owner).owner_nav).analysis.?; - func.setAnalyzed(ip, io); - if (func.analysisUnordered(ip).inferred_error_set) { - func.setResolvedErrorSet(ip, io, .none); - } + const file = zcu.fileByIndex(decl_analysis.zir_index.resolveFile(ip)); + const zir = file.zir.?; + + try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, reason); + defer assert(zcu.analysis_in_progress.swapRemove(anal_unit)); if (zcu.comp.time_report) |*tr| { if (func.generic_owner != .none) { @@ -2916,16 +3290,8 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE } } - // This is the `Nau` corresponding to the `declaration` instruction which the function or its generic owner originates from. - const decl_nav = ip.getNav(if (func.generic_owner == .none) - func.owner_nav - else - zcu.funcInfo(func.generic_owner).owner_nav); - const func_nav = ip.getNav(func.owner_nav); - zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit); - var analysis_arena = std.heap.ArenaAllocator.init(gpa); defer analysis_arena.deinit(); @@ -2957,9 +3323,30 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE // Every runtime function has a dependency on the source of the Decl it originates from. // It also depends on the value of its owner Decl. - try sema.declareDependency(.{ .src_hash = decl_nav.analysis.?.zir_index }); + try sema.declareDependency(.{ .src_hash = decl_analysis.zir_index }); try sema.declareDependency(.{ .nav_val = func.owner_nav }); + // Make sure that the declaration `Nav` still refers to this function (or its generic owner). + // This will not be the case if the incremental update has changed a function type or turned a + // `fn` decl into some other declaration. In that case, we must not run analysis: this function + // will not be referenced this update, and trying to generate it could be problematic since we + // assume the owner NAV actually, um, owns us. + // + // If we *are* still owned by the right NAV, this analysis updates `zir_body_inst` if necessary. + + if (func.generic_owner == .none) { + try pt.ensureNavValUpToDate(func.owner_nav, reason); + if (ip.getNav(func.owner_nav).status.fully_resolved.val != func_index) { + return error.AnalysisFail; + } + } else { + const go_nav = zcu.funcInfo(func.generic_owner).owner_nav; + try pt.ensureNavValUpToDate(go_nav, reason); + if (ip.getNav(go_nav).status.fully_resolved.val != func.generic_owner) { + return error.AnalysisFail; + } + } + if (func.analysisUnordered(ip).inferred_error_set) { const ies = try analysis_arena.allocator().create(Sema.InferredErrorSet); ies.* = .{ .func = func_index }; @@ -2977,11 +3364,11 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE var inner_block: Sema.Block = .{ .parent = null, .sema = &sema, - .namespace = decl_nav.analysis.?.namespace, - .instructions = .{}, + .namespace = decl_analysis.namespace, + .instructions = .empty, .inlining = null, .comptime_reason = null, - .src_base_inst = decl_nav.analysis.?.zir_index, + .src_base_inst = decl_analysis.zir_index, .type_name_ctx = func_nav.fqn, }; defer inner_block.instructions.deinit(gpa); @@ -3020,16 +3407,21 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE const gop = sema.inst_map.getOrPutAssumeCapacity(inst); if (gop.found_existing) continue; // provided above by comptime arg - const param_ty = fn_ty_info.param_types.get(ip)[runtime_param_index]; + const param_ty: Type = .fromInterned(fn_ty_info.param_types.get(ip)[runtime_param_index]); runtime_param_index += 1; - const opt_opv = sema.typeHasOnePossibleValue(Type.fromInterned(param_ty)) catch |err| switch (err) { - error.ComptimeReturn => unreachable, - error.ComptimeBreak => unreachable, - else => |e| return e, - }; - if (opt_opv) |opv| { - gop.value_ptr.* = Air.internedToRef(opv.toIntern()); + if (param_ty.isGenericPoison()) { + // We're guaranteed to get a compile error on the `fnHasRuntimeBits` check after this + // loop (the generic poison means this is a generic function). But `continue` here to + // avoid an illegal call to `onePossibleValue` below. + continue; + } + + const param_ty_src = inner_block.src(.{ .func_decl_param_ty = @intCast(zir_param_index) }); + + try sema.ensureLayoutResolved(param_ty, param_ty_src, .parameter); + if (try param_ty.onePossibleValue(pt)) |opv| { + gop.value_ptr.* = .fromValue(opv); continue; } const arg_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); @@ -3038,12 +3430,31 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE sema.air_instructions.appendAssumeCapacity(.{ .tag = .arg, .data = .{ .arg = .{ - .ty = Air.internedToRef(param_ty), + .ty = .fromIntern(param_ty.toIntern()), .zir_param_index = @intCast(zir_param_index), } }, }); } + try sema.ensureLayoutResolved(sema.fn_ret_ty, inner_block.src(.{ .node_offset_fn_type_ret_ty = .zero }), .return_type); + + // The function type is now resolved, so we're ready to check whether it even makes sense to ask + // for it to be analyzed at runtime. + if (!fn_ty.fnHasRuntimeBits(zcu)) { + const description: []const u8 = switch (fn_ty_info.cc) { + .@"inline" => "inline", + else => "generic", + }; + // This error makes sense because the only reason this analysis would ever be requested is + // for IES resolution. + return sema.fail( + &inner_block, + inner_block.nodeOffset(.zero), + "cannot resolve inferred error set of {s} function type '{f}'", + .{ description, fn_ty.fmt(pt) }, + ); + } + const last_arg_index = inner_block.instructions.items.len; // Save the error trace as our first action in the function. @@ -3103,21 +3514,6 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE func.setResolvedErrorSet(ip, io, ies.resolved); } - assert(zcu.analysis_in_progress.swapRemove(anal_unit)); - - // Finally we must resolve the return type and parameter types so that backends - // have full access to type information. - // Crucially, this happens *after* we set the function state to success above, - // so that dependencies on the function body will now be satisfied rather than - // result in circular dependency errors. - // TODO: this can go away once we fix backends having to resolve `StackTrace`. - // The codegen timing guarantees that the parameter types will be populated. - sema.resolveFnTypes(fn_ty, inner_block.nodeOffset(.zero)) catch |err| switch (err) { - error.ComptimeReturn => unreachable, - error.ComptimeBreak => unreachable, - else => |e| return e, - }; - try sema.flushExports(); defer { @@ -3244,7 +3640,7 @@ pub fn processExports(pt: Zcu.PerThread) !void { break :gop .{ gop.value_ptr, gop.found_existing }; }, }; - if (!found_existing) value_ptr.* = .{}; + if (!found_existing) value_ptr.* = .empty; try value_ptr.append(gpa, export_idx); } @@ -3273,7 +3669,7 @@ pub fn processExports(pt: Zcu.PerThread) !void { break :gop .{ gop.value_ptr, gop.found_existing }; }, }; - if (!found_existing) value_ptr.* = .{}; + if (!found_existing) value_ptr.* = .empty; try value_ptr.append(gpa, @enumFromInt(export_idx)); } } @@ -3545,36 +3941,45 @@ pub fn intern(pt: Zcu.PerThread, key: InternPool.Key) Allocator.Error!InternPool /// Essentially a shortcut for calling `intern_pool.getCoerced`. /// However, this function also allows coercing `extern`s. The `InternPool` function can't do -/// this because it requires potentially pushing to the job queue. +/// this because it requires potentially queueing a link task. pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!Value { const ip = &pt.zcu.intern_pool; const comp = pt.zcu.comp; const gpa = comp.gpa; const io = comp.io; switch (ip.indexToKey(val.toIntern())) { - .@"extern" => |e| { - const coerced = try pt.getExtern(.{ - .name = e.name, + .@"extern" => |@"extern"| { + // TODO: it's awkward to make this function cancelable. The problem is really that + // `getCoerced` is a bad API: it should be replaced with smaller, more specialized + // functions, so that this cancel point is only possible in the rare case that you + // may actually need to coerce an extern! + const old_prot = io.swapCancelProtection(.blocked); + defer _ = io.swapCancelProtection(old_prot); + const coerced = pt.getExtern(.{ + .name = @"extern".name, .ty = new_ty.toIntern(), - .lib_name = e.lib_name, - .is_const = e.is_const, - .is_threadlocal = e.is_threadlocal, - .linkage = e.linkage, - .visibility = e.visibility, - .is_dll_import = e.is_dll_import, - .relocation = e.relocation, - .decoration = e.decoration, - .alignment = e.alignment, - .@"addrspace" = e.@"addrspace", - .zir_index = e.zir_index, + .lib_name = @"extern".lib_name, + .is_const = @"extern".is_const, + .is_threadlocal = @"extern".is_threadlocal, + .linkage = @"extern".linkage, + .visibility = @"extern".visibility, + .is_dll_import = @"extern".is_dll_import, + .relocation = @"extern".relocation, + .decoration = @"extern".decoration, + .alignment = @"extern".alignment, + .@"addrspace" = @"extern".@"addrspace", + .zir_index = @"extern".zir_index, .owner_nav = undefined, // ignored by `getExtern`. - .source = e.source, - }); - return Value.fromInterned(coerced); + .source = @"extern".source, + }) catch |err| switch (err) { + error.Canceled => unreachable, // blocked above + error.OutOfMemory => |e| return e, + }; + return .fromInterned(coerced); }, else => {}, } - return Value.fromInterned(try ip.getCoerced(gpa, io, pt.tid, val.toIntern(), new_ty.toIntern())); + return .fromInterned(try ip.getCoerced(gpa, io, pt.tid, val.toIntern(), new_ty.toIntern())); } pub fn intType(pt: Zcu.PerThread, signedness: std.builtin.Signedness, bits: u16) Allocator.Error!Type { @@ -3605,16 +4010,6 @@ pub fn ptrType(pt: Zcu.PerThread, info: InternPool.Key.PtrType) Allocator.Error! if (info.flags.size == .c) canon_info.flags.is_allowzero = true; - // Canonicalize non-zero alignment. If it matches the ABI alignment of the pointee - // type, we change it to 0 here. If this causes an assertion trip because the - // pointee type needs to be resolved more, that needs to be done before calling - // this ptr() function. - if (info.flags.alignment != .none and - info.flags.alignment == Type.fromInterned(info.child).abiAlignment(pt.zcu)) - { - canon_info.flags.alignment = .none; - } - switch (info.flags.vector_index) { // Canonicalize host_size. If it matches the bit size of the pointee type, // we change it to 0 here. If this causes an assertion trip, the pointee type @@ -3632,16 +4027,6 @@ pub fn ptrType(pt: Zcu.PerThread, info: InternPool.Key.PtrType) Allocator.Error! return Type.fromInterned(try pt.intern(.{ .ptr_type = canon_info })); } -/// Like `ptrType`, but if `info` specifies an `alignment`, first ensures the pointer -/// child type's alignment is resolved so that an invalid alignment is not used. -/// In general, prefer this function during semantic analysis. -pub fn ptrTypeSema(pt: Zcu.PerThread, info: InternPool.Key.PtrType) Zcu.SemaError!Type { - if (info.flags.alignment != .none) { - _ = try Type.fromInterned(info.child).abiAlignmentSema(pt); - } - return pt.ptrType(info); -} - pub fn singleMutPtrType(pt: Zcu.PerThread, child_type: Type) Allocator.Error!Type { return pt.ptrType(.{ .child = child_type.toIntern() }); } @@ -3741,29 +4126,54 @@ pub fn enumValueFieldIndex(pt: Zcu.PerThread, ty: Type, field_index: u32) Alloca const ip = &pt.zcu.intern_pool; const enum_type = ip.loadEnumType(ty.toIntern()); - if (enum_type.values.len == 0) { + assert(field_index < enum_type.field_names.len); + + if (enum_type.field_values.len == 0) { // Auto-numbered fields. return Value.fromInterned(try pt.intern(.{ .enum_tag = .{ .ty = ty.toIntern(), .int = try pt.intern(.{ .int = .{ - .ty = enum_type.tag_ty, + .ty = enum_type.int_tag_type, .storage = .{ .u64 = field_index }, } }), } })); } - return Value.fromInterned(try pt.intern(.{ .enum_tag = .{ + return .fromInterned(try pt.intern(.{ .enum_tag = .{ .ty = ty.toIntern(), - .int = enum_type.values.get(ip)[field_index], + .int = enum_type.field_values.get(ip)[field_index], } })); } pub fn undefValue(pt: Zcu.PerThread, ty: Type) Allocator.Error!Value { - return Value.fromInterned(try pt.intern(.{ .undef = ty.toIntern() })); + if (std.debug.runtime_safety) { + // TODO: values of type `struct { comptime x: u8 = undefined }` are currently represented as + // undef. This is wrong: they should really be represented as empty aggregates instead, + // because `comptime` fields shouldn't factor into that decision! This is implemented + // through logic in `aggregateValue` and requires this weird workaround in what ought to be + // a straightforward assertion: + //assert(ty.classify(pt.zcu) != .one_possible_value); + if (ty.classify(pt.zcu) == .one_possible_value) { + const ip = &pt.zcu.intern_pool; + switch (ip.indexToKey(ty.toIntern())) { + else => unreachable, // assertion failure + .struct_type => { + const comptime_bits = ip.loadStructType(ty.toIntern()).field_is_comptime_bits.getAll(ip); + for (comptime_bits) |bag| { + if (@popCount(bag) > 0) break; + } else unreachable; // assertion failure + }, + .tuple_type => |tuple| for (tuple.values.get(ip)) |val| { + if (val != .none) break; + } else unreachable, // assertion failure + } + } + } + return .fromInterned(try pt.intern(.{ .undef = ty.toIntern() })); } pub fn undefRef(pt: Zcu.PerThread, ty: Type) Allocator.Error!Air.Inst.Ref { - return Air.internedToRef((try pt.undefValue(ty)).toIntern()); + return .fromValue(try pt.undefValue(ty)); } pub fn intValue(pt: Zcu.PerThread, ty: Type, x: anytype) Allocator.Error!Value { @@ -3839,7 +4249,7 @@ pub fn aggregateValue(pt: Zcu.PerThread, ty: Type, elems: []const InternPool.Ind for (elems) |elem| { if (!Value.fromInterned(elem).isUndef(pt.zcu)) break; } else if (elems.len > 0) { - return pt.undefValue(ty); // all-undef + return pt.undefValue(ty); } return .fromInterned(try pt.intern(.{ .aggregate = .{ .ty = ty.toIntern(), @@ -3877,6 +4287,15 @@ pub fn floatValue(pt: Zcu.PerThread, ty: Type, x: anytype) Allocator.Error!Value } })); } +/// Create a value whose type is a `packed struct` or `packed union`, from the backing integer value. +pub fn bitpackValue(pt: Zcu.PerThread, ty: Type, backing_int_val: Value) Allocator.Error!Value { + assert(backing_int_val.typeOf(pt.zcu).toIntern() == ty.bitpackBackingInt(pt.zcu).toIntern()); + return .fromInterned(try pt.intern(.{ .bitpack = .{ + .ty = ty.toIntern(), + .backing_int_val = backing_int_val.toIntern(), + } })); +} + pub fn nullValue(pt: Zcu.PerThread, opt_ty: Type) Allocator.Error!Value { assert(pt.zcu.intern_pool.isOptionalType(opt_ty.toIntern())); return Value.fromInterned(try pt.intern(.{ .opt = .{ @@ -3916,7 +4335,7 @@ pub fn intFittingRange(pt: Zcu.PerThread, min: Value, max: Value) !Type { assert(Value.order(min, max, zcu).compare(.lte)); } - const sign = min.orderAgainstZero(zcu) == .lt; + const sign = min.compareHetero(.lt, .zero_comptime_int, zcu); const min_val_bits = pt.intBitsForValue(min, sign); const max_val_bits = pt.intBitsForValue(max, sign); @@ -3955,12 +4374,6 @@ pub fn intBitsForValue(pt: Zcu.PerThread, val: Value, sign: bool) u16 { return @as(u16, @intCast(big.bitCountTwosComp())); }, - .lazy_align => |lazy_ty| { - return Type.smallestUnsignedBits(Type.fromInterned(lazy_ty).abiAlignment(pt.zcu).toByteUnits() orelse 0) + @intFromBool(sign); - }, - .lazy_size => |lazy_ty| { - return Type.smallestUnsignedBits(Type.fromInterned(lazy_ty).abiSize(pt.zcu)) + @intFromBool(sign); - }, } } @@ -3975,10 +4388,7 @@ pub fn navPtrType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Allocator.Err return pt.ptrType(.{ .child = ty, .flags = .{ - .alignment = if (alignment == Type.fromInterned(ty).abiAlignment(zcu)) - .none - else - alignment, + .alignment = alignment, .address_space = @"addrspace", .is_const = is_const, }, @@ -3988,392 +4398,19 @@ pub fn navPtrType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Allocator.Err /// Intern an `.@"extern"`, creating a corresponding owner `Nav` if necessary. /// If necessary, the new `Nav` is queued for codegen. /// `key.owner_nav` is ignored and may be `undefined`. -pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error!InternPool.Index { +pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) (Io.Cancelable || Allocator.Error)!InternPool.Index { const zcu = pt.zcu; const comp = zcu.comp; + Type.fromInterned(key.ty).assertHasLayout(zcu); const result = try zcu.intern_pool.getExtern(comp.gpa, comp.io, pt.tid, key); if (result.new_nav.unwrap()) |nav| { - // This job depends on any resolve_type_fully jobs queued up before it. - comp.link_prog_node.increaseEstimatedTotalItems(1); - try comp.queueJob(.{ .link_nav = nav }); if (comp.debugIncremental()) try zcu.incremental_debug_state.newNav(zcu, nav); + comp.link_prog_node.increaseEstimatedTotalItems(1); + try comp.link_queue.enqueueZcu(comp, pt.tid, .{ .link_nav = nav }); } return result.index; } -// TODO: this shouldn't need a `PerThread`! Fix the signature of `Type.abiAlignment`. -pub fn navAlignment(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) InternPool.Alignment { - const zcu = pt.zcu; - const ty: Type, const alignment = switch (zcu.intern_pool.getNav(nav_index).status) { - .unresolved => unreachable, - .type_resolved => |r| .{ .fromInterned(r.type), r.alignment }, - .fully_resolved => |r| .{ Value.fromInterned(r.val).typeOf(zcu), r.alignment }, - }; - if (alignment != .none) return alignment; - return ty.abiAlignment(zcu); -} - -/// `ty` is a container type requiring resolution (struct, union, or enum). -/// If `ty` is outdated, it is recreated at a new `InternPool.Index`, which is returned. -/// If the type cannot be recreated because it has been lost, `error.AnalysisFail` is returned. -/// If `ty` is not outdated, that same `InternPool.Index` is returned. -/// If `ty` has already been replaced by this function, the new index will not be returned again. -/// Also, if `ty` is an enum, this function will resolve the new type if needed, and the call site -/// is responsible for checking `[transitive_]failed_analysis` to detect resolution failures. -pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index) Zcu.SemaError!InternPool.Index { - const zcu = pt.zcu; - const gpa = zcu.gpa; - const ip = &zcu.intern_pool; - - const anal_unit: AnalUnit = .wrap(.{ .type = ty }); - const outdated = zcu.outdated.swapRemove(anal_unit) or - zcu.potentially_outdated.swapRemove(anal_unit); - - if (outdated) { - _ = zcu.outdated_ready.swapRemove(anal_unit); - try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty }); - } - - const ty_key = switch (ip.indexToKey(ty)) { - .struct_type, .union_type, .enum_type => |key| key, - else => unreachable, - }; - const declared_ty_key = switch (ty_key) { - .reified => unreachable, // never outdated - .generated_tag => unreachable, // never outdated - .declared => |d| d, - }; - - if (declared_ty_key.zir_index.resolve(ip) == null) { - // The instruction has been lost -- this type is dead. - return error.AnalysisFail; - } - - if (!outdated) return ty; - - // We will recreate the type at a new `InternPool.Index`. - - // Delete old state which is no longer in use. Technically, this is not necessary: these exports, - // references, etc, will be ignored because the type itself is unreferenced. However, it allows - // reusing the memory which is currently being used to track this state. - zcu.deleteUnitExports(anal_unit); - zcu.deleteUnitReferences(anal_unit); - zcu.deleteUnitCompileLogs(anal_unit); - if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { - kv.value.destroy(gpa); - } - _ = zcu.transitive_failed_analysis.swapRemove(anal_unit); - zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit); - - if (zcu.comp.debugIncremental()) { - const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit); - info.last_update_gen = zcu.generation; - info.deps.clearRetainingCapacity(); - } - - switch (ip.indexToKey(ty)) { - .struct_type => return pt.recreateStructType(ty, declared_ty_key), - .union_type => return pt.recreateUnionType(ty, declared_ty_key), - .enum_type => return pt.recreateEnumType(ty, declared_ty_key), - else => unreachable, - } -} - -fn recreateStructType( - pt: Zcu.PerThread, - old_ty: InternPool.Index, - key: InternPool.Key.NamespaceType.Declared, -) Allocator.Error!InternPool.Index { - const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = comp.gpa; - const io = comp.io; - const ip = &zcu.intern_pool; - - const inst_info = key.zir_index.resolveFull(ip).?; - const file = zcu.fileByIndex(inst_info.file); - const zir = file.zir.?; - - assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); - const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended; - assert(extended.opcode == .struct_decl); - const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); - const extra = zir.extraData(Zir.Inst.StructDecl, extended.operand); - var extra_index = extra.end; - - const captures_len = if (small.has_captures_len) blk: { - const captures_len = zir.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - const fields_len = if (small.has_fields_len) blk: { - const fields_len = zir.extra[extra_index]; - extra_index += 1; - break :blk fields_len; - } else 0; - - assert(captures_len == key.captures.owned.len); // synchronises with logic in `Zcu.mapOldZirToNew` - - const struct_obj = ip.loadStructType(old_ty); - - const wip_ty = switch (try ip.getStructType(gpa, io, pt.tid, .{ - .layout = small.layout, - .fields_len = fields_len, - .known_non_opv = small.known_non_opv, - .requires_comptime = if (small.known_comptime_only) .yes else .unknown, - .any_comptime_fields = small.any_comptime_fields, - .any_default_inits = small.any_default_inits, - .inits_resolved = false, - .any_aligned_fields = small.any_aligned_fields, - .key = .{ .declared_owned_captures = .{ - .zir_index = key.zir_index, - .captures = key.captures.owned, - } }, - }, true)) { - .wip => |wip| wip, - .existing => unreachable, // we passed `replace_existing` - }; - errdefer wip_ty.cancel(ip, pt.tid); - - wip_ty.setName(ip, struct_obj.name, struct_obj.name_nav); - try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = key.zir_index }); - zcu.namespacePtr(struct_obj.namespace).owner_type = wip_ty.index; - // No need to re-scan the namespace -- `zirStructDecl` will ultimately do that if the type is still alive. - try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); - - codegen_type: { - if (file.mod.?.strip) break :codegen_type; - // This job depends on any resolve_type_fully jobs queued up before it. - zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); - try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); - } - - if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); - const new_ty = wip_ty.finish(ip, struct_obj.namespace); - if (inst_info.inst == .main_struct_inst) { - // This is the root type of a file! Update the reference. - zcu.setFileRootType(inst_info.file, new_ty); - } - return new_ty; -} - -fn recreateUnionType( - pt: Zcu.PerThread, - old_ty: InternPool.Index, - key: InternPool.Key.NamespaceType.Declared, -) Allocator.Error!InternPool.Index { - const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = comp.gpa; - const io = comp.io; - const ip = &zcu.intern_pool; - - const inst_info = key.zir_index.resolveFull(ip).?; - const file = zcu.fileByIndex(inst_info.file); - const zir = file.zir.?; - - assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); - const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended; - assert(extended.opcode == .union_decl); - const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); - const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand); - var extra_index = extra.end; - - extra_index += @intFromBool(small.has_tag_type); - const captures_len = if (small.has_captures_len) blk: { - const captures_len = zir.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - extra_index += @intFromBool(small.has_body_len); - const fields_len = if (small.has_fields_len) blk: { - const fields_len = zir.extra[extra_index]; - extra_index += 1; - break :blk fields_len; - } else 0; - - assert(captures_len == key.captures.owned.len); // synchronises with logic in `Zcu.mapOldZirToNew` - - const union_obj = ip.loadUnionType(old_ty); - - const namespace_index = union_obj.namespace; - - const wip_ty = switch (try ip.getUnionType(gpa, io, pt.tid, .{ - .flags = .{ - .layout = small.layout, - .status = .none, - .runtime_tag = if (small.has_tag_type or small.auto_enum_tag) - .tagged - else if (small.layout != .auto) - .none - else switch (true) { // TODO - true => .safety, - false => .none, - }, - .any_aligned_fields = small.any_aligned_fields, - .requires_comptime = .unknown, - .assumed_runtime_bits = false, - .assumed_pointer_aligned = false, - .alignment = .none, - }, - .fields_len = fields_len, - .enum_tag_ty = .none, // set later - .field_types = &.{}, // set later - .field_aligns = &.{}, // set later - .key = .{ .declared_owned_captures = .{ - .zir_index = key.zir_index, - .captures = key.captures.owned, - } }, - }, true)) { - .wip => |wip| wip, - .existing => unreachable, // we passed `replace_existing` - }; - errdefer wip_ty.cancel(ip, pt.tid); - - wip_ty.setName(ip, union_obj.name, union_obj.name_nav); - try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = key.zir_index }); - zcu.namespacePtr(namespace_index).owner_type = wip_ty.index; - // No need to re-scan the namespace -- `zirUnionDecl` will ultimately do that if the type is still alive. - try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); - - codegen_type: { - if (file.mod.?.strip) break :codegen_type; - // This job depends on any resolve_type_fully jobs queued up before it. - zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); - try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); - } - - if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); - return wip_ty.finish(ip, namespace_index); -} - -/// This *does* call `Sema.resolveDeclaredEnum`, but errors from it are not propagated. -/// Call sites are resposible for checking `[transitive_]failed_analysis` after `ensureTypeUpToDate` -/// returns in order to detect resolution failures. -fn recreateEnumType( - pt: Zcu.PerThread, - old_ty: InternPool.Index, - key: InternPool.Key.NamespaceType.Declared, -) (Allocator.Error || Io.Cancelable)!InternPool.Index { - const zcu = pt.zcu; - const comp = zcu.comp; - const gpa = comp.gpa; - const io = comp.io; - const ip = &zcu.intern_pool; - - const inst_info = key.zir_index.resolveFull(ip).?; - const file = zcu.fileByIndex(inst_info.file); - const zir = file.zir.?; - - assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); - const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended; - assert(extended.opcode == .enum_decl); - const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small); - const extra = zir.extraData(Zir.Inst.EnumDecl, extended.operand); - var extra_index = extra.end; - - const tag_type_ref = if (small.has_tag_type) blk: { - const tag_type_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); - extra_index += 1; - break :blk tag_type_ref; - } else .none; - - const captures_len = if (small.has_captures_len) blk: { - const captures_len = zir.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - - const body_len = if (small.has_body_len) blk: { - const body_len = zir.extra[extra_index]; - extra_index += 1; - break :blk body_len; - } else 0; - - const fields_len = if (small.has_fields_len) blk: { - const fields_len = zir.extra[extra_index]; - extra_index += 1; - break :blk fields_len; - } else 0; - - const decls_len = if (small.has_decls_len) blk: { - const decls_len = zir.extra[extra_index]; - extra_index += 1; - break :blk decls_len; - } else 0; - - assert(captures_len == key.captures.owned.len); // synchronises with logic in `Zcu.mapOldZirToNew` - - extra_index += captures_len * 2; - extra_index += decls_len; - - const body = zir.bodySlice(extra_index, body_len); - extra_index += body.len; - - const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; - const body_end = extra_index; - extra_index += bit_bags_count; - - const any_values = for (zir.extra[body_end..][0..bit_bags_count]) |bag| { - if (bag != 0) break true; - } else false; - - const enum_obj = ip.loadEnumType(old_ty); - - const namespace_index = enum_obj.namespace; - - const wip_ty = switch (try ip.getEnumType(gpa, io, pt.tid, .{ - .has_values = any_values, - .tag_mode = if (small.nonexhaustive) - .nonexhaustive - else if (tag_type_ref == .none) - .auto - else - .explicit, - .fields_len = fields_len, - .key = .{ .declared_owned_captures = .{ - .zir_index = key.zir_index, - .captures = key.captures.owned, - } }, - }, true)) { - .wip => |wip| wip, - .existing => unreachable, // we passed `replace_existing` - }; - var done = true; - errdefer if (!done) wip_ty.cancel(ip, pt.tid); - - wip_ty.setName(ip, enum_obj.name, enum_obj.name_nav); - - zcu.namespacePtr(namespace_index).owner_type = wip_ty.index; - // No need to re-scan the namespace -- `zirEnumDecl` will ultimately do that if the type is still alive. - - if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); - wip_ty.prepare(ip, namespace_index); - done = true; - - Sema.resolveDeclaredEnum( - pt, - wip_ty, - inst_info.inst, - key.zir_index, - namespace_index, - enum_obj.name, - small, - body, - tag_type_ref, - any_values, - fields_len, - zir, - body_end, - ) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.Canceled => |e| return e, - error.AnalysisFail => {}, // call sites are responsible for checking `[transitive_]failed_analysis` to detect this - }; - - return wip_ty.index; -} - /// Given a namespace, re-scan its declarations from the type definition if they have not /// yet been re-scanned on this update. /// If the type declaration instruction has been lost, returns `error.AnalysisFail`. @@ -4396,7 +4433,7 @@ pub fn ensureNamespaceUpToDate(pt: Zcu.PerThread, namespace_index: Zcu.Namespace }; const key = switch (full_key) { - .reified, .generated_tag => { + .reified, .generated_union_tag => { // Namespace always empty, so up-to-date. namespace.generation = zcu.generation; return; @@ -4408,123 +4445,37 @@ pub fn ensureNamespaceUpToDate(pt: Zcu.PerThread, namespace_index: Zcu.Namespace const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail; const file = zcu.fileByIndex(inst_info.file); - const zir = file.zir.?; - - assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); - const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended; + const zir = &file.zir.?; const decls = switch (container) { - .@"struct" => decls: { - assert(extended.opcode == .struct_decl); - const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); - const extra = zir.extraData(Zir.Inst.StructDecl, extended.operand); - var extra_index = extra.end; - const captures_len = if (small.has_captures_len) blk: { - const captures_len = zir.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - extra_index += @intFromBool(small.has_fields_len); - const decls_len = if (small.has_decls_len) blk: { - const decls_len = zir.extra[extra_index]; - extra_index += 1; - break :blk decls_len; - } else 0; - extra_index += captures_len * 2; - if (small.has_backing_int) { - const backing_int_body_len = zir.extra[extra_index]; - extra_index += 1; // backing_int_body_len - if (backing_int_body_len == 0) { - extra_index += 1; // backing_int_ref - } else { - extra_index += backing_int_body_len; // backing_int_body_inst - } - } - break :decls zir.bodySlice(extra_index, decls_len); - }, - .@"union" => decls: { - assert(extended.opcode == .union_decl); - const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); - const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand); - var extra_index = extra.end; - extra_index += @intFromBool(small.has_tag_type); - const captures_len = if (small.has_captures_len) blk: { - const captures_len = zir.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - extra_index += @intFromBool(small.has_body_len); - extra_index += @intFromBool(small.has_fields_len); - const decls_len = if (small.has_decls_len) blk: { - const decls_len = zir.extra[extra_index]; - extra_index += 1; - break :blk decls_len; - } else 0; - extra_index += captures_len * 2; - break :decls zir.bodySlice(extra_index, decls_len); - }, - .@"enum" => decls: { - assert(extended.opcode == .enum_decl); - const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small); - const extra = zir.extraData(Zir.Inst.EnumDecl, extended.operand); - var extra_index = extra.end; - extra_index += @intFromBool(small.has_tag_type); - const captures_len = if (small.has_captures_len) blk: { - const captures_len = zir.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - extra_index += @intFromBool(small.has_body_len); - extra_index += @intFromBool(small.has_fields_len); - const decls_len = if (small.has_decls_len) blk: { - const decls_len = zir.extra[extra_index]; - extra_index += 1; - break :blk decls_len; - } else 0; - extra_index += captures_len * 2; - break :decls zir.bodySlice(extra_index, decls_len); - }, - .@"opaque" => decls: { - assert(extended.opcode == .opaque_decl); - const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small); - const extra = zir.extraData(Zir.Inst.OpaqueDecl, extended.operand); - var extra_index = extra.end; - const captures_len = if (small.has_captures_len) blk: { - const captures_len = zir.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - const decls_len = if (small.has_decls_len) blk: { - const decls_len = zir.extra[extra_index]; - extra_index += 1; - break :blk decls_len; - } else 0; - extra_index += captures_len * 2; - break :decls zir.bodySlice(extra_index, decls_len); - }, + .@"struct" => zir.getStructDecl(inst_info.inst).decls, + .@"union" => zir.getUnionDecl(inst_info.inst).decls, + .@"enum" => zir.getEnumDecl(inst_info.inst).decls, + .@"opaque" => zir.getOpaqueDecl(inst_info.inst).decls, }; try pt.scanNamespace(namespace_index, decls); namespace.generation = zcu.generation; } -pub fn refValue(pt: Zcu.PerThread, val: InternPool.Index) Zcu.SemaError!InternPool.Index { - const ptr_ty = (try pt.ptrTypeSema(.{ - .child = pt.zcu.intern_pool.typeOf(val), +pub fn uavValue(pt: Zcu.PerThread, val: Value) Zcu.SemaError!Value { + const zcu = pt.zcu; + const ptr_ty = try pt.ptrType(.{ + .child = val.typeOf(zcu).toIntern(), .flags = .{ .alignment = .none, .is_const = true, .address_space = .generic, }, - })).toIntern(); - return pt.intern(.{ .ptr = .{ - .ty = ptr_ty, + }); + return .fromInterned(try pt.intern(.{ .ptr = .{ + .ty = ptr_ty.toIntern(), .base_addr = .{ .uav = .{ - .val = val, - .orig_ty = ptr_ty, + .val = val.toIntern(), + .orig_ty = ptr_ty.toIntern(), } }, .byte_offset = 0, - } }); + } })); } pub fn addDependency(pt: Zcu.PerThread, unit: AnalUnit, dependee: InternPool.Dependee) Allocator.Error!void { diff --git a/src/codegen.zig b/src/codegen.zig @@ -343,11 +343,9 @@ pub fn generateSymbol( .undef => unreachable, // handled above .simple_value => |simple_value| switch (simple_value) { - .undefined => unreachable, // non-runtime value .void => unreachable, // non-runtime value .null => unreachable, // non-runtime value .@"unreachable" => unreachable, // non-runtime value - .empty_tuple => return, .false, .true => try w.writeByte(switch (simple_value) { .false => 0, .true => 1, @@ -358,7 +356,6 @@ pub fn generateSymbol( .@"extern", .func, .enum_literal, - .empty_enum_value, => unreachable, // non-runtime values .int => { const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; @@ -377,7 +374,7 @@ pub fn generateSymbol( .payload => 0, }; - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { try w.writeInt(u16, err_val, endian); return; } @@ -571,46 +568,11 @@ pub fn generateSymbol( .struct_type => { const struct_type = ip.loadStructType(ty.toIntern()); switch (struct_type.layout) { - .@"packed" => { - const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; - const start = w.end; - const buffer = try w.writableSlice(abi_size); - @memset(buffer, 0); - var bits: u16 = 0; - - for (struct_type.field_types.get(ip), 0..) |field_ty, index| { - const field_val = switch (aggregate.storage) { - .bytes => |bytes| try pt.intern(.{ .int = .{ - .ty = field_ty, - .storage = .{ .u64 = bytes.at(index, ip) }, - } }), - .elems => |elems| elems[index], - .repeated_elem => |elem| elem, - }; - - // pointer may point to a decl which must be marked used - // but can also result in a relocation. Therefore we handle those separately. - if (Type.fromInterned(field_ty).zigTypeTag(zcu) == .pointer) { - const field_offset = std.math.divExact(u16, bits, 8) catch |err| switch (err) { - error.DivisionByZero => unreachable, - error.UnexpectedRemainder => return error.RelocationNotByteAligned, - }; - w.end = start + field_offset; - defer { - assert(w.end == start + field_offset + @divExact(target.ptrBitWidth(), 8)); - w.end = start + abi_size; - } - try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), w, reloc_parent); - } else { - Value.fromInterned(field_val).writeToPackedMemory(.fromInterned(field_ty), pt, buffer, bits) catch unreachable; - } - bits += @intCast(Type.fromInterned(field_ty).bitSize(zcu)); - } - }, + .@"packed" => unreachable, .auto, .@"extern" => { const struct_begin = w.end; const field_types = struct_type.field_types.get(ip); - const offsets = struct_type.offsets.get(ip); + const offsets = struct_type.field_offsets.get(ip); var it = struct_type.iterateRuntimeOrder(ip); while (it.next()) |field_index| { @@ -635,13 +597,11 @@ pub fn generateSymbol( try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), w, reloc_parent); } - const size = struct_type.sizeUnordered(ip); - const alignment = struct_type.flagsUnordered(ip).alignment.toByteUnits().?; + assert(struct_type.alignment.check(struct_type.size)); - const padding = math.cast( - usize, - std.mem.alignForward(u64, size, @max(alignment, 1)) - (w.end - struct_begin), - ) orelse return error.Overflow; + const padding = math.cast(usize, struct_type.size - (w.end - struct_begin)) orelse { + return error.Overflow; + }; if (padding > 0) try w.splatByteAll(0, padding); }, } @@ -686,6 +646,7 @@ pub fn generateSymbol( } } }, + .bitpack => |bitpack| try generateSymbol(bin_file, pt, src_loc, .fromInterned(bitpack.backing_int_val), w, reloc_parent), .memoized_call => unreachable, } } @@ -739,7 +700,14 @@ fn lowerPtr( }; return lowerPtr(bin_file, pt, src_loc, field.base, w, reloc_parent, offset + field_off); }, - .arr_elem, .comptime_field, .comptime_alloc => unreachable, + .arr_elem => |arr_elem| { + const base_ptr_ty = Value.fromInterned(arr_elem.base).typeOf(zcu); + assert(base_ptr_ty.ptrSize(zcu) == .many); + const elem_size = base_ptr_ty.childType(zcu).abiSize(zcu); + return lowerPtr(bin_file, pt, src_loc, arr_elem.base, w, reloc_parent, offset + elem_size * arr_elem.index); + }, + .comptime_alloc => unreachable, + .comptime_field => unreachable, }; } @@ -820,9 +788,8 @@ fn lowerNavRef( const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); const is_obj = lf.comp.config.output_mode == .Obj; const nav_ty = Type.fromInterned(ip.getNav(nav_index).typeOf(ip)); - const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn"; - if (!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) { + if (!nav_ty.isRuntimeFnOrHasRuntimeBits(zcu) and ip.getNav(nav_index).getExtern(ip) == null) { try w.splatByteAll(0xaa, ptr_width_bytes); return; } @@ -834,7 +801,7 @@ fn lowerNavRef( dev.check(link.File.Tag.wasm.devFeature()); const wasm = lf.cast(.wasm).?; assert(reloc_parent == .none); - if (is_fn_body) { + if (nav_ty.zigTypeTag(zcu) == .@"fn") { const gop = try wasm.zcu_indirect_function_set.getOrPut(gpa, nav_index); if (!gop.found_existing) gop.value_ptr.* = {}; if (is_obj) { @@ -1060,51 +1027,41 @@ pub fn lowerValue(pt: Zcu.PerThread, val: Value, target: *const std.Target) Allo switch (ty.zigTypeTag(zcu)) { .void => return .none, + .bool => return .{ .immediate = @intFromBool(val.toBool()) }, .pointer => switch (ty.ptrSize(zcu)) { .slice => {}, - else => switch (val.toIntern()) { - .null_value => { - return .{ .immediate = 0 }; - }, - else => switch (ip.indexToKey(val.toIntern())) { - .int => { - return .{ .immediate = val.toUnsignedInt(zcu) }; + .one, .many, .c => { + const ptr = ip.indexToKey(val.toIntern()).ptr; + if (ptr.base_addr == .int) return .{ .immediate = ptr.byte_offset }; + if (ptr.byte_offset == 0) switch (ptr.base_addr) { + .int => unreachable, // handled above + + .nav => |nav_index| { + const nav = ip.getNav(nav_index); + const nav_ty: Type = .fromInterned(nav.typeOf(ip)); + if (nav_ty.isRuntimeFnOrHasRuntimeBits(zcu) or nav.getExtern(ip) != null) { + return .{ .lea_nav = nav_index }; + } else { + // Create the 0xaa bit pattern... + const undef_ptr_bits: u64 = @intCast((@as(u66, 1) << @intCast(target.ptrBitWidth() + 1)) / 3); + // ...but align the pointer + const alignment = zcu.navAlignment(nav_index); + return .{ .immediate = alignment.forward(undef_ptr_bits) }; + } }, - .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) { - .nav => |nav| { - if (!ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) { - const imm: u64 = switch (@divExact(target.ptrBitWidth(), 8)) { - 1 => 0xaa, - 2 => 0xaaaa, - 4 => 0xaaaaaaaa, - 8 => 0xaaaaaaaaaaaaaaaa, - else => unreachable, - }; - return .{ .immediate = imm }; - } - if (ty.castPtrToFn(zcu)) |fn_ty| { - if (zcu.typeToFunc(fn_ty).?.is_generic) { - return .{ .immediate = fn_ty.abiAlignment(zcu).toByteUnits().? }; - } - } else if (ty.zigTypeTag(zcu) == .pointer) { - const elem_ty = ty.elemType2(zcu); - if (!elem_ty.hasRuntimeBits(zcu)) { - return .{ .immediate = elem_ty.abiAlignment(zcu).toByteUnits().? }; - } - } - - return .{ .lea_nav = nav }; - }, - .uav => |uav| if (Value.fromInterned(uav.val).typeOf(zcu).hasRuntimeBits(zcu)) - return .{ .lea_uav = uav } - else - return .{ .immediate = Type.fromInterned(uav.orig_ty).ptrAlignment(zcu) - .forward(@intCast((@as(u66, 1) << @intCast(target.ptrBitWidth() | 1)) / 3)) }, - else => {}, + .uav => |uav| if (Value.fromInterned(uav.val).typeOf(zcu).isRuntimeFnOrHasRuntimeBits(zcu)) { + return .{ .lea_uav = uav }; + } else { + // Create the 0xaa bit pattern... + const undef_ptr_bits: u64 = @intCast((@as(u66, 1) << @intCast(target.ptrBitWidth() + 1)) / 3); + // ...but align the pointer + const alignment = Type.fromInterned(uav.orig_ty).ptrAlignment(zcu); + return .{ .immediate = alignment.forward(undef_ptr_bits) }; }, + else => {}, - }, + }; }, }, .int => { @@ -1117,9 +1074,6 @@ pub fn lowerValue(pt: Zcu.PerThread, val: Value, target: *const std.Target) Allo return .{ .immediate = unsigned }; } }, - .bool => { - return .{ .immediate = @intFromBool(val.toBool()) }; - }, .optional => { if (ty.isPtrLikeOptional(zcu)) { return lowerValue( @@ -1139,6 +1093,10 @@ pub fn lowerValue(pt: Zcu.PerThread, val: Value, target: *const std.Target) Allo target, ); }, + .@"struct", .@"union" => if (ty.containerLayout(zcu) == .@"packed") { + const bitpack = ip.indexToKey(val.toIntern()).bitpack; + return lowerValue(pt, .fromInterned(bitpack.backing_int_val), target); + }, .error_set => { const err_name = ip.indexToKey(val.toIntern()).err.name; const error_index = ip.getErrorValueIfExists(err_name).?; @@ -1147,7 +1105,7 @@ pub fn lowerValue(pt: Zcu.PerThread, val: Value, target: *const std.Target) Allo .error_union => { const err_type = ty.errorUnionSet(zcu); const payload_type = ty.errorUnionPayload(zcu); - if (!payload_type.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_type.hasRuntimeBits(zcu)) { // We use the error type directly as the type. const err_int_ty = try pt.errorIntType(); switch (ip.indexToKey(val.toIntern()).error_union.val) { @@ -1187,10 +1145,10 @@ pub fn lowerValue(pt: Zcu.PerThread, val: Value, target: *const std.Target) Allo } pub fn errUnionPayloadOffset(payload_ty: Type, zcu: *Zcu) u64 { - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) return 0; + if (!payload_ty.hasRuntimeBits(zcu)) return 0; const payload_align = payload_ty.abiAlignment(zcu); const error_align = Type.anyerror.abiAlignment(zcu); - if (payload_align.compare(.gte, error_align) or !payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (payload_align.compare(.gte, error_align) or !payload_ty.hasRuntimeBits(zcu)) { return 0; } else { return payload_align.forward(Type.anyerror.abiSize(zcu)); @@ -1198,10 +1156,10 @@ pub fn errUnionPayloadOffset(payload_ty: Type, zcu: *Zcu) u64 { } pub fn errUnionErrorOffset(payload_ty: Type, zcu: *Zcu) u64 { - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) return 0; + if (!payload_ty.hasRuntimeBits(zcu)) return 0; const payload_align = payload_ty.abiAlignment(zcu); const error_align = Type.anyerror.abiAlignment(zcu); - if (payload_align.compare(.gte, error_align) and payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (payload_align.compare(.gte, error_align) and payload_ty.hasRuntimeBits(zcu)) { return error_align.forward(payload_ty.abiSize(zcu)); } else { return 0; diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig @@ -2464,7 +2464,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const ty_pl = air.data(air.inst_index).ty_pl; const bin_op = isel.air.extraData(Air.Bin, ty_pl.payload).data; - const elem_size = ty_pl.ty.toType().elemType2(zcu).abiSize(zcu); + const elem_size = ty_pl.ty.toType().childType(zcu).abiSize(zcu); const base_vi = try isel.use(bin_op.lhs); var base_part_it = base_vi.field(ty_pl.ty.toType(), 0, 8); @@ -2791,17 +2791,17 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, } else return isel.fail("invalid constraint: '{s}'", .{constraint}); } - const clobbers = ip.indexToKey(unwrapped_asm.clobbers).aggregate; - const clobbers_ty: ZigType = .fromInterned(clobbers.ty); + const clobbers_val: Constant = .fromInterned(unwrapped_asm.clobbers); + const clobbers_ty = clobbers_val.typeOf(zcu); + var clobbers_bigint_buf: Constant.BigIntSpace = undefined; + const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu); for (0..clobbers_ty.structFieldCount(zcu)) |field_index| { - switch (switch (clobbers.storage) { - .bytes => unreachable, - .elems => |elems| elems[field_index], - .repeated_elem => |repeated_elem| repeated_elem, - }) { - else => unreachable, - .bool_false => continue, - .bool_true => {}, + assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type); + const limb_bits = @bitSizeOf(std.math.big.Limb); + if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false + switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) { + 0 => continue, // field is false + 1 => {}, // field is true } const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?; if (std.mem.eql(u8, clobber_name, "memory")) continue; @@ -2816,14 +2816,11 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, } } for (0..clobbers_ty.structFieldCount(zcu)) |field_index| { - switch (switch (clobbers.storage) { - .bytes => unreachable, - .elems => |elems| elems[field_index], - .repeated_elem => |repeated_elem| repeated_elem, - }) { - else => unreachable, - .bool_false => continue, - .bool_true => {}, + const limb_bits = @bitSizeOf(std.math.big.Limb); + if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false + switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) { + 0 => continue, // field is false + 1 => {}, // field is true } const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?; if (std.mem.eql(u8, clobber_name, "memory")) continue; @@ -2872,14 +2869,11 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, } for (0..clobbers_ty.structFieldCount(zcu)) |field_index| { - switch (switch (clobbers.storage) { - .bytes => unreachable, - .elems => |elems| elems[field_index], - .repeated_elem => |repeated_elem| repeated_elem, - }) { - else => unreachable, - .bool_false => continue, - .bool_true => {}, + const limb_bits = @bitSizeOf(std.math.big.Limb); + if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false + switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) { + 0 => continue, // field is false + 1 => {}, // field is true } const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?; if (std.mem.eql(u8, clobber_name, "memory")) continue; @@ -3289,8 +3283,8 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, } else if (dst_ty.isSliceAtRuntime(zcu) and src_ty.isSliceAtRuntime(zcu)) { try dst_vi.value.move(isel, ty_op.operand); } else if (dst_tag == .error_union and src_tag == .error_union) { - assert(dst_ty.errorUnionSet(zcu).hasRuntimeBitsIgnoreComptime(zcu) == - src_ty.errorUnionSet(zcu).hasRuntimeBitsIgnoreComptime(zcu)); + assert(dst_ty.errorUnionSet(zcu).hasRuntimeBits(zcu) == + src_ty.errorUnionSet(zcu).hasRuntimeBits(zcu)); if (dst_ty.errorUnionPayload(zcu).toIntern() == src_ty.errorUnionPayload(zcu).toIntern()) { try dst_vi.value.move(isel, ty_op.operand); } else return isel.fail("bad {t} {f} {f}", .{ air_tag, isel.fmtType(dst_ty), isel.fmtType(src_ty) }); @@ -4568,7 +4562,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, } if (case.ranges.len == 0 and case.items.len == 1 and Constant.fromInterned( case.items[0].toInterned().?, - ).orderAgainstZero(zcu).compare(.eq)) { + ).compareHetero(.eq, .zero_comptime_int, zcu)) { try isel.emit(.cbnz( cond_reg, @intCast((isel.instructions.items.len + 1 - next_label) << 2), @@ -6145,7 +6139,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, } else { const elem_ptr_ra = try isel.allocIntReg(); defer isel.freeReg(elem_ptr_ra); - if (!try elem_vi.value.load(isel, slice_ty.elemType2(zcu), elem_ptr_ra, .{ + if (!try elem_vi.value.load(isel, slice_ty.childType(zcu), elem_ptr_ra, .{ .@"volatile" = ptr_info.flags.is_volatile, })) break :unused; const slice_vi = try isel.use(bin_op.lhs); @@ -6253,7 +6247,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, } else { const elem_ptr_ra = try isel.allocIntReg(); defer isel.freeReg(elem_ptr_ra); - if (!try elem_vi.value.load(isel, ptr_ty.elemType2(zcu), elem_ptr_ra, .{ + if (!try elem_vi.value.load(isel, ptr_ty.childType(zcu), elem_ptr_ra, .{ .@"volatile" = ptr_info.flags.is_volatile, })) break :unused; const base_vi = try isel.use(bin_op.lhs); @@ -6594,7 +6588,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, if (try isel.hasRepeatedByteRepr(.fromInterned(fill_val))) |fill_byte| break :fill_byte .{ .constant = fill_byte }; } - switch (dst_ty.elemType2(zcu).abiSize(zcu)) { + switch (dst_ty.indexableElem(zcu).abiSize(zcu)) { 0 => unreachable, 1 => break :fill_byte .{ .value = bin_op.rhs }, 2, 4, 8 => |size| { @@ -6899,11 +6893,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, var field_it = loaded_struct.iterateRuntimeOrder(ip); while (field_it.next()) |field_index| { const field_ty: ZigType = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - field_offset = field_ty.structFieldAlignment( - loaded_struct.fieldAlign(ip, field_index), - loaded_struct.layout, - zcu, - ).forward(field_offset); + field_offset = loaded_struct.field_offsets.get(ip)[field_index]; const field_size = field_ty.abiSize(zcu); if (field_size == 0) continue; var agg_part_it = agg_vi.value.field(agg_ty, field_offset, field_size); @@ -6911,7 +6901,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, try agg_part_vi.?.move(isel, elems[field_index]); field_offset += field_size; } - assert(loaded_struct.flagsUnordered(ip).alignment.forward(field_offset) == agg_vi.value.size(isel)); + assert(loaded_struct.alignment.forward(field_offset) == agg_vi.value.size(isel)); }, .tuple_type => |tuple_type| { const elems: []const Air.Inst.Ref = @@ -6953,23 +6943,23 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const union_layout = ZigType.getUnionLayout(loaded_union, zcu); if (union_layout.tag_size > 0) unused_tag: { - const loaded_tag = loaded_union.loadTagType(ip); + const loaded_tag = ip.loadEnumType(loaded_union.enum_tag_type); var tag_it = union_vi.value.field(union_ty, union_layout.tagOffset(), union_layout.tag_size); const tag_vi = try tag_it.only(isel); const tag_ra = try tag_vi.?.defReg(isel) orelse break :unused_tag; switch (union_layout.tag_size) { 0 => unreachable, - 1...4 => try isel.movImmediate(tag_ra.w(), @as(u32, switch (loaded_tag.values.len) { + 1...4 => try isel.movImmediate(tag_ra.w(), @as(u32, switch (loaded_tag.field_values.len) { 0 => extra.field_index, - else => switch (ip.indexToKey(loaded_tag.values.get(ip)[extra.field_index]).int.storage) { + else => switch (ip.indexToKey(loaded_tag.field_values.get(ip)[extra.field_index]).int.storage) { .u64 => |imm| @intCast(imm), .i64 => |imm| @bitCast(@as(i32, @intCast(imm))), else => unreachable, }, })), - 5...8 => try isel.movImmediate(tag_ra.x(), switch (loaded_tag.values.len) { + 5...8 => try isel.movImmediate(tag_ra.x(), switch (loaded_tag.field_values.len) { 0 => extra.field_index, - else => switch (ip.indexToKey(loaded_tag.values.get(ip)[extra.field_index]).int.storage) { + else => switch (ip.indexToKey(loaded_tag.field_values.get(ip)[extra.field_index]).int.storage) { .u64 => |imm| imm, .i64 => |imm| @bitCast(imm), else => unreachable, @@ -7217,7 +7207,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const ptr_ra = try ptr_vi.value.defReg(isel) orelse break :unused; const ty_nav = air.data(air.inst_index).ty_nav; - if (ZigType.fromInterned(ip.getNav(ty_nav.nav).typeOf(ip)).isFnOrHasRuntimeBits(zcu)) switch (true) { + if (ZigType.fromInterned(ip.getNav(ty_nav.nav).typeOf(ip)).isRuntimeFnOrHasRuntimeBits(zcu)) switch (true) { false => { try isel.nav_relocs.append(gpa, .{ .nav = ty_nav.nav, @@ -7240,7 +7230,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, }); try isel.emit(.adrp(ptr_ra.x(), 0)); }, - } else try isel.movImmediate(ptr_ra.x(), isel.pt.navAlignment(ty_nav.nav).forward(0xaaaaaaaaaaaaaaaa)); + } else try isel.movImmediate(ptr_ra.x(), zcu.navAlignment(ty_nav.nav).forward(0xaaaaaaaaaaaaaaaa)); } if (air.next()) |next_air_tag| continue :air_tag next_air_tag; }, @@ -10397,7 +10387,7 @@ pub const Value = struct { switch (loaded_struct.layout) { .auto, .@"extern" => {}, .@"packed" => continue :type_key .{ - .int_type = ip.indexToKey(loaded_struct.backingIntTypeUnordered(ip)).int_type, + .int_type = ip.indexToKey(loaded_struct.packed_backing_int_type).int_type, }, } const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0; @@ -10412,7 +10402,7 @@ pub const Value = struct { var field_it = loaded_struct.iterateRuntimeOrder(ip); while (field_it.next()) |field_index| { const field_ty: ZigType = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - const field_begin = switch (loaded_struct.fieldAlign(ip, field_index)) { + const field_begin = switch (loaded_struct.field_aligns.getOrNone(ip, field_index)) { .none => field_ty.abiAlignment(zcu), else => |field_align| field_align, }.forward(field_end); @@ -10510,7 +10500,7 @@ pub const Value = struct { }, .union_type => { const loaded_union = ip.loadUnionType(ty.toIntern()); - switch (loaded_union.flagsUnordered(ip).layout) { + switch (loaded_union.layout) { .auto, .@"extern" => {}, .@"packed" => continue :type_key .{ .int_type = .{ .signedness = .unsigned, @@ -10545,12 +10535,13 @@ pub const Value = struct { const field_signedness = field_signedness: switch (field) { .tag => { if (offset >= field_begin and offset + size <= field_begin + field_size) { - ty = .fromInterned(loaded_union.enum_tag_ty); + ty = .fromInterned(loaded_union.enum_tag_type); ty_size = field_size; offset -= field_begin; - continue :type_key ip.indexToKey(loaded_union.enum_tag_ty); + continue :type_key ip.indexToKey(loaded_union.enum_tag_type); } - break :field_signedness ip.indexToKey(loaded_union.loadTagType(ip).tag_ty).int_type.signedness; + const loaded_enum = ip.loadEnumType(loaded_union.enum_tag_type); + break :field_signedness ip.indexToKey(loaded_enum.int_tag_type).int_type.signedness; }, .payload => null, }; @@ -10580,7 +10571,7 @@ pub const Value = struct { } }, .opaque_type, .func_type => continue :type_key .{ .simple_type = .anyopaque }, - .enum_type => continue :type_key ip.indexToKey(ip.loadEnumType(ty.toIntern()).tag_ty), + .enum_type => continue :type_key ip.indexToKey(ip.loadEnumType(ty.toIntern()).int_tag_type), .error_set_type, .inferred_error_set_type, => continue :type_key .{ .simple_type = .anyerror }, @@ -10594,7 +10585,6 @@ pub const Value = struct { .error_union, .enum_literal, .enum_tag, - .empty_enum_value, .float, .ptr, .slice, @@ -10717,7 +10707,6 @@ pub const Value = struct { .inferred_error_set_type, .enum_literal, - .empty_enum_value, .memoized_call, => unreachable, // not a runtime value .undef => break :free try isel.emit(if (mat.ra.isVector()) .movi(switch (size) { @@ -10738,7 +10727,7 @@ pub const Value = struct { } }), }), .simple_value => |simple_value| switch (simple_value) { - .undefined, .void, .null, .empty_tuple, .@"unreachable" => unreachable, + .void, .null, .@"unreachable" => unreachable, .true => continue :constant_key .{ .int = .{ .ty = .bool_type, .storage = .{ .u64 = 1 }, @@ -10748,7 +10737,7 @@ pub const Value = struct { .storage = .{ .u64 = 0 }, } }, }, - .int => |int| break :free storage: switch (int.storage) { + .int => |int| break :free switch (int.storage) { .u64 => |imm| try isel.movImmediate(switch (size) { else => unreachable, 1...4 => mat.ra.w(), @@ -10780,12 +10769,6 @@ pub const Value = struct { } try isel.movImmediate(mat.ra.x(), imm); }, - .lazy_align => |ty| continue :storage .{ - .u64 = ZigType.fromInterned(ty).abiAlignment(zcu).toByteUnits().?, - }, - .lazy_size => |ty| continue :storage .{ - .u64 = ZigType.fromInterned(ty).abiSize(zcu), - }, }, .err => |err| continue :constant_key .{ .int = .{ .ty = err.ty, @@ -10931,7 +10914,7 @@ pub const Value = struct { .ptr => |ptr| { assert(offset == 0 and size == 8); break :free switch (ptr.base_addr) { - .nav => |nav| if (ZigType.fromInterned(ip.getNav(nav).typeOf(ip)).isFnOrHasRuntimeBits(zcu)) switch (true) { + .nav => |nav| if (ZigType.fromInterned(ip.getNav(nav).typeOf(ip)).isRuntimeFnOrHasRuntimeBits(zcu)) switch (true) { false => { try isel.nav_relocs.append(zcu.gpa, .{ .nav = nav, @@ -10965,9 +10948,9 @@ pub const Value = struct { }, } else continue :constant_key .{ .int = .{ .ty = .usize_type, - .storage = .{ .u64 = isel.pt.navAlignment(nav).forward(0xaaaaaaaaaaaaaaaa) }, + .storage = .{ .u64 = zcu.navAlignment(nav).forward(0xaaaaaaaaaaaaaaaa) }, } }, - .uav => |uav| if (ZigType.fromInterned(ip.typeOf(uav.val)).isFnOrHasRuntimeBits(zcu)) switch (true) { + .uav => |uav| if (ZigType.fromInterned(ip.typeOf(uav.val)).isRuntimeFnOrHasRuntimeBits(zcu)) switch (true) { false => { try isel.uav_relocs.append(zcu.gpa, .{ .uav = uav, @@ -11092,13 +11075,9 @@ pub const Value = struct { var field_offset: u64 = 0; var field_it = loaded_struct.iterateRuntimeOrder(ip); while (field_it.next()) |field_index| { - if (loaded_struct.fieldIsComptime(ip, field_index)) continue; + if (loaded_struct.field_is_comptime_bits.get(ip, field_index)) continue; const field_ty: ZigType = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - field_offset = field_ty.structFieldAlignment( - loaded_struct.fieldAlign(ip, field_index), - loaded_struct.layout, - zcu, - ).forward(field_offset); + field_offset = loaded_struct.field_offsets.get(ip)[field_index]; const field_size = field_ty.abiSize(zcu); if (offset >= field_offset and offset + size <= field_offset + field_size) { offset -= field_offset; @@ -11140,7 +11119,7 @@ pub const Value = struct { .un => |un| { const loaded_union = ip.loadUnionType(un.ty); const union_layout = ZigType.getUnionLayout(loaded_union, zcu); - if (loaded_union.hasTag(ip)) { + if (loaded_union.has_runtime_tag) { const tag_offset = union_layout.tagOffset(); if (offset >= tag_offset and offset + size <= tag_offset + union_layout.tag_size) { offset -= tag_offset; @@ -11414,7 +11393,6 @@ fn writeKeyToMemory(isel: *Select, constant_key: InternPool.Key, buffer: []u8) e .inferred_error_set_type, .enum_literal, - .empty_enum_value, .memoized_call, => unreachable, // not a runtime value .err => |err| { @@ -11486,13 +11464,9 @@ fn writeKeyToMemory(isel: *Select, constant_key: InternPool.Key, buffer: []u8) e var field_offset: u64 = 0; var field_it = loaded_struct.iterateRuntimeOrder(ip); while (field_it.next()) |field_index| { - if (loaded_struct.fieldIsComptime(ip, field_index)) continue; + if (loaded_struct.field_is_comptime_bits.get(ip, field_index)) continue; const field_ty: ZigType = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - field_offset = field_ty.structFieldAlignment( - loaded_struct.fieldAlign(ip, field_index), - loaded_struct.layout, - zcu, - ).forward(field_offset); + field_offset = loaded_struct.field_offsets.get(ip)[field_index]; const field_size = field_ty.abiSize(zcu); if (!try isel.writeToMemory(.fromInterned(switch (aggregate.storage) { .bytes => unreachable, @@ -12091,7 +12065,7 @@ pub const CallAbiIterator = struct { const zcu = isel.pt.zcu; const ip = &zcu.intern_pool; - if (ty.isNoReturn(zcu) or !ty.hasRuntimeBitsIgnoreComptime(zcu)) return null; + if (!ty.hasRuntimeBits(zcu)) return null; try isel.values.ensureUnusedCapacity(zcu.gpa, Value.max_parts); const wip_vi = isel.initValue(ty); type_key: switch (ip.indexToKey(ty.toIntern())) { @@ -12195,7 +12169,7 @@ pub const CallAbiIterator = struct { switch (loaded_struct.layout) { .auto, .@"extern" => {}, .@"packed" => continue :type_key .{ - .int_type = ip.indexToKey(loaded_struct.backingIntTypeUnordered(ip)).int_type, + .int_type = ip.indexToKey(loaded_struct.packed_backing_int_type).int_type, }, } const size = wip_vi.size(isel); @@ -12219,7 +12193,7 @@ pub const CallAbiIterator = struct { const field_end = next_field_end; const next_field_begin = if (field_it.next()) |field_index| next_field_begin: { const field_ty: ZigType = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - const next_field_begin = switch (loaded_struct.fieldAlign(ip, field_index)) { + const next_field_begin = switch (loaded_struct.field_aligns.getOrNone(ip, field_index)) { .none => field_ty.abiAlignment(zcu), else => |field_align| field_align, }.forward(field_end); @@ -12285,7 +12259,7 @@ pub const CallAbiIterator = struct { }, .union_type => { const loaded_union = ip.loadUnionType(ty.toIntern()); - switch (loaded_union.flagsUnordered(ip).layout) { + switch (loaded_union.layout) { .auto, .@"extern" => {}, .@"packed" => continue :type_key .{ .int_type = .{ .signedness = .unsigned, @@ -12318,7 +12292,9 @@ pub const CallAbiIterator = struct { } }, .opaque_type, .func_type => continue :type_key .{ .simple_type = .anyopaque }, - .enum_type => continue :type_key ip.indexToKey(ip.loadEnumType(ty.toIntern()).tag_ty), + .enum_type => continue :type_key .{ + .int_type = ip.indexToKey(ip.loadEnumType(ty.toIntern()).int_tag_type).int_type, + }, .error_set_type, .inferred_error_set_type, => continue :type_key .{ .simple_type = .anyerror }, @@ -12332,7 +12308,6 @@ pub const CallAbiIterator = struct { .error_union, .enum_literal, .enum_tag, - .empty_enum_value, .float, .ptr, .slice, @@ -12424,8 +12399,8 @@ pub const CallAbiIterator = struct { const ip = &zcu.intern_pool; var common_fdt: ?FundamentalDataType = null; for (0.., loaded_struct.field_types.get(ip)) |field_index, field_ty| { - if (loaded_struct.fieldIsComptime(ip, field_index)) continue; - if (loaded_struct.fieldAlign(ip, field_index) != .none) return null; + if (loaded_struct.field_is_comptime_bits.get(ip, field_index)) continue; + if (loaded_struct.field_aligns.getOrNone(ip, field_index) != .none) return null; if (!ZigType.fromInterned(field_ty).hasRuntimeBits(zcu)) continue; const fdt = homogeneousAggregateBaseType(zcu, field_ty); if (common_fdt == null) common_fdt = fdt else if (fdt != common_fdt) return null; diff --git a/src/codegen/aarch64/abi.zig b/src/codegen/aarch64/abi.zig @@ -13,7 +13,7 @@ pub const Class = union(enum) { /// For `float_array` the second element will be the amount of floats. pub fn classifyType(ty: Type, zcu: *Zcu) Class { - assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + assert(ty.hasRuntimeBits(zcu)); var maybe_float_bits: ?u16 = null; switch (ty.zigTypeTag(zcu)) { diff --git a/src/codegen/arm/abi.zig b/src/codegen/arm/abi.zig @@ -23,7 +23,7 @@ pub const Class = union(enum) { pub const Context = enum { ret, arg }; pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { - assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + assert(ty.hasRuntimeBits(zcu)); var maybe_float_bits: ?u16 = null; const max_byval_size = 512; @@ -39,22 +39,22 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { const float_count = countFloats(ty, zcu, &maybe_float_bits); if (float_count <= byval_float_count) return .byval; + if (ty.abiAlignment(zcu).compare(.gt, .@"32")) { + return Class.arrSize(bit_size, 64); + } + const fields = ty.structFieldCount(zcu); var i: u32 = 0; while (i < fields) : (i += 1) { const field_ty = ty.fieldType(i, zcu); - const field_alignment = ty.fieldAlignment(i, zcu); - const field_size = field_ty.bitSize(zcu); - if (field_size > 32 or field_alignment.compare(.gt, .@"32")) { - return Class.arrSize(bit_size, 64); - } + if (field_ty.bitSize(zcu) > 32) return Class.arrSize(bit_size, 64); } return Class.arrSize(bit_size, 32); }, .@"union" => { const bit_size = ty.bitSize(zcu); const union_obj = zcu.typeToUnion(ty).?; - if (union_obj.flagsUnordered(ip).layout == .@"packed") { + if (union_obj.layout == .@"packed") { if (bit_size > 64) return .memory; return .byval; } @@ -62,10 +62,12 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { const float_count = countFloats(ty, zcu, &maybe_float_bits); if (float_count <= byval_float_count) return .byval; - for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| { - if (Type.fromInterned(field_ty).bitSize(zcu) > 32 or - ty.fieldAlignment(field_index, zcu).compare(.gt, .@"32")) - { + if (union_obj.alignment.compareStrict(.gt, .@"32")) { + return Class.arrSize(bit_size, 64); + } + + for (union_obj.field_types.get(ip)) |field_ty| { + if (Type.fromInterned(field_ty).bitSize(zcu) > 32) { return Class.arrSize(bit_size, 64); } } diff --git a/src/codegen/c.zig b/src/codegen/c.zig @@ -50,32 +50,39 @@ pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { /// * The types used, so declarations can be emitted in `flush` /// * The lazy functions used, so definitions can be emitted in `flush` pub const Mir = struct { + // These remaining fields are essentially just an owned version of `link.C.AvBlock`. + fwd_decl: []u8, + code_header: []u8, + code: []u8, /// This map contains all the UAVs we saw generating this function. /// `link.C` will merge them into its `uavs`/`aligned_uavs` fields. /// Key is the value of the UAV; value is the UAV's alignment, or /// `.none` for natural alignment. The specified alignment is never /// less than the natural alignment. - uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), - // These remaining fields are essentially just an owned version of `link.C.AvBlock`. - code_header: []u8, - code: []u8, - fwd_decl: []u8, - ctype_pool: CType.Pool, - lazy_fns: LazyFnMap, + need_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), + ctype_deps: CType.Dependencies, + /// Key is an enum type for which we need a generated `@tagName` function. + need_tag_name_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), + /// Key is a function Nav for which we need a generated `zig_never_tail` wrapper. + need_never_tail_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void), + /// Key is a function Nav for which we need a generated `zig_never_inline` wrapper. + need_never_inline_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void), pub fn deinit(mir: *Mir, gpa: Allocator) void { - mir.uavs.deinit(gpa); + gpa.free(mir.fwd_decl); gpa.free(mir.code_header); gpa.free(mir.code); - gpa.free(mir.fwd_decl); - mir.ctype_pool.deinit(gpa); - mir.lazy_fns.deinit(gpa); + mir.need_uavs.deinit(gpa); + mir.ctype_deps.deinit(gpa); + mir.need_tag_name_funcs.deinit(gpa); + mir.need_never_tail_funcs.deinit(gpa); + mir.need_never_inline_funcs.deinit(gpa); } }; -pub const Error = Writer.Error || std.mem.Allocator.Error || error{AnalysisFail}; +pub const Error = Writer.Error || Allocator.Error || error{AnalysisFail}; -pub const CType = @import("c/Type.zig"); +pub const CType = @import("c/type.zig").CType; pub const CValue = union(enum) { none: void, @@ -87,8 +94,6 @@ pub const CValue = union(enum) { constant: Value, /// Index into the parameters arg: usize, - /// The array field of a parameter - arg_array: usize, /// Index into a tuple's fields field: usize, /// By-value @@ -100,8 +105,6 @@ pub const CValue = union(enum) { identifier: []const u8, /// Rendered as "payload." followed by as identifier (using fmtIdent) payload_identifier: []const u8, - /// Rendered with fmtCTypePoolString - ctype_pool_string: CType.Pool.String, fn eql(lhs: CValue, rhs: CValue) bool { return switch (lhs) { @@ -122,10 +125,6 @@ pub const CValue = union(enum) { .arg => |rhs_arg_index| lhs_arg_index == rhs_arg_index, else => false, }, - .arg_array => |lhs_arg_index| switch (rhs) { - .arg_array => |rhs_arg_index| lhs_arg_index == rhs_arg_index, - else => false, - }, .field => |lhs_field_index| switch (rhs) { .field => |rhs_field_index| lhs_field_index == rhs_field_index, else => false, @@ -150,10 +149,6 @@ pub const CValue = union(enum) { .payload_identifier => |rhs_id| std.mem.eql(u8, lhs_id, rhs_id), else => false, }, - .ctype_pool_string => |lhs_str| switch (rhs) { - .ctype_pool_string => |rhs_str| lhs_str.index == rhs_str.index, - else => false, - }, }; } }; @@ -163,53 +158,24 @@ const BlockData = struct { result: CValue, }; -pub const CValueMap = std.AutoHashMap(Air.Inst.Ref, CValue); - -pub const LazyFnKey = union(enum) { - tag_name: InternPool.Index, - never_tail: InternPool.Nav.Index, - never_inline: InternPool.Nav.Index, -}; -pub const LazyFnValue = struct { - fn_name: CType.Pool.String, -}; -pub const LazyFnMap = std.AutoArrayHashMapUnmanaged(LazyFnKey, LazyFnValue); - -const Local = struct { - ctype: CType, - flags: packed struct(u32) { - alignas: CType.AlignAs, - _: u20 = undefined, - }, - - fn getType(local: Local) LocalType { - return .{ .ctype = local.ctype, .alignas = local.flags.alignas }; - } +const LocalType = struct { + type: Type, + alignment: Alignment, }; const LocalIndex = u16; -const LocalType = struct { ctype: CType, alignas: CType.AlignAs }; const LocalsList = std.AutoArrayHashMapUnmanaged(LocalIndex, void); const LocalsMap = std.AutoArrayHashMapUnmanaged(LocalType, LocalsList); const ValueRenderLocation = enum { - FunctionArgument, - Initializer, - StaticInitializer, - Other, + initializer, + static_initializer, + other, fn isInitializer(loc: ValueRenderLocation) bool { return switch (loc) { - .Initializer, .StaticInitializer => true, - else => false, - }; - } - - fn toCTypeKind(loc: ValueRenderLocation) CType.Kind { - return switch (loc) { - .FunctionArgument => .parameter, - .Initializer, .Other => .complete, - .StaticInitializer => .global, + .initializer, .static_initializer => true, + .other => false, }; } }; @@ -334,16 +300,31 @@ const reserved_idents = std.StaticStringMap(void).initComptime(.{ }); fn isReservedIdent(ident: []const u8) bool { - if (ident.len >= 2 and ident[0] == '_') { // C language + // C language + if (ident.len >= 2 and ident[0] == '_') { switch (ident[1]) { 'A'...'Z', '_' => return true, - else => return false, + else => {}, } - } else if (mem.startsWith(u8, ident, "DUMMYSTRUCTNAME") or + } + + // windows.h + if (mem.startsWith(u8, ident, "DUMMYSTRUCTNAME") or mem.startsWith(u8, ident, "DUMMYUNIONNAME")) - { // windows.h + { + return true; + } + + // CType + if (mem.startsWith(u8, ident, "enum__") or + mem.startsWith(u8, ident, "bitpack__") or + mem.startsWith(u8, ident, "aligned__") or + mem.startsWith(u8, ident, "fn__")) + { return true; - } else return reserved_idents.has(ident); + } + + return reserved_idents.has(ident); } fn formatIdentSolo(ident: []const u8, w: *Writer) Writer.Error!void { @@ -361,7 +342,7 @@ fn formatIdentOptions(ident: []const u8, w: *Writer, solo: bool) Writer.Error!vo for (ident, 0..) |c, i| { switch (c) { 'a'...'z', 'A'...'Z', '_' => try w.writeByte(c), - '.' => try w.writeByte('_'), + '.', ' ' => try w.writeByte('_'), '0'...'9' => if (i == 0) { try w.print("_{x:2}", .{c}); } else { @@ -380,29 +361,6 @@ pub fn fmtIdentUnsolo(ident: []const u8) std.fmt.Alt([]const u8, formatIdentUnso return .{ .data = ident }; } -const CTypePoolStringFormatData = struct { - ctype_pool_string: CType.Pool.String, - ctype_pool: *const CType.Pool, - solo: bool, -}; -fn formatCTypePoolString(data: CTypePoolStringFormatData, w: *Writer) Writer.Error!void { - if (data.ctype_pool_string.toSlice(data.ctype_pool)) |slice| - try formatIdentOptions(slice, w, data.solo) - else - try w.print("{f}", .{data.ctype_pool_string.fmt(data.ctype_pool)}); -} -pub fn fmtCTypePoolString( - ctype_pool_string: CType.Pool.String, - ctype_pool: *const CType.Pool, - solo: bool, -) std.fmt.Alt(CTypePoolStringFormatData, formatCTypePoolString) { - return .{ .data = .{ - .ctype_pool_string = ctype_pool_string, - .ctype_pool = ctype_pool, - .solo = solo, - } }; -} - // Returns true if `formatIdent` would make any edits to ident. // This must be kept in sync with `formatIdent`. pub fn isMangledIdent(ident: []const u8, solo: bool) bool { @@ -417,21 +375,26 @@ pub fn isMangledIdent(ident: []const u8, solo: bool) bool { return false; } -/// This data is available when outputting .c code for a `InternPool.Index` -/// that corresponds to `func`. -/// It is not available when generating .h file. +/// This data is available when rendering C source code for an interned function. pub const Function = struct { air: Air, liveness: Air.Liveness, - value_map: CValueMap, + value_map: std.AutoHashMap(Air.Inst.Ref, CValue), blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty, next_arg_index: u32 = 0, next_block_index: u32 = 0, - object: Object, - lazy_fns: LazyFnMap, + dg: DeclGen, + code: Writer.Allocating, + indent_counter: usize, + /// Key is an enum type for which we need a generated `@tagName` function. + need_tag_name_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), + /// Key is a function Nav for which we need a generated `zig_never_tail` wrapper. + need_never_tail_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void), + /// Key is a function Nav for which we need a generated `zig_never_inline` wrapper. + need_never_inline_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void), func_index: InternPool.Index, /// All the locals, to be emitted at the top of the function. - locals: std.ArrayList(Local) = .empty, + locals: std.ArrayList(LocalType) = .empty, /// Which locals are available for reuse, based on Type. free_locals_map: LocalsMap = .{}, /// Locals which will not be freed by Liveness. This is used after a @@ -445,37 +408,41 @@ pub const Function = struct { /// for the switch cond. Dispatches should set this local to the new cond. loop_switch_conds: std.AutoHashMapUnmanaged(Air.Inst.Index, LocalIndex) = .empty, + const indent_width = 1; + const indent_char = ' '; + + fn newline(f: *Function) !void { + const w = &f.code.writer; + try w.writeByte('\n'); + try w.splatByteAll(indent_char, f.indent_counter); + } + fn indent(f: *Function) void { + f.indent_counter += indent_width; + } + fn outdent(f: *Function) !void { + f.indent_counter -= indent_width; + const written = f.code.written(); + switch (written[written.len - 1]) { + indent_char => f.code.shrinkRetainingCapacity(written.len - indent_width), + '\n' => try f.code.writer.splatByteAll(indent_char, f.indent_counter), + else => { + std.debug.print("\"{f}\"\n", .{std.zig.fmtString(written[written.len -| 100..])}); + unreachable; + }, + } + } + fn resolveInst(f: *Function, ref: Air.Inst.Ref) !CValue { const gop = try f.value_map.getOrPut(ref); - if (gop.found_existing) return gop.value_ptr.*; - - const pt = f.object.dg.pt; - const zcu = pt.zcu; - const val = (try f.air.value(ref, pt)).?; - const ty = f.typeOf(ref); - - const result: CValue = if (lowersToArray(ty, zcu)) result: { - const ch = &f.object.code_header.writer; - const decl_c_value = try f.allocLocalValue(.{ - .ctype = try f.ctypeFromType(ty, .complete), - .alignas = CType.AlignAs.fromAbiAlignment(ty.abiAlignment(zcu)), - }); - const gpa = f.object.dg.gpa; - try f.allocs.put(gpa, decl_c_value.new_local, false); - try ch.writeAll("static "); - try f.object.dg.renderTypeAndName(ch, ty, decl_c_value, Const, .none, .complete); - try ch.writeAll(" = "); - try f.object.dg.renderValue(ch, val, .StaticInitializer); - try ch.writeAll(";\n "); - break :result .{ .local = decl_c_value.new_local }; - } else .{ .constant = val }; - - gop.value_ptr.* = result; - return result; + if (!gop.found_existing) { + const val = try f.air.value(ref, f.dg.pt); + gop.value_ptr.* = .{ .constant = val.? }; + } + return gop.value_ptr.*; } fn wantSafety(f: *Function) bool { - return switch (f.object.dg.pt.zcu.optimizeMode()) { + return switch (f.dg.pt.zcu.optimizeMode()) { .Debug, .ReleaseSafe => true, .ReleaseFast, .ReleaseSmall => false, }; @@ -485,18 +452,16 @@ pub const Function = struct { /// those which go into `allocs`. This function does not add the resulting local into `allocs`; /// that responsibility lies with the caller. fn allocLocalValue(f: *Function, local_type: LocalType) !CValue { - try f.locals.ensureUnusedCapacity(f.object.dg.gpa, 1); - defer f.locals.appendAssumeCapacity(.{ - .ctype = local_type.ctype, - .flags = .{ .alignas = local_type.alignas }, - }); - return .{ .new_local = @intCast(f.locals.items.len) }; + try f.locals.ensureUnusedCapacity(f.dg.gpa, 1); + const index = f.locals.items.len; + f.locals.appendAssumeCapacity(local_type); + return .{ .new_local = @intCast(index) }; } fn allocLocal(f: *Function, inst: ?Air.Inst.Index, ty: Type) !CValue { return f.allocAlignedLocal(inst, .{ - .ctype = try f.ctypeFromType(ty, .complete), - .alignas = CType.AlignAs.fromAbiAlignment(ty.abiAlignment(f.object.dg.pt.zcu)), + .type = ty, + .alignment = .none, }); } @@ -524,11 +489,10 @@ pub const Function = struct { .none => unreachable, .new_local, .local => |i| try w.print("t{d}", .{i}), .local_ref => |i| try w.print("&t{d}", .{i}), - .constant => |val| try f.object.dg.renderValue(w, val, location), + .constant => |val| try f.dg.renderValue(w, val, location), .arg => |i| try w.print("a{d}", .{i}), - .arg_array => |i| try f.writeCValueMember(w, .{ .arg = i }, .{ .identifier = "array" }), - .undef => |ty| try f.object.dg.renderUndefValue(w, ty, location), - else => try f.object.dg.writeCValue(w, c_value), + .undef => |ty| try f.dg.renderUndefValue(w, ty, location), + else => try f.dg.writeCValue(w, c_value), } } @@ -537,17 +501,12 @@ pub const Function = struct { .none => unreachable, .new_local, .local, .constant => { try w.writeAll("(*"); - try f.writeCValue(w, c_value, .Other); + try f.writeCValue(w, c_value, .other); try w.writeByte(')'); }, .local_ref => |i| try w.print("t{d}", .{i}), .arg => |i| try w.print("(*a{d})", .{i}), - .arg_array => |i| { - try w.writeAll("(*"); - try f.writeCValueMember(w, .{ .arg = i }, .{ .identifier = "array" }); - try w.writeByte(')'); - }, - else => try f.object.dg.writeCValueDeref(w, c_value), + else => try f.dg.writeCValueDeref(w, c_value), } } @@ -558,119 +517,77 @@ pub const Function = struct { member: CValue, ) Error!void { switch (c_value) { - .new_local, .local, .local_ref, .constant, .arg, .arg_array => { - try f.writeCValue(w, c_value, .Other); + .new_local, .local, .local_ref, .constant, .arg => { + try f.writeCValue(w, c_value, .other); try w.writeByte('.'); - try f.writeCValue(w, member, .Other); + try f.writeCValue(w, member, .other); }, - else => return f.object.dg.writeCValueMember(w, c_value, member), + else => return f.dg.writeCValueMember(w, c_value, member), } } fn writeCValueDerefMember(f: *Function, w: *Writer, c_value: CValue, member: CValue) !void { switch (c_value) { - .new_local, .local, .arg, .arg_array => { - try f.writeCValue(w, c_value, .Other); + .new_local, .local, .arg => { + try f.writeCValue(w, c_value, .other); try w.writeAll("->"); }, .constant => { try w.writeByte('('); - try f.writeCValue(w, c_value, .Other); + try f.writeCValue(w, c_value, .other); try w.writeAll(")->"); }, .local_ref => { try f.writeCValueDeref(w, c_value); try w.writeByte('.'); }, - else => return f.object.dg.writeCValueDerefMember(w, c_value, member), + else => return f.dg.writeCValueDerefMember(w, c_value, member), } - try f.writeCValue(w, member, .Other); + try f.writeCValue(w, member, .other); } fn fail(f: *Function, comptime format: []const u8, args: anytype) Error { - return f.object.dg.fail(format, args); - } - - fn ctypeFromType(f: *Function, ty: Type, kind: CType.Kind) !CType { - return f.object.dg.ctypeFromType(ty, kind); - } - - fn byteSize(f: *Function, ctype: CType) u64 { - return f.object.dg.byteSize(ctype); - } - - fn renderType(f: *Function, w: *Writer, ctype: Type) !void { - return f.object.dg.renderType(w, ctype); + return f.dg.fail(format, args); } - fn renderCType(f: *Function, w: *Writer, ctype: CType) !void { - return f.object.dg.renderCType(w, ctype); + fn renderType(f: *Function, w: *Writer, ty: Type) !void { + return f.dg.renderType(w, ty); } fn renderIntCast(f: *Function, w: *Writer, dest_ty: Type, src: CValue, v: Vectorize, src_ty: Type, location: ValueRenderLocation) !void { - return f.object.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src, .v = v } }, src_ty, location); + return f.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src, .v = v } }, src_ty, location); } fn fmtIntLiteralDec(f: *Function, val: Value) !std.fmt.Alt(FormatIntLiteralContext, formatIntLiteral) { - return f.object.dg.fmtIntLiteralDec(val, .Other); + return f.dg.fmtIntLiteralDec(val, .other); } fn fmtIntLiteralHex(f: *Function, val: Value) !std.fmt.Alt(FormatIntLiteralContext, formatIntLiteral) { - return f.object.dg.fmtIntLiteralHex(val, .Other); - } - - fn getLazyFnName(f: *Function, key: LazyFnKey) ![]const u8 { - const gpa = f.object.dg.gpa; - const pt = f.object.dg.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const ctype_pool = &f.object.dg.ctype_pool; - - const gop = try f.lazy_fns.getOrPut(gpa, key); - if (!gop.found_existing) { - errdefer _ = f.lazy_fns.pop(); - - gop.value_ptr.* = .{ - .fn_name = switch (key) { - .tag_name, - => |enum_ty| try ctype_pool.fmt(gpa, "zig_{s}_{f}__{d}", .{ - @tagName(key), - fmtIdentUnsolo(ip.loadEnumType(enum_ty).name.toSlice(ip)), - @intFromEnum(enum_ty), - }), - .never_tail, - .never_inline, - => |owner_nav| try ctype_pool.fmt(gpa, "zig_{s}_{f}__{d}", .{ - @tagName(key), - fmtIdentUnsolo(ip.getNav(owner_nav).name.toSlice(ip)), - @intFromEnum(owner_nav), - }), - }, - }; - } - return gop.value_ptr.fn_name.toSlice(ctype_pool).?; + return f.dg.fmtIntLiteralHex(val, .other); } pub fn deinit(f: *Function) void { - const gpa = f.object.dg.gpa; + const gpa = f.dg.gpa; f.allocs.deinit(gpa); f.locals.deinit(gpa); deinitFreeLocalsMap(gpa, &f.free_locals_map); f.blocks.deinit(gpa); f.value_map.deinit(); - f.lazy_fns.deinit(gpa); + f.need_tag_name_funcs.deinit(gpa); + f.need_never_tail_funcs.deinit(gpa); + f.need_never_inline_funcs.deinit(gpa); f.loop_switch_conds.deinit(gpa); } fn typeOf(f: *Function, inst: Air.Inst.Ref) Type { - return f.air.typeOf(inst, &f.object.dg.pt.zcu.intern_pool); + return f.air.typeOf(inst, &f.dg.pt.zcu.intern_pool); } fn typeOfIndex(f: *Function, inst: Air.Inst.Index) Type { - return f.air.typeOfIndex(inst, &f.object.dg.pt.zcu.intern_pool); + return f.air.typeOfIndex(inst, &f.dg.pt.zcu.intern_pool); } - fn copyCValue(f: *Function, ctype: CType, dst: CValue, src: CValue) !void { + fn copyCValue(f: *Function, dst: CValue, src: CValue) !void { switch (dst) { .new_local, .local => |dst_local_index| switch (src) { .new_local, .local => |src_local_index| if (dst_local_index == src_local_index) return, @@ -678,12 +595,12 @@ pub const Function = struct { }, else => {}, } - const w = &f.object.code.writer; - const a = try Assignment.start(f, w, ctype); - try f.writeCValue(w, dst, .Other); - try a.assign(f, w); - try f.writeCValue(w, src, .Other); - try a.end(f, w); + const w = &f.code.writer; + try f.writeCValue(w, dst, .other); + try w.writeAll(" = "); + try f.writeCValue(w, src, .other); + try w.writeByte(';'); + try f.newline(); } fn moveCValue(f: *Function, inst: Air.Inst.Index, ty: Type, src: CValue) !CValue { @@ -694,7 +611,7 @@ pub const Function = struct { else => { try freeCValue(f, inst, src); const dst = try f.allocLocal(inst, ty); - try f.copyCValue(try f.ctypeFromType(ty, .complete), dst, src); + try f.copyCValue(dst, src); return dst; }, } @@ -708,51 +625,17 @@ pub const Function = struct { } }; -/// This data is available when outputting .c code for a `Zcu`. -/// It is not available when generating .h file. -pub const Object = struct { - dg: DeclGen, - code_header: Writer.Allocating, - code: Writer.Allocating, - indent_counter: usize, - - const indent_width = 1; - const indent_char = ' '; - - fn newline(o: *Object) !void { - const w = &o.code.writer; - try w.writeByte('\n'); - try w.splatByteAll(indent_char, o.indent_counter); - } - fn indent(o: *Object) void { - o.indent_counter += indent_width; - } - fn outdent(o: *Object) !void { - o.indent_counter -= indent_width; - const written = o.code.written(); - switch (written[written.len - 1]) { - indent_char => o.code.shrinkRetainingCapacity(written.len - indent_width), - '\n' => try o.code.writer.splatByteAll(indent_char, o.indent_counter), - else => { - std.debug.print("\"{f}\"\n", .{std.zig.fmtString(written[written.len -| 100..])}); - unreachable; - }, - } - } -}; - -/// This data is available both when outputting .c code and when outputting an .h file. +/// This data is available when rendering *any* C source code (function or otherwise). pub const DeclGen = struct { gpa: Allocator, + arena: Allocator, pt: Zcu.PerThread, mod: *Module, - pass: Pass, + owner_nav: InternPool.Nav.Index.Optional, is_naked_fn: bool, expected_block: ?u32, - fwd_decl: Writer.Allocating, error_msg: ?*Zcu.ErrorMsg, - ctype_pool: CType.Pool, - scratch: std.ArrayList(u32), + ctype_deps: CType.Dependencies, /// This map contains all the UAVs we saw generating this function. /// `link.C` will merge them into its `uavs`/`aligned_uavs` fields. /// Key is the value of the UAV; value is the UAV's alignment, or @@ -760,16 +643,10 @@ pub const DeclGen = struct { /// less than the natural alignment. uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), - pub const Pass = union(enum) { - nav: InternPool.Nav.Index, - uav: InternPool.Index, - flush, - }; - fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) Error { @branchHint(.cold); const zcu = dg.pt.zcu; - const src_loc = zcu.navSrcLoc(dg.pass.nav); + const src_loc = zcu.navSrcLoc(dg.owner_nav.unwrap().?); dg.error_msg = try Zcu.ErrorMsg.create(dg.gpa, src_loc, format, args); return error.AnalysisFail; } @@ -783,14 +660,13 @@ pub const DeclGen = struct { const pt = dg.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - const ctype_pool = &dg.ctype_pool; const uav_val = Value.fromInterned(uav.val); const uav_ty = uav_val.typeOf(zcu); // Render an undefined pointer if we have a pointer to a zero-bit or comptime type. const ptr_ty: Type = .fromInterned(uav.orig_ty); - if (ptr_ty.isPtrAtRuntime(zcu) and !uav_ty.isFnOrHasRuntimeBits(zcu)) { - return dg.writeCValue(w, .{ .undef = ptr_ty }); + if (ptr_ty.isPtrAtRuntime(zcu) and !uav_ty.isRuntimeFnOrHasRuntimeBits(zcu)) { + return dg.renderUndefValue(w, ptr_ty, location); } // Chase function values in order to be able to reference the original function. @@ -805,14 +681,12 @@ pub const DeclGen = struct { // them). The analysis until now should ensure that the C function // pointers are compatible. If they are not, then there is a bug // somewhere and we should let the C compiler tell us about it. - const ptr_ctype = try dg.ctypeFromType(ptr_ty, .complete); - const elem_ctype = ptr_ctype.info(ctype_pool).pointer.elem_ctype; - const uav_ctype = try dg.ctypeFromType(uav_ty, .complete); - const need_cast = !elem_ctype.eql(uav_ctype) and - (elem_ctype.info(ctype_pool) != .function or uav_ctype.info(ctype_pool) != .function); + const elem_ty = ptr_ty.childType(zcu); + const need_cast = elem_ty.toIntern() != uav_ty.toIntern() and + elem_ty.zigTypeTag(zcu) != .@"fn" or uav_ty.zigTypeTag(zcu) != .@"fn"; if (need_cast) { try w.writeAll("(("); - try dg.renderCType(w, ptr_ctype); + try dg.renderType(w, ptr_ty); try w.writeByte(')'); } try w.writeByte('&'); @@ -842,11 +716,9 @@ pub const DeclGen = struct { nav_index: InternPool.Nav.Index, location: ValueRenderLocation, ) Error!void { - _ = location; const pt = dg.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - const ctype_pool = &dg.ctype_pool; // Chase function values in order to be able to reference the original function. const owner_nav = switch (ip.getNav(nav_index).status) { @@ -862,26 +734,24 @@ pub const DeclGen = struct { // Render an undefined pointer if we have a pointer to a zero-bit or comptime type. const nav_ty: Type = .fromInterned(ip.getNav(owner_nav).typeOf(ip)); const ptr_ty = try pt.navPtrType(owner_nav); - if (!nav_ty.isFnOrHasRuntimeBits(zcu)) { - return dg.writeCValue(w, .{ .undef = ptr_ty }); + if (!nav_ty.isRuntimeFnOrHasRuntimeBits(zcu)) { + return dg.renderUndefValue(w, ptr_ty, location); } // We shouldn't cast C function pointers as this is UB (when you call // them). The analysis until now should ensure that the C function // pointers are compatible. If they are not, then there is a bug // somewhere and we should let the C compiler tell us about it. - const ctype = try dg.ctypeFromType(ptr_ty, .complete); - const elem_ctype = ctype.info(ctype_pool).pointer.elem_ctype; - const nav_ctype = try dg.ctypeFromType(nav_ty, .complete); - const need_cast = !elem_ctype.eql(nav_ctype) and - (elem_ctype.info(ctype_pool) != .function or nav_ctype.info(ctype_pool) != .function); + const elem_ty = ptr_ty.childType(zcu); + const need_cast = elem_ty.toIntern() != nav_ty.toIntern() and + elem_ty.zigTypeTag(zcu) != .@"fn" or nav_ty.zigTypeTag(zcu) != .@"fn"; if (need_cast) { try w.writeAll("(("); - try dg.renderCType(w, ctype); + try dg.renderType(w, ptr_ty); try w.writeByte(')'); } try w.writeByte('&'); - try dg.renderNavName(w, owner_nav); + try renderNavName(w, owner_nav, ip); if (need_cast) try w.writeByte(')'); } @@ -896,11 +766,10 @@ pub const DeclGen = struct { switch (derivation) { .comptime_alloc_ptr, .comptime_field_ptr => unreachable, .int => |int| { - const ptr_ctype = try dg.ctypeFromType(int.ptr_ty, .complete); const addr_val = try pt.intValue(.usize, int.addr); try w.writeByte('('); - try dg.renderCType(w, ptr_ctype); - try w.print("){f}", .{try dg.fmtIntLiteralHex(addr_val, .Other)}); + try dg.renderType(w, int.ptr_ty); + try w.print("){f}", .{try dg.fmtIntLiteralHex(addr_val, .other)}); }, .nav_ptr => |nav| try dg.renderNav(w, nav, location), @@ -915,14 +784,10 @@ pub const DeclGen = struct { .field_ptr => |field| { const parent_ptr_ty = try field.parent.ptrType(pt); - // Ensure complete type definition is available before accessing fields. - _ = try dg.ctypeFromType(parent_ptr_ty.childType(zcu), .complete); - switch (fieldLocation(parent_ptr_ty, field.result_ptr_ty, field.field_idx, zcu)) { .begin => { - const ptr_ctype = try dg.ctypeFromType(field.result_ptr_ty, .complete); try w.writeByte('('); - try dg.renderCType(w, ptr_ctype); + try dg.renderType(w, field.result_ptr_ty); try w.writeByte(')'); try dg.renderPointer(w, field.parent.*, location); }, @@ -933,51 +798,40 @@ pub const DeclGen = struct { try dg.writeCValue(w, name); }, .byte_offset => |byte_offset| { - const ptr_ctype = try dg.ctypeFromType(field.result_ptr_ty, .complete); try w.writeByte('('); - try dg.renderCType(w, ptr_ctype); + try dg.renderType(w, field.result_ptr_ty); try w.writeByte(')'); const offset_val = try pt.intValue(.usize, byte_offset); try w.writeAll("((char *)"); try dg.renderPointer(w, field.parent.*, location); - try w.print(" + {f})", .{try dg.fmtIntLiteralDec(offset_val, .Other)}); + try w.print(" + {f})", .{try dg.fmtIntLiteralDec(offset_val, .other)}); }, } }, .elem_ptr => |elem| if (!(try elem.parent.ptrType(pt)).childType(zcu).hasRuntimeBits(zcu)) { // Element type is zero-bit, so lowers to `void`. The index is irrelevant; just cast the pointer. - const ptr_ctype = try dg.ctypeFromType(elem.result_ptr_ty, .complete); try w.writeByte('('); - try dg.renderCType(w, ptr_ctype); + try dg.renderType(w, elem.result_ptr_ty); try w.writeByte(')'); try dg.renderPointer(w, elem.parent.*, location); } else { const index_val = try pt.intValue(.usize, elem.elem_idx); - // We want to do pointer arithmetic on a pointer to the element type. - // We might have a pointer-to-array. In this case, we must cast first. - const result_ctype = try dg.ctypeFromType(elem.result_ptr_ty, .complete); - const parent_ctype = try dg.ctypeFromType(try elem.parent.ptrType(pt), .complete); - if (result_ctype.eql(parent_ctype)) { - // The pointer already has an appropriate type - just do the arithmetic. + try w.writeByte('('); + // We want to do pointer arithmetic on a pointer to the element type, but the parent + // might be a pointer-to-array, in which case we must cast it. + if (elem.result_ptr_ty.toIntern() != (try elem.parent.ptrType(pt)).toIntern()) { try w.writeByte('('); - try dg.renderPointer(w, elem.parent.*, location); - try w.print(" + {f})", .{try dg.fmtIntLiteralDec(index_val, .Other)}); - } else { - // We probably have an array pointer `T (*)[n]`. Cast to an element pointer, - // and *then* apply the index. - try w.writeAll("(("); - try dg.renderCType(w, result_ctype); + try dg.renderType(w, elem.result_ptr_ty); try w.writeByte(')'); - try dg.renderPointer(w, elem.parent.*, location); - try w.print(" + {f})", .{try dg.fmtIntLiteralDec(index_val, .Other)}); } + try dg.renderPointer(w, elem.parent.*, location); + try w.print(" + {f})", .{try dg.fmtIntLiteralDec(index_val, .other)}); }, .offset_and_cast => |oac| { - const ptr_ctype = try dg.ctypeFromType(oac.new_ptr_ty, .complete); try w.writeByte('('); - try dg.renderCType(w, ptr_ctype); + try dg.renderType(w, oac.new_ptr_ty); try w.writeByte(')'); if (oac.byte_offset == 0) { try dg.renderPointer(w, oac.parent.*, location); @@ -985,14 +839,40 @@ pub const DeclGen = struct { const offset_val = try pt.intValue(.usize, oac.byte_offset); try w.writeAll("((char *)"); try dg.renderPointer(w, oac.parent.*, location); - try w.print(" + {f})", .{try dg.fmtIntLiteralDec(offset_val, .Other)}); + try w.print(" + {f})", .{try dg.fmtIntLiteralDec(offset_val, .other)}); } }, } } - fn renderErrorName(dg: *DeclGen, w: *Writer, err_name: InternPool.NullTerminatedString) !void { - try w.print("zig_error_{f}", .{fmtIdentUnsolo(err_name.toSlice(&dg.pt.zcu.intern_pool))}); + fn renderValueAsLvalue( + dg: *DeclGen, + w: *Writer, + val: Value, + ) Error!void { + const zcu = dg.pt.zcu; + + // If the type of `val` lowers to a C struct or union type, then `renderValue` will render + // it as a compound literal, and compound literals are already lvalues. + const ty = val.typeOf(zcu); + const is_aggregate: bool = switch (ty.zigTypeTag(zcu)) { + .@"struct", .@"union" => switch (ty.containerLayout(zcu)) { + .auto, .@"extern" => true, + .@"packed" => false, + }, + .array, + .vector, + .error_union, + .optional, + => true, + else => false, + }; + if (is_aggregate) return renderValue(dg, w, val, .other); + + // Otherwise, use a UAV. + const gop = try dg.uavs.getOrPut(dg.gpa, val.toIntern()); + if (!gop.found_existing) gop.value_ptr.* = .none; + try renderUavName(w, val); } fn renderValue( @@ -1005,16 +885,13 @@ pub const DeclGen = struct { const zcu = pt.zcu; const ip = &zcu.intern_pool; const target = &dg.mod.resolved_target.result; - const ctype_pool = &dg.ctype_pool; const initializer_type: ValueRenderLocation = switch (location) { - .StaticInitializer => .StaticInitializer, - else => .Initializer, + .static_initializer => .static_initializer, + else => .initializer, }; const ty = val.typeOf(zcu); - if (val.isUndef(zcu)) return dg.renderUndefValue(w, ty, location); - const ctype = try dg.ctypeFromType(ty, location.toCTypeKind()); switch (ip.indexToKey(val.toIntern())) { // types, not values .int_type, @@ -1037,13 +914,11 @@ pub const DeclGen = struct { .memoized_call, => unreachable, - .undef => unreachable, // handled above + .undef => try dg.renderUndefValue(w, ty, location), .simple_value => |simple_value| switch (simple_value) { // non-runtime values - .undefined => unreachable, .void => unreachable, .null => unreachable, - .empty_tuple => unreachable, .@"unreachable" => unreachable, .false => try w.writeAll("false"), @@ -1053,59 +928,30 @@ pub const DeclGen = struct { .@"extern", .func, .enum_literal, - .empty_enum_value, => unreachable, // non-runtime values - .int => |int| switch (int.storage) { - .u64, .i64, .big_int => try w.print("{f}", .{try dg.fmtIntLiteralDec(val, location)}), - .lazy_align, .lazy_size => { - try w.writeAll("(("); - try dg.renderCType(w, ctype); - try w.print("){f})", .{try dg.fmtIntLiteralHex( - try pt.intValue(.usize, val.toUnsignedInt(zcu)), - .Other, - )}); - }, - }, - .err => |err| try dg.renderErrorName(w, err.name), - .error_union => |error_union| switch (ctype.info(ctype_pool)) { - .basic => switch (error_union.val) { - .err_name => |err_name| try dg.renderErrorName(w, err_name), + .int => try w.print("{f}", .{try dg.fmtIntLiteralDec(val, location)}), + .err => |err| try renderErrorName(w, err.name.toSlice(ip)), + .error_union => |error_union| { + if (!location.isInitializer()) { + try w.writeByte('('); + try dg.renderType(w, ty); + try w.writeByte(')'); + } + try w.writeAll("{ .error = "); + switch (error_union.val) { + .err_name => |err_name| try renderErrorName(w, err_name.toSlice(ip)), .payload => try w.writeByte('0'), - }, - .pointer, .aligned, .array, .vector, .fwd_decl, .function => unreachable, - .aggregate => |aggregate| { - if (!location.isInitializer()) { - try w.writeByte('('); - try dg.renderCType(w, ctype); - try w.writeByte(')'); - } - try w.writeByte('{'); - for (0..aggregate.fields.len) |field_index| { - if (field_index > 0) try w.writeByte(','); - switch (aggregate.fields.at(field_index, ctype_pool).name.index) { - .@"error" => switch (error_union.val) { - .err_name => |err_name| try dg.renderErrorName(w, err_name), - .payload => try w.writeByte('0'), - }, - .payload => switch (error_union.val) { - .err_name => try dg.renderUndefValue( - w, - ty.errorUnionPayload(zcu), - initializer_type, - ), - .payload => |payload| try dg.renderValue( - w, - Value.fromInterned(payload), - initializer_type, - ), - }, - else => unreachable, - } + } + if (ty.errorUnionPayload(zcu).hasRuntimeBits(zcu)) { + try w.writeAll(", .payload = "); + switch (error_union.val) { + .err_name => try dg.renderUndefValue(w, ty.errorUnionPayload(zcu), initializer_type), + .payload => |payload| try dg.renderValue(w, .fromInterned(payload), initializer_type), } - try w.writeByte('}'); - }, + } + try w.writeAll(" }"); }, - .enum_tag => |enum_tag| try dg.renderValue(w, Value.fromInterned(enum_tag.int), location), + .enum_tag => |enum_tag| try dg.renderValue(w, .fromInterned(enum_tag.int), location), .float => { const bits = ty.floatBits(target); const f128_val = val.toFloat(f128, zcu); @@ -1156,7 +1002,7 @@ pub const DeclGen = struct { else unreachable; - if (location == .StaticInitializer) { + if (location == .static_initializer) { if (!std.math.isNan(f128_val) and std.math.isSignalNan(f128_val)) return dg.fail("TODO: C backend: implement nans rendering in static initializers", .{}); @@ -1167,9 +1013,11 @@ pub const DeclGen = struct { // return dg.fail("Only quiet nans are supported in global variable initializers", .{}); } - try w.writeAll("zig_"); - try w.writeAll(if (location == .StaticInitializer) "init" else "make"); - try w.writeAll("_special_"); + if (location == .static_initializer) { + try w.writeAll("zig_init_special_"); + } else { + try w.writeAll("zig_make_special_"); + } try dg.renderTypeForBuiltinFnName(w, ty); try w.writeByte('('); if (std.math.signbit(f128_val)) try w.writeByte('-'); @@ -1196,105 +1044,85 @@ pub const DeclGen = struct { if (!empty) try w.writeByte(')'); }, .slice => |slice| { - const aggregate = ctype.info(ctype_pool).aggregate; if (!location.isInitializer()) { try w.writeByte('('); - try dg.renderCType(w, ctype); + try dg.renderType(w, ty); try w.writeByte(')'); } try w.writeByte('{'); - for (0..aggregate.fields.len) |field_index| { - if (field_index > 0) try w.writeByte(','); - try dg.renderValue(w, Value.fromInterned( - switch (aggregate.fields.at(field_index, ctype_pool).name.index) { - .ptr => slice.ptr, - .len => slice.len, - else => unreachable, - }, - ), initializer_type); - } + try dg.renderValue(w, .fromInterned(slice.ptr), initializer_type); + try w.writeByte(','); + try dg.renderValue(w, .fromInterned(slice.len), initializer_type); try w.writeByte('}'); }, .ptr => { - var arena = std.heap.ArenaAllocator.init(zcu.gpa); - defer arena.deinit(); - const derivation = try val.pointerDerivation(arena.allocator(), pt); + const derivation = try val.pointerDerivation(dg.arena, pt, null); + try w.writeByte('('); try dg.renderPointer(w, derivation, location); + try w.writeByte(')'); }, - .opt => |opt| switch (ctype.info(ctype_pool)) { - .basic => if (ctype.isBool()) try w.writeAll(switch (opt.val) { - .none => "true", - else => "false", - }) else switch (opt.val) { + .opt => |opt| switch (CType.classifyOptional(ty, zcu)) { + .npv_payload => unreachable, // opv optional + .opv_payload => { + if (!location.isInitializer()) { + try w.writeByte('('); + try dg.renderType(w, ty); + try w.writeByte(')'); + } + try w.writeAll(switch (opt.val) { + .none => "{.is_null = true}", + else => "{.is_null = false}", + }); + }, + .error_set => switch (opt.val) { .none => try w.writeByte('0'), - else => |payload| switch (ip.indexToKey(payload)) { - .undef => |err_ty| try dg.renderUndefValue( - w, - .fromInterned(err_ty), - location, - ), - .err => |err| try dg.renderErrorName(w, err.name), - else => unreachable, - }, + else => |payload_val| try dg.renderValue(w, .fromInterned(payload_val), location), }, - .pointer => switch (opt.val) { + .ptr_like => switch (opt.val) { .none => try w.writeAll("NULL"), - else => |payload| try dg.renderValue(w, Value.fromInterned(payload), location), + else => |payload_val| try dg.renderValue(w, .fromInterned(payload_val), location), }, - .aligned, .array, .vector, .fwd_decl, .function => unreachable, - .aggregate => |aggregate| { - switch (opt.val) { - .none => {}, - else => |payload| switch (aggregate.fields.at(0, ctype_pool).name.index) { - .is_null, .payload => {}, - .ptr, .len => return dg.renderValue( - w, - Value.fromInterned(payload), - location, - ), - else => unreachable, - }, - } + .slice_like => switch (opt.val) { + .none => { + if (!location.isInitializer()) { + try w.writeByte('('); + try dg.renderType(w, ty); + try w.writeByte(')'); + } + try w.writeAll("{NULL,"); + try dg.renderUndefValue(w, .usize, initializer_type); + try w.writeByte('}'); + }, + else => |payload_val| try dg.renderValue(w, .fromInterned(payload_val), location), + }, + .@"struct" => { if (!location.isInitializer()) { try w.writeByte('('); - try dg.renderCType(w, ctype); + try dg.renderType(w, ty); try w.writeByte(')'); } - try w.writeByte('{'); - for (0..aggregate.fields.len) |field_index| { - if (field_index > 0) try w.writeByte(','); - switch (aggregate.fields.at(field_index, ctype_pool).name.index) { - .is_null => try w.writeAll(switch (opt.val) { - .none => "true", - else => "false", - }), - .payload => switch (opt.val) { - .none => try dg.renderUndefValue( - w, - ty.optionalChild(zcu), - initializer_type, - ), - else => |payload| try dg.renderValue( - w, - Value.fromInterned(payload), - initializer_type, - ), - }, - .ptr => try w.writeAll("NULL"), - .len => try dg.renderUndefValue(w, .usize, initializer_type), - else => unreachable, - } + switch (opt.val) { + .none => { + try w.writeAll("{ .is_null = true, .payload = "); + try dg.renderUndefValue(w, ty.optionalChild(zcu), initializer_type); + try w.writeAll(" }"); + }, + else => |payload_val| { + try w.writeAll("{ .is_null = false, .payload = "); + try dg.renderValue(w, .fromInterned(payload_val), initializer_type); + try w.writeAll(" }"); + }, } - try w.writeByte('}'); }, }, .aggregate => switch (ip.indexToKey(ty.toIntern())) { .array_type, .vector_type => { - if (location == .FunctionArgument) { + if (!location.isInitializer()) { try w.writeByte('('); - try dg.renderCType(w, ctype); + try dg.renderType(w, ty); try w.writeByte(')'); } + try w.writeByte('{'); const ai = ty.arrayInfo(zcu); if (ai.elem_type.eql(.u8, zcu)) { var literal: StringLiteral = .init(w, @intCast(ty.arrayLenIncludingSentinel(zcu))); @@ -1327,11 +1155,12 @@ pub const DeclGen = struct { } try w.writeByte('}'); } + try w.writeByte('}'); }, .tuple_type => |tuple| { if (!location.isInitializer()) { try w.writeByte('('); - try dg.renderCType(w, ctype); + try dg.renderType(w, ty); try w.writeByte(')'); } @@ -1341,7 +1170,7 @@ pub const DeclGen = struct { const comptime_val = tuple.values.get(ip)[field_index]; if (comptime_val != .none) continue; const field_ty: Type = .fromInterned(tuple.types.get(ip)[field_index]); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!field_ty.hasRuntimeBits(zcu)) continue; if (!empty) try w.writeByte(','); @@ -1363,139 +1192,95 @@ pub const DeclGen = struct { }, .struct_type => { const loaded_struct = ip.loadStructType(ty.toIntern()); - switch (loaded_struct.layout) { - .auto, .@"extern" => { - if (!location.isInitializer()) { - try w.writeByte('('); - try dg.renderCType(w, ctype); - try w.writeByte(')'); - } + assert(loaded_struct.layout != .@"packed"); - try w.writeByte('{'); - var field_it = loaded_struct.iterateRuntimeOrder(ip); - var need_comma = false; - while (field_it.next()) |field_index| { - const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!location.isInitializer()) { + try w.writeByte('('); + try dg.renderType(w, ty); + try w.writeByte(')'); + } - if (need_comma) try w.writeByte(','); - need_comma = true; - const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) { - .bytes => |bytes| try pt.intern(.{ .int = .{ - .ty = field_ty.toIntern(), - .storage = .{ .u64 = bytes.at(field_index, ip) }, - } }), - .elems => |elems| elems[field_index], - .repeated_elem => |elem| elem, - }; - try dg.renderValue(w, Value.fromInterned(field_val), initializer_type); - } - try w.writeByte('}'); - }, - .@"packed" => { - // https://github.com/ziglang/zig/issues/24657 will eliminate most of the - // following logic, leaving only the recursive `renderValue` call. Once - // that proposal is implemented, a `packed struct` will literally be - // represented in the InternPool by its comptime-known backing integer. - var arena: std.heap.ArenaAllocator = .init(zcu.gpa); - defer arena.deinit(); - const backing_ty: Type = .fromInterned(loaded_struct.backingIntTypeUnordered(ip)); - const buf = try arena.allocator().alloc(u8, @intCast(ty.abiSize(zcu))); - val.writeToMemory(pt, buf) catch |err| switch (err) { - error.IllDefinedMemoryLayout => unreachable, - error.OutOfMemory => |e| return e, - error.ReinterpretDeclRef, error.Unimplemented => return dg.fail("TODO: C backend: lower packed struct value", .{}), - }; - const backing_val: Value = try .readUintFromMemory(backing_ty, pt, buf, arena.allocator()); - return dg.renderValue(w, backing_val, location); - }, + try w.writeByte('{'); + var field_it = loaded_struct.iterateRuntimeOrder(ip); + var need_comma = false; + while (field_it.next()) |field_index| { + const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBits(zcu)) continue; + + if (need_comma) try w.writeByte(','); + need_comma = true; + const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) { + .bytes => |bytes| try pt.intern(.{ .int = .{ + .ty = field_ty.toIntern(), + .storage = .{ .u64 = bytes.at(field_index, ip) }, + } }), + .elems => |elems| elems[field_index], + .repeated_elem => |elem| elem, + }; + try dg.renderValue(w, Value.fromInterned(field_val), initializer_type); } + try w.writeByte('}'); }, else => unreachable, }, + .bitpack => |bitpack| return dg.renderValue(w, .fromInterned(bitpack.backing_int_val), location), .un => |un| { const loaded_union = ip.loadUnionType(ty.toIntern()); - if (loaded_union.flagsUnordered(ip).layout == .@"packed") { - // https://github.com/ziglang/zig/issues/24657 will eliminate most of the - // following logic, leaving only the recursive `renderValue` call. Once - // that proposal is implemented, a `packed union` will literally be - // represented in the InternPool by its comptime-known backing integer. - var arena: std.heap.ArenaAllocator = .init(zcu.gpa); - defer arena.deinit(); - const backing_ty = try ty.unionBackingType(pt); - const buf = try arena.allocator().alloc(u8, @intCast(ty.abiSize(zcu))); - val.writeToMemory(pt, buf) catch |err| switch (err) { - error.IllDefinedMemoryLayout => unreachable, - error.OutOfMemory => |e| return e, - error.ReinterpretDeclRef, error.Unimplemented => return dg.fail("TODO: C backend: lower packed union value", .{}), - }; - const backing_val: Value = try .readUintFromMemory(backing_ty, pt, buf, arena.allocator()); - return dg.renderValue(w, backing_val, location); - } if (un.tag == .none) { - const backing_ty = try ty.unionBackingType(pt); - assert(loaded_union.flagsUnordered(ip).layout == .@"extern"); - if (location == .StaticInitializer) { + assert(loaded_union.layout == .@"extern"); + if (location == .static_initializer) { return dg.fail("TODO: C backend: implement extern union backing type rendering in static initializers", .{}); } const ptr_ty = try pt.singleConstPtrType(ty); - try w.writeAll("*(("); + try w.writeAll("*("); try dg.renderType(w, ptr_ty); - try w.writeAll(")("); - try dg.renderType(w, backing_ty); - try w.writeAll("){"); - try dg.renderValue(w, Value.fromInterned(un.val), location); - try w.writeAll("})"); + try w.writeAll(")&"); + // We need an lvalue for '&'. + try dg.renderValueAsLvalue(w, .fromInterned(un.val)); } else { if (!location.isInitializer()) { try w.writeByte('('); - try dg.renderCType(w, ctype); + try dg.renderType(w, ty); try w.writeByte(')'); } + if (ty.unionHasAllZeroBitFieldTypes(zcu)) { + assert(loaded_union.has_runtime_tag); // otherwise it does not have runtime bits + try w.writeAll("{ .tag = "); + try dg.renderValue(w, .fromInterned(un.tag), initializer_type); + try w.writeAll(" }"); + return; + } - const field_index = zcu.unionTagFieldIndex(loaded_union, Value.fromInterned(un.tag)).?; - const field_ty: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); - const field_name = loaded_union.loadTagType(ip).names.get(ip)[field_index]; - - const has_tag = loaded_union.hasTag(ip); - if (has_tag) try w.writeByte('{'); - const aggregate = ctype.info(ctype_pool).aggregate; - for (0..if (has_tag) aggregate.fields.len else 1) |outer_field_index| { - if (outer_field_index > 0) try w.writeByte(','); - switch (if (has_tag) - aggregate.fields.at(outer_field_index, ctype_pool).name.index - else - .payload) { - .tag => try dg.renderValue( - w, - Value.fromInterned(un.tag), - initializer_type, - ), - .payload => { - try w.writeByte('{'); - if (field_ty.hasRuntimeBits(zcu)) { - try w.print(" .{f} = ", .{fmtIdentSolo(field_name.toSlice(ip))}); - try dg.renderValue( - w, - Value.fromInterned(un.val), - initializer_type, - ); - try w.writeByte(' '); - } else for (0..loaded_union.field_types.len) |inner_field_index| { - const inner_field_ty: Type = .fromInterned( - loaded_union.field_types.get(ip)[inner_field_index], - ); - if (!inner_field_ty.hasRuntimeBits(zcu)) continue; - try dg.renderUndefValue(w, inner_field_ty, initializer_type); - break; - } - try w.writeByte('}'); - }, - else => unreachable, - } + if (loaded_union.layout == .auto) try w.writeByte('{'); + + if (loaded_union.has_runtime_tag) { + try w.writeAll(" .tag = "); + try dg.renderValue(w, .fromInterned(un.tag), initializer_type); + try w.writeAll(", .payload = "); + } + + const enum_tag_ty: Type = .fromInterned(loaded_union.enum_tag_type); + const active_field_index = enum_tag_ty.enumTagFieldIndex(.fromInterned(un.tag), zcu).?; + const active_field_ty: Type = .fromInterned(loaded_union.field_types.get(ip)[active_field_index]); + if (active_field_ty.hasRuntimeBits(zcu)) { + const active_field_name = enum_tag_ty.enumFieldName(active_field_index, zcu); + try w.print("{{ .{f} = ", .{fmtIdentSolo(active_field_name.toSlice(ip))}); + try dg.renderValue(w, .fromInterned(un.val), initializer_type); + try w.writeAll(" }"); + } else { + const first_field_ty: Type = for (loaded_union.field_types.get(ip)) |field_ty_ip| { + const field_ty: Type = .fromInterned(field_ty_ip); + if (!field_ty.hasRuntimeBits(pt.zcu)) continue; + break field_ty; + } else unreachable; + try w.writeByte('{'); + try dg.renderUndefValue(w, first_field_ty, initializer_type); + try w.writeByte('}'); } - if (has_tag) try w.writeByte('}'); + + if (loaded_union.has_runtime_tag) try w.writeByte(' '); + if (loaded_union.layout == .auto) try w.writeByte('}'); } }, } @@ -1511,11 +1296,10 @@ pub const DeclGen = struct { const zcu = pt.zcu; const ip = &zcu.intern_pool; const target = &dg.mod.resolved_target.result; - const ctype_pool = &dg.ctype_pool; const initializer_type: ValueRenderLocation = switch (location) { - .StaticInitializer => .StaticInitializer, - else => .Initializer, + .static_initializer => .static_initializer, + else => .initializer, }; const safety_on = switch (zcu.optimizeMode()) { @@ -1523,7 +1307,6 @@ pub const DeclGen = struct { .ReleaseFast, .ReleaseSmall => false, }; - const ctype = try dg.ctypeFromType(ty, location.toCTypeKind()); switch (ty.toIntern()) { .c_longdouble_type, .f16_type, @@ -1548,76 +1331,109 @@ pub const DeclGen = struct { else => unreachable, } try w.writeAll(", "); - try dg.renderUndefValue(w, repr_ty, .FunctionArgument); + try dg.renderUndefValue(w, repr_ty, .other); return w.writeByte(')'); }, .bool_type => try w.writeAll(if (safety_on) "0xaa" else "false"), else => switch (ip.indexToKey(ty.toIntern())) { - .simple_type, + .simple_type, // anyerror, c_char (etc), usize, isize .int_type, .enum_type, .error_set_type, .inferred_error_set_type, - => return w.print("{f}", .{ - try dg.fmtIntLiteralHex(try pt.undefValue(ty), location), - }), + => switch (CType.classifyInt(ty, zcu)) { + .void => unreachable, // opv + .small => |s| { + const int = ty.intInfo(zcu); + var buf: [std.math.big.int.calcTwosCompLimbCount(128)]std.math.big.Limb = undefined; + var bigint: std.math.big.int.Mutable = .init(&buf, undefPattern(u128)); + bigint.truncate(bigint.toConst(), int.signedness, int.bits); + const fmt_undef: FormatInt128 = .{ + .target = zcu.getTarget(), + .int_cty = s, + .val = bigint.toConst(), + .is_global = location == .static_initializer, + .base = 16, + .case = .lower, + }; + try w.print("{f}", .{fmt_undef}); + }, + .big => |big| { + var buf: [std.math.big.int.calcTwosCompLimbCount(128)]std.math.big.Limb = undefined; + var limb_bigint: std.math.big.int.Mutable = .init(&buf, undefPattern(u128)); + limb_bigint.truncate(limb_bigint.toConst(), .unsigned, big.limb_size.bits()); + const fmt_undef_limb: FormatInt128 = .{ + .target = zcu.getTarget(), + .int_cty = big.limb_size.unsigned(), + .val = limb_bigint.toConst(), + .is_global = location == .static_initializer, + .base = 16, + .case = .lower, + }; + + if (!location.isInitializer()) { + try w.writeByte('('); + try dg.renderType(w, ty); + try w.writeByte(')'); + } + try w.writeAll("{{"); + try w.print("{f}", .{fmt_undef_limb}); + for (1..big.limbs_len) |_| { + try w.print(",{f}", .{fmt_undef_limb}); + } + try w.writeAll("}}"); + }, + }, .ptr_type => |ptr_type| switch (ptr_type.flags.size) { .one, .many, .c => { try w.writeAll("(("); - try dg.renderCType(w, ctype); - return w.print("){f})", .{ - try dg.fmtIntLiteralHex(.undef_usize, .Other), - }); + try dg.renderType(w, ty); + try w.writeByte(')'); + try dg.renderUndefValue(w, .usize, location); + try w.writeByte(')'); }, .slice => { if (!location.isInitializer()) { try w.writeByte('('); - try dg.renderCType(w, ctype); + try dg.renderType(w, ty); try w.writeByte(')'); } - try w.writeAll("{("); - const ptr_ty = ty.slicePtrFieldType(zcu); - try dg.renderType(w, ptr_ty); - return w.print("){f}, {0f}}}", .{ - try dg.fmtIntLiteralHex(.undef_usize, .Other), - }); + try w.writeByte('{'); + try dg.renderUndefValue(w, ty.slicePtrFieldType(zcu), initializer_type); + try w.writeByte(','); + try dg.renderUndefValue(w, .usize, initializer_type); + try w.writeByte('}'); }, }, - .opt_type => |child_type| switch (ctype.info(ctype_pool)) { - .basic, .pointer => try dg.renderUndefValue( - w, - .fromInterned(if (ctype.isBool()) .bool_type else child_type), - location, - ), - .aligned, .array, .vector, .fwd_decl, .function => unreachable, - .aggregate => |aggregate| { - switch (aggregate.fields.at(0, ctype_pool).name.index) { - .is_null, .payload => {}, - .ptr, .len => return dg.renderUndefValue( - w, - .fromInterned(child_type), - location, - ), - else => unreachable, - } + .opt_type => |child_type| switch (CType.classifyOptional(ty, zcu)) { + .npv_payload => unreachable, // opv optional + + .error_set, + .ptr_like, + .slice_like, + => try dg.renderUndefValue(w, .fromInterned(child_type), location), + + .opv_payload => { if (!location.isInitializer()) { try w.writeByte('('); - try dg.renderCType(w, ctype); + try dg.renderType(w, ty); try w.writeByte(')'); } - try w.writeByte('{'); - for (0..aggregate.fields.len) |field_index| { - if (field_index > 0) try w.writeByte(','); - try dg.renderUndefValue(w, .fromInterned( - switch (aggregate.fields.at(field_index, ctype_pool).name.index) { - .is_null => .bool_type, - .payload => child_type, - else => unreachable, - }, - ), initializer_type); + try w.writeAll(if (safety_on) "{.is_null=0xaa}" else "{.is_null=false}"); + }, + + .@"struct" => { + if (!location.isInitializer()) { + try w.writeByte('('); + try dg.renderType(w, ty); + try w.writeByte(')'); } - try w.writeByte('}'); + try w.writeAll("{ .is_null = "); + try dg.renderUndefValue(w, .bool, initializer_type); + try w.writeAll(", .payload = "); + try dg.renderUndefValue(w, .fromInterned(child_type), initializer_type); + try w.writeAll(" }"); }, }, .struct_type => { @@ -1626,16 +1442,15 @@ pub const DeclGen = struct { .auto, .@"extern" => { if (!location.isInitializer()) { try w.writeByte('('); - try dg.renderCType(w, ctype); + try dg.renderType(w, ty); try w.writeByte(')'); } - try w.writeByte('{'); var field_it = loaded_struct.iterateRuntimeOrder(ip); var need_comma = false; while (field_it.next()) |field_index| { const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!field_ty.hasRuntimeBits(zcu)) continue; if (need_comma) try w.writeByte(','); need_comma = true; @@ -1643,17 +1458,13 @@ pub const DeclGen = struct { } return w.writeByte('}'); }, - .@"packed" => return dg.renderUndefValue( - w, - .fromInterned(loaded_struct.backingIntTypeUnordered(ip)), - location, - ), + .@"packed" => return dg.renderUndefValue(w, ty.bitpackBackingInt(zcu), location), } }, .tuple_type => |tuple_info| { if (!location.isInitializer()) { try w.writeByte('('); - try dg.renderCType(w, ctype); + try dg.renderType(w, ty); try w.writeByte(')'); } @@ -1662,7 +1473,7 @@ pub const DeclGen = struct { for (0..tuple_info.types.len) |field_index| { if (tuple_info.values.get(ip)[field_index] != .none) continue; const field_ty: Type = .fromInterned(tuple_info.types.get(ip)[field_index]); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!field_ty.hasRuntimeBits(zcu)) continue; if (need_comma) try w.writeByte(','); need_comma = true; @@ -1672,88 +1483,65 @@ pub const DeclGen = struct { }, .union_type => { const loaded_union = ip.loadUnionType(ty.toIntern()); - switch (loaded_union.flagsUnordered(ip).layout) { + switch (loaded_union.layout) { .auto, .@"extern" => { if (!location.isInitializer()) { try w.writeByte('('); - try dg.renderCType(w, ctype); + try dg.renderType(w, ty); try w.writeByte(')'); } - const has_tag = loaded_union.hasTag(ip); - if (has_tag) try w.writeByte('{'); - const aggregate = ctype.info(ctype_pool).aggregate; - for (0..if (has_tag) aggregate.fields.len else 1) |outer_field_index| { - if (outer_field_index > 0) try w.writeByte(','); - switch (if (has_tag) - aggregate.fields.at(outer_field_index, ctype_pool).name.index - else - .payload) { - .tag => try dg.renderUndefValue( - w, - .fromInterned(loaded_union.enum_tag_ty), - initializer_type, - ), - .payload => { - try w.writeByte('{'); - for (0..loaded_union.field_types.len) |inner_field_index| { - const inner_field_ty: Type = .fromInterned( - loaded_union.field_types.get(ip)[inner_field_index], - ); - if (!inner_field_ty.hasRuntimeBits(pt.zcu)) continue; - try dg.renderUndefValue( - w, - inner_field_ty, - initializer_type, - ); - break; - } - try w.writeByte('}'); - }, - else => unreachable, - } + const first_field_ty: Type = for (loaded_union.field_types.get(ip)) |field_ty_ip| { + const field_ty: Type = .fromInterned(field_ty_ip); + if (!field_ty.hasRuntimeBits(pt.zcu)) continue; + break field_ty; + } else { + assert(loaded_union.has_runtime_tag); // otherwise it does not have runtime bits + try w.writeAll("{ .tag = "); + try dg.renderUndefValue(w, .fromInterned(loaded_union.enum_tag_type), initializer_type); + try w.writeAll(" }"); + return; + }; + + if (loaded_union.layout == .auto) try w.writeByte('{'); + + if (loaded_union.has_runtime_tag) { + try w.writeAll(" .tag = "); + try dg.renderUndefValue(w, .fromInterned(loaded_union.enum_tag_type), initializer_type); + try w.writeAll(", .payload = "); } - if (has_tag) try w.writeByte('}'); + + try w.writeByte('{'); + try dg.renderUndefValue(w, first_field_ty, initializer_type); + try w.writeByte('}'); + + if (loaded_union.has_runtime_tag) try w.writeByte(' '); + if (loaded_union.layout == .auto) try w.writeByte('}'); }, - .@"packed" => return dg.renderUndefValue( - w, - try ty.unionBackingType(pt), - location, - ), + .@"packed" => return dg.renderUndefValue(w, ty.bitpackBackingInt(zcu), location), } }, - .error_union_type => |error_union_type| switch (ctype.info(ctype_pool)) { - .basic => try dg.renderUndefValue( - w, - .fromInterned(error_union_type.error_set_type), - location, - ), - .pointer, .aligned, .array, .vector, .fwd_decl, .function => unreachable, - .aggregate => |aggregate| { - if (!location.isInitializer()) { - try w.writeByte('('); - try dg.renderCType(w, ctype); - try w.writeByte(')'); - } - try w.writeByte('{'); - for (0..aggregate.fields.len) |field_index| { - if (field_index > 0) try w.writeByte(','); - try dg.renderUndefValue( - w, - .fromInterned( - switch (aggregate.fields.at(field_index, ctype_pool).name.index) { - .@"error" => error_union_type.error_set_type, - .payload => error_union_type.payload_type, - else => unreachable, - }, - ), - initializer_type, - ); - } - try w.writeByte('}'); - }, + .error_union_type => |error_union| { + if (!location.isInitializer()) { + try w.writeByte('('); + try dg.renderType(w, ty); + try w.writeByte(')'); + } + try w.writeAll("{ .error = "); + try dg.renderUndefValue(w, .fromInterned(error_union.error_set_type), initializer_type); + if (Type.fromInterned(error_union.payload_type).hasRuntimeBits(zcu)) { + try w.writeAll(", .payload = "); + try dg.renderUndefValue(w, .fromInterned(error_union.payload_type), initializer_type); + } + try w.writeAll(" }"); }, .array_type, .vector_type => { + if (!location.isInitializer()) { + try w.writeByte('('); + try dg.renderType(w, ty); + try w.writeByte(')'); + } + try w.writeByte('{'); const ai = ty.arrayInfo(zcu); if (ai.elem_type.eql(.u8, zcu)) { var literal: StringLiteral = .init(w, @intCast(ty.arrayLenIncludingSentinel(zcu))); @@ -1764,14 +1552,8 @@ pub const DeclGen = struct { const s_u8: u8 = @intCast(s.toUnsignedInt(zcu)); if (s_u8 != 0) try literal.writeChar(s_u8); } - return literal.end(); + try literal.end(); } else { - if (!location.isInitializer()) { - try w.writeByte('('); - try dg.renderCType(w, ctype); - try w.writeByte(')'); - } - try w.writeByte('{'); var index: u64 = 0; while (index < ai.len) : (index += 1) { @@ -1782,8 +1564,9 @@ pub const DeclGen = struct { if (index > 0) try w.writeAll(", "); try dg.renderValue(w, s, location); } - return w.writeByte('}'); + try w.writeByte('}'); } + try w.writeByte('}'); }, .anyframe_type, .opaque_type, @@ -1800,13 +1583,13 @@ pub const DeclGen = struct { .error_union, .enum_literal, .enum_tag, - .empty_enum_value, .float, .ptr, .slice, .opt, .aggregate, .un, + .bitpack, .memoized_call, => unreachable, // values, not types }, @@ -1818,10 +1601,11 @@ pub const DeclGen = struct { w: *Writer, fn_val: Value, fn_align: InternPool.Alignment, - kind: CType.Kind, + kind: enum { forward_decl, definition }, name: union(enum) { nav: InternPool.Nav.Index, - fmt_ctype_pool_string: std.fmt.Alt(CTypePoolStringFormatData, formatCTypePoolString), + nav_never_tail: InternPool.Nav.Index, + nav_never_inline: InternPool.Nav.Index, @"export": struct { main_name: InternPool.NullTerminatedString, extern_name: InternPool.NullTerminatedString, @@ -1832,14 +1616,12 @@ pub const DeclGen = struct { const ip = &zcu.intern_pool; const fn_ty = fn_val.typeOf(zcu); - const fn_ctype = try dg.ctypeFromType(fn_ty, kind); const fn_info = zcu.typeToFunc(fn_ty).?; if (fn_info.cc == .naked) { switch (kind) { - .forward => try w.writeAll("zig_naked_decl "), - .complete => try w.writeAll("zig_naked "), - else => unreachable, + .forward_decl => try w.writeAll("zig_naked_decl "), + .definition => try w.writeAll("zig_naked "), } } @@ -1849,45 +1631,63 @@ pub const DeclGen = struct { if (func_analysis.branch_hint == .cold) try w.writeAll("zig_cold "); - if (kind == .complete and func_analysis.disable_intrinsics or dg.mod.no_builtin) + if (kind == .definition and func_analysis.disable_intrinsics or dg.mod.no_builtin) try w.writeAll("zig_no_builtin "); } if (fn_info.return_type == .noreturn_type) try w.writeAll("zig_noreturn "); - var trailing = try renderTypePrefix(dg.pass, &dg.ctype_pool, zcu, w, fn_ctype, .suffix, .{}); + // While incomplete types are usually an acceptable substitute for "void", this is not true + // in function return types, where "void" is the only incomplete type permitted. + const actual_return_type: Type = .fromInterned(fn_info.return_type); + const effective_return_type: Type = switch (actual_return_type.classify(zcu)) { + .no_possible_value => .noreturn, + .one_possible_value, .fully_comptime => .void, // no runtime bits + .partially_comptime, .runtime => actual_return_type, // yes runtime bits + }; + const ret_cty: CType = try .lower(effective_return_type, &dg.ctype_deps, dg.arena, zcu); + try w.print("{f}", .{ret_cty.fmtDeclaratorPrefix(zcu)}); if (toCallingConvention(fn_info.cc, zcu)) |call_conv| { - try w.print("{f}zig_callconv({s})", .{ trailing, call_conv }); - trailing = .maybe_space; + try w.print("zig_callconv({s}) ", .{call_conv}); } - - try w.print("{f}", .{trailing}); switch (name) { - .nav => |nav| try dg.renderNavName(w, nav), - .fmt_ctype_pool_string => |fmt| try w.print("{f}", .{fmt}), + .nav => |nav| try renderNavName(w, nav, ip), + .nav_never_tail => |nav| try w.print("zig_never_tail_{f}__{d}", .{ + fmtIdentUnsolo(ip.getNav(nav).name.toSlice(ip)), @intFromEnum(nav), + }), + .nav_never_inline => |nav| try w.print("zig_never_inline_{f}__{d}", .{ + fmtIdentUnsolo(ip.getNav(nav).name.toSlice(ip)), @intFromEnum(nav), + }), .@"export" => |@"export"| try w.print("{f}", .{fmtIdentSolo(@"export".extern_name.toSlice(ip))}), } - - try renderTypeSuffix( - dg.pass, - &dg.ctype_pool, - zcu, - w, - fn_ctype, - .suffix, - CQualifiers.init(.{ .@"const" = switch (kind) { - .forward => false, - .complete => true, - else => unreachable, - } }), - ); + { + try w.writeByte('('); + var c_param_index: u32 = 0; + for (fn_info.param_types.get(ip)) |param_ty_ip| { + const param_ty: Type = .fromInterned(param_ty_ip); + if (!param_ty.hasRuntimeBits(zcu)) continue; + if (c_param_index != 0) try w.writeAll(", "); + try dg.renderTypeAndName(w, param_ty, .{ .arg = c_param_index }, .{ + .@"const" = kind == .definition, + }, .none); + c_param_index += 1; + } + if (fn_info.is_var_args) { + if (c_param_index != 0) try w.writeAll(", "); + try w.writeAll("..."); + } else if (c_param_index == 0) { + try w.writeAll("void"); + } + try w.writeByte(')'); + } + try w.print("{f}", .{ret_cty.fmtDeclaratorSuffixIgnoreNonstring(zcu)}); switch (kind) { - .forward => { + .forward_decl => { if (fn_align.toByteUnits()) |a| try w.print(" zig_align_fn({})", .{a}); switch (name) { - .nav, .fmt_ctype_pool_string => {}, + .nav, .nav_never_tail, .nav_never_inline => {}, .@"export" => |@"export"| { const extern_name = @"export".extern_name.toSlice(ip); const is_mangled = isMangledIdent(extern_name, true); @@ -1911,38 +1711,16 @@ pub const DeclGen = struct { }, } }, - .complete => {}, - else => unreachable, + .definition => {}, } } - fn ctypeFromType(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType { - defer std.debug.assert(dg.scratch.items.len == 0); - return dg.ctype_pool.fromType(dg.gpa, &dg.scratch, ty, dg.pt, dg.mod, kind); - } - - fn byteSize(dg: *DeclGen, ctype: CType) u64 { - return ctype.byteSize(&dg.ctype_pool, dg.mod); - } - - /// Renders a type as a single identifier, generating intermediate typedefs - /// if necessary. - /// - /// This is guaranteed to be valid in both typedefs and declarations/definitions. - /// - /// There are three type formats in total that we support rendering: - /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) | - /// |---------------------|-----------------|---------------------| - /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | - /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | - /// - fn renderType(dg: *DeclGen, w: *Writer, t: Type) Error!void { - try dg.renderCType(w, try dg.ctypeFromType(t, .complete)); - } - - fn renderCType(dg: *DeclGen, w: *Writer, ctype: CType) Error!void { - _ = try renderTypePrefix(dg.pass, &dg.ctype_pool, dg.pt.zcu, w, ctype, .suffix, .{}); - try renderTypeSuffix(dg.pass, &dg.ctype_pool, dg.pt.zcu, w, ctype, .suffix, .{}); + /// Renders the C lowering of the given Zig type to `w`. This renders the type name---to render + /// a declarator with this type, see instead `renderTypeAndName`. + fn renderType(dg: *DeclGen, w: *Writer, ty: Type) (Writer.Error || Allocator.Error)!void { + const zcu = dg.pt.zcu; + const cty: CType = try .lower(ty, &dg.ctype_deps, dg.arena, zcu); + try w.print("{f}", .{cty.fmtTypeName(zcu)}); } const IntCastContext = union(enum) { @@ -2046,7 +1824,7 @@ pub const DeclGen = struct { try w.writeAll("zig_lo_"); try dg.renderTypeForBuiltinFnName(w, src_eff_ty); try w.writeByte('('); - try context.writeValue(dg, w, .FunctionArgument); + try context.writeValue(dg, w, .other); try w.writeByte(')'); } else if (dest_bits > 64 and src_bits <= 64) { try w.writeAll("zig_make_"); @@ -2057,7 +1835,7 @@ pub const DeclGen = struct { try dg.renderType(w, src_eff_ty); try w.writeByte(')'); } - try context.writeValue(dg, w, .FunctionArgument); + try context.writeValue(dg, w, .other); try w.writeByte(')'); } else { assert(!src_is_ptr); @@ -2066,23 +1844,16 @@ pub const DeclGen = struct { try w.writeAll("(zig_hi_"); try dg.renderTypeForBuiltinFnName(w, src_eff_ty); try w.writeByte('('); - try context.writeValue(dg, w, .FunctionArgument); + try context.writeValue(dg, w, .other); try w.writeAll("), zig_lo_"); try dg.renderTypeForBuiltinFnName(w, src_eff_ty); try w.writeByte('('); - try context.writeValue(dg, w, .FunctionArgument); + try context.writeValue(dg, w, .other); try w.writeAll("))"); } } - /// Renders a type and name in field declaration/definition format. - /// - /// There are three type formats in total that we support rendering: - /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) | - /// |---------------------|-----------------|---------------------| - /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | - /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | - /// + /// Renders to `w` a C declarator whose type is the C lowering of the given Zig type. fn renderTypeAndName( dg: *DeclGen, w: *Writer, @@ -2090,73 +1861,47 @@ pub const DeclGen = struct { name: CValue, qualifiers: CQualifiers, alignment: Alignment, - kind: CType.Kind, - ) !void { - try dg.renderCTypeAndName( - w, - try dg.ctypeFromType(ty, kind), - name, - qualifiers, - CType.AlignAs.fromAlignment(.{ - .@"align" = alignment, - .abi = ty.abiAlignment(dg.pt.zcu), - }), - ); - } - - fn renderCTypeAndName( - dg: *DeclGen, - w: *Writer, - ctype: CType, - name: CValue, - qualifiers: CQualifiers, - alignas: CType.AlignAs, ) !void { const zcu = dg.pt.zcu; - switch (alignas.abiOrder()) { - .lt => try w.print("zig_under_align({}) ", .{alignas.toByteUnits()}), + const ip = &zcu.intern_pool; + const cty: CType = try .lower(ty, &dg.ctype_deps, dg.arena, zcu); + try w.print("{f}", .{cty.fmtDeclaratorPrefix(zcu)}); + if (alignment != .none) switch (alignment.order(ty.abiAlignment(zcu))) { + .lt => try w.print("zig_under_align({d}) ", .{alignment.toByteUnits().?}), .eq => {}, - .gt => try w.print("zig_align({}) ", .{alignas.toByteUnits()}), - } - - try w.print("{f}", .{ - try renderTypePrefix(dg.pass, &dg.ctype_pool, zcu, w, ctype, .suffix, qualifiers), - }); - try dg.writeName(w, name); - try renderTypeSuffix(dg.pass, &dg.ctype_pool, zcu, w, ctype, .suffix, .{}); - if (ctype.isNonString(&dg.ctype_pool)) try w.writeAll(" zig_nonstring"); - } - - fn writeName(dg: *DeclGen, w: *Writer, c_value: CValue) !void { - switch (c_value) { + .gt => try w.print("zig_align({d}) ", .{alignment.toByteUnits().?}), + }; + if (qualifiers.@"const") try w.writeAll("const "); + if (qualifiers.@"volatile") try w.writeAll("volatile "); + if (qualifiers.restrict) try w.writeAll("restrict "); + switch (name) { .new_local, .local => |i| try w.print("t{d}", .{i}), + .arg => |i| try w.print("a{d}", .{i}), .constant => |uav| try renderUavName(w, uav), - .nav => |nav| try dg.renderNavName(w, nav), + .nav => |nav| try renderNavName(w, nav, ip), .identifier => |ident| try w.print("{f}", .{fmtIdentSolo(ident)}), else => unreachable, } + try w.print("{f}", .{cty.fmtDeclaratorSuffix(zcu)}); } fn writeCValue(dg: *DeclGen, w: *Writer, c_value: CValue) Error!void { switch (c_value) { .none, .new_local, .local, .local_ref => unreachable, .constant => |uav| try renderUavName(w, uav), - .arg, .arg_array => unreachable, + .arg => unreachable, .field => |i| try w.print("f{d}", .{i}), - .nav => |nav| try dg.renderNavName(w, nav), + .nav => |nav| try renderNavName(w, nav, &dg.pt.zcu.intern_pool), .nav_ref => |nav| { try w.writeByte('&'); - try dg.renderNavName(w, nav); + try renderNavName(w, nav, &dg.pt.zcu.intern_pool); }, - .undef => |ty| try dg.renderUndefValue(w, ty, .Other), + .undef => |ty| try dg.renderUndefValue(w, ty, .other), .identifier => |ident| try w.print("{f}", .{fmtIdentSolo(ident)}), .payload_identifier => |ident| try w.print("{f}.{f}", .{ fmtIdentSolo("payload"), fmtIdentSolo(ident), }), - .ctype_pool_string => |string| try w.print("{f}", .{ - fmtCTypePoolString(string, &dg.ctype_pool, true), - }), } } @@ -2168,16 +1913,14 @@ pub const DeclGen = struct { .local_ref, .constant, .arg, - .arg_array, - .ctype_pool_string, => unreachable, .field => |i| try w.print("f{d}", .{i}), .nav => |nav| { try w.writeAll("(*"); - try dg.renderNavName(w, nav); + try renderNavName(w, nav, &dg.pt.zcu.intern_pool); try w.writeByte(')'); }, - .nav_ref => |nav| try dg.renderNavName(w, nav), + .nav_ref => |nav| try renderNavName(w, nav, &dg.pt.zcu.intern_pool), .undef => unreachable, .identifier => |ident| try w.print("(*{f})", .{fmtIdentSolo(ident)}), .payload_identifier => |ident| try w.print("(*{f}.{f})", .{ @@ -2213,8 +1956,6 @@ pub const DeclGen = struct { .field, .undef, .arg, - .arg_array, - .ctype_pool_string, => unreachable, .nav, .identifier, .payload_identifier => { try dg.writeCValue(w, c_value); @@ -2228,101 +1969,36 @@ pub const DeclGen = struct { try dg.writeCValue(w, member); } - fn renderFwdDecl( - dg: *DeclGen, - nav_index: InternPool.Nav.Index, - flags: packed struct { - is_const: bool, - is_threadlocal: bool, - linkage: std.builtin.GlobalLinkage, - visibility: std.builtin.SymbolVisibility, - }, - ) !void { + fn renderTypeForBuiltinFnName(dg: *DeclGen, w: *Writer, ty: Type) !void { const zcu = dg.pt.zcu; - const ip = &zcu.intern_pool; - const nav = ip.getNav(nav_index); - const fwd = &dg.fwd_decl.writer; - try fwd.writeAll(switch (flags.linkage) { - .internal => "static ", - .strong, .weak, .link_once => "zig_extern ", - }); - switch (flags.linkage) { - .internal, .strong => {}, - .weak => try fwd.writeAll("zig_weak_linkage "), - .link_once => return dg.fail("TODO: CBE: implement linkonce linkage?", .{}), - } - switch (flags.linkage) { - .internal => {}, - .strong, .weak, .link_once => try fwd.print("zig_visibility({s}) ", .{@tagName(flags.visibility)}), + switch (ty.zigTypeTag(zcu)) { + .bool => return w.writeAll("u8"), + .float => return w.print("f{d}", .{ty.floatBits(zcu.getTarget())}), + else => {}, } - if (flags.is_threadlocal and !dg.mod.single_threaded) try fwd.writeAll("zig_threadlocal "); - try dg.renderTypeAndName( - fwd, - .fromInterned(nav.typeOf(ip)), - .{ .nav = nav_index }, - CQualifiers.init(.{ .@"const" = flags.is_const }), - nav.getAlignment(), - .complete, - ); - try fwd.writeAll(";\n"); - } - - fn renderNavName(dg: *DeclGen, w: *Writer, nav_index: InternPool.Nav.Index) !void { - const zcu = dg.pt.zcu; - const ip = &zcu.intern_pool; - const nav = ip.getNav(nav_index); - if (nav.getExtern(ip)) |@"extern"| { - try w.print("{f}", .{ - fmtIdentSolo(ip.getNav(@"extern".owner_nav).name.toSlice(ip)), - }); - } else { - // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case), - // expand to 3x the length of its input, but let's cut it off at a much shorter limit. - const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip); - try w.print("{f}__{d}", .{ - fmtIdentUnsolo(fqn_slice[0..@min(fqn_slice.len, 100)]), - @intFromEnum(nav_index), - }); + if (ty.isPtrAtRuntime(zcu)) { + return w.print("p{d}", .{zcu.getTarget().ptrBitWidth()}); } - } - - fn renderUavName(w: *Writer, uav: Value) !void { - try w.print("__anon_{d}", .{@intFromEnum(uav.toIntern())}); - } - - fn renderTypeForBuiltinFnName(dg: *DeclGen, w: *Writer, ty: Type) !void { - try dg.renderCTypeForBuiltinFnName(w, try dg.ctypeFromType(ty, .complete)); - } - - fn renderCTypeForBuiltinFnName(dg: *DeclGen, w: *Writer, ctype: CType) !void { - switch (ctype.info(&dg.ctype_pool)) { - else => |ctype_info| try w.print("{c}{d}", .{ - if (ctype.isBool()) - signAbbrev(.unsigned) - else if (ctype.isInteger()) - signAbbrev(ctype.signedness(dg.mod)) - else if (ctype.isFloat()) - @as(u8, 'f') - else if (ctype_info == .pointer) - @as(u8, 'p') - else - return dg.fail("TODO: CBE: implement renderTypeForBuiltinFnName for {s} type", .{@tagName(ctype_info)}), - if (ctype.isFloat()) ctype.floatActiveBits(dg.mod) else dg.byteSize(ctype) * 8, + switch (CType.classifyInt(ty, zcu)) { + .void => unreachable, // opv + .small => try w.print("{c}{d}", .{ + signAbbrev(ty.intInfo(zcu).signedness), + ty.abiSize(zcu) * 8, }), - .array => try w.writeAll("big"), + .big => try w.writeAll("big"), } } fn renderBuiltinInfo(dg: *DeclGen, w: *Writer, ty: Type, info: BuiltinInfo) !void { - const ctype = try dg.ctypeFromType(ty, .complete); - const is_big = ctype.info(&dg.ctype_pool) == .array; + const pt = dg.pt; + const zcu = pt.zcu; + + const is_big = lowersToBigInt(ty, zcu); switch (info) { .none => if (!is_big) return, .bits => {}, } - const pt = dg.pt; - const zcu = pt.zcu; const int_info: std.builtin.Type.Int = if (ty.isAbiInt(zcu)) ty.intInfo(zcu) else .{ .signedness = .unsigned, .bits = @intCast(ty.bitSize(zcu)), @@ -2331,7 +2007,7 @@ pub const DeclGen = struct { if (is_big) try w.print(", {}", .{int_info.signedness == .signed}); try w.print(", {f}", .{try dg.fmtIntLiteralDec( try pt.intValue(if (is_big) .u16 else .u8, int_info.bits), - .FunctionArgument, + .other, )}); } @@ -2342,15 +2018,13 @@ pub const DeclGen = struct { base: u8, case: std.fmt.Case, ) !std.fmt.Alt(FormatIntLiteralContext, formatIntLiteral) { - const zcu = dg.pt.zcu; - const kind = loc.toCTypeKind(); - const ty = val.typeOf(zcu); + // If there's a bigint type involved, mark a dependency on it. + const cty: CType = try .lower(val.typeOf(dg.pt.zcu), &dg.ctype_deps, dg.arena, dg.pt.zcu); return .{ .data = .{ .dg = dg, - .int_info = ty.intInfo(zcu), - .kind = kind, - .ctype = try dg.ctypeFromType(ty, kind), + .loc = loc, .val = val, + .cty = cty, .base = base, .case = case, } }; @@ -2373,339 +2047,11 @@ pub const DeclGen = struct { } }; -const CTypeFix = enum { prefix, suffix }; -const CQualifiers = std.enums.EnumSet(enum { @"const", @"volatile", restrict }); -const Const = CQualifiers.init(.{ .@"const" = true }); -const RenderCTypeTrailing = enum { - no_space, - maybe_space, - - pub fn format(self: @This(), w: *Writer) Writer.Error!void { - switch (self) { - .no_space => {}, - .maybe_space => try w.writeByte(' '), - } - } +const CQualifiers = packed struct { + @"const": bool = false, + @"volatile": bool = false, + restrict: bool = false, }; -fn renderAlignedTypeName(w: *Writer, ctype: CType) !void { - try w.print("anon__aligned_{d}", .{@intFromEnum(ctype.index)}); -} -fn renderFwdDeclTypeName( - zcu: *Zcu, - w: *Writer, - ctype: CType, - fwd_decl: CType.Info.FwdDecl, - attributes: []const u8, -) !void { - const ip = &zcu.intern_pool; - try w.print("{s} {s}", .{ @tagName(fwd_decl.tag), attributes }); - switch (fwd_decl.name) { - .anon => try w.print("anon__lazy_{d}", .{@intFromEnum(ctype.index)}), - .index => |index| try w.print("{f}__{d}", .{ - fmtIdentUnsolo(Type.fromInterned(index).containerTypeName(ip).toSlice(&zcu.intern_pool)), - @intFromEnum(index), - }), - } -} -fn renderTypePrefix( - pass: DeclGen.Pass, - ctype_pool: *const CType.Pool, - zcu: *Zcu, - w: *Writer, - ctype: CType, - parent_fix: CTypeFix, - qualifiers: CQualifiers, -) Writer.Error!RenderCTypeTrailing { - var trailing = RenderCTypeTrailing.maybe_space; - switch (ctype.info(ctype_pool)) { - .basic => |basic_info| try w.writeAll(@tagName(basic_info)), - - .pointer => |pointer_info| { - try w.print("{f}*", .{try renderTypePrefix( - pass, - ctype_pool, - zcu, - w, - pointer_info.elem_ctype, - .prefix, - CQualifiers.init(.{ - .@"const" = pointer_info.@"const", - .@"volatile" = pointer_info.@"volatile", - }), - )}); - trailing = .no_space; - }, - - .aligned => switch (pass) { - .nav => |nav| try w.print("nav__{d}_{d}", .{ - @intFromEnum(nav), @intFromEnum(ctype.index), - }), - .uav => |uav| try w.print("uav__{d}_{d}", .{ - @intFromEnum(uav), @intFromEnum(ctype.index), - }), - .flush => try renderAlignedTypeName(w, ctype), - }, - - .array, .vector => |sequence_info| { - const child_trailing = try renderTypePrefix( - pass, - ctype_pool, - zcu, - w, - sequence_info.elem_ctype, - .suffix, - qualifiers, - ); - switch (parent_fix) { - .prefix => { - try w.print("{f}(", .{child_trailing}); - return .no_space; - }, - .suffix => return child_trailing, - } - }, - - .fwd_decl => |fwd_decl_info| switch (fwd_decl_info.name) { - .anon => switch (pass) { - .nav => |nav| try w.print("nav__{d}_{d}", .{ - @intFromEnum(nav), @intFromEnum(ctype.index), - }), - .uav => |uav| try w.print("uav__{d}_{d}", .{ - @intFromEnum(uav), @intFromEnum(ctype.index), - }), - .flush => try renderFwdDeclTypeName(zcu, w, ctype, fwd_decl_info, ""), - }, - .index => try renderFwdDeclTypeName(zcu, w, ctype, fwd_decl_info, ""), - }, - - .aggregate => |aggregate_info| switch (aggregate_info.name) { - .anon => { - try w.print("{s} {s}", .{ - @tagName(aggregate_info.tag), - if (aggregate_info.@"packed") "zig_packed(" else "", - }); - try renderFields(zcu, w, ctype_pool, aggregate_info, 1); - if (aggregate_info.@"packed") try w.writeByte(')'); - }, - .fwd_decl => |fwd_decl| return renderTypePrefix( - pass, - ctype_pool, - zcu, - w, - fwd_decl, - parent_fix, - qualifiers, - ), - }, - - .function => |function_info| { - const child_trailing = try renderTypePrefix( - pass, - ctype_pool, - zcu, - w, - function_info.return_ctype, - .suffix, - .{}, - ); - switch (parent_fix) { - .prefix => { - try w.print("{f}(", .{child_trailing}); - return .no_space; - }, - .suffix => return child_trailing, - } - }, - } - var qualifier_it = qualifiers.iterator(); - while (qualifier_it.next()) |qualifier| { - try w.print("{f}{s}", .{ trailing, @tagName(qualifier) }); - trailing = .maybe_space; - } - return trailing; -} -fn renderTypeSuffix( - pass: DeclGen.Pass, - ctype_pool: *const CType.Pool, - zcu: *Zcu, - w: *Writer, - ctype: CType, - parent_fix: CTypeFix, - qualifiers: CQualifiers, -) Writer.Error!void { - switch (ctype.info(ctype_pool)) { - .basic, .aligned, .fwd_decl, .aggregate => {}, - .pointer => |pointer_info| try renderTypeSuffix( - pass, - ctype_pool, - zcu, - w, - pointer_info.elem_ctype, - .prefix, - .{}, - ), - .array, .vector => |sequence_info| { - switch (parent_fix) { - .prefix => try w.writeByte(')'), - .suffix => {}, - } - - try w.print("[{}]", .{sequence_info.len}); - try renderTypeSuffix(pass, ctype_pool, zcu, w, sequence_info.elem_ctype, .suffix, .{}); - }, - .function => |function_info| { - switch (parent_fix) { - .prefix => try w.writeByte(')'), - .suffix => {}, - } - - try w.writeByte('('); - var need_comma = false; - for (0..function_info.param_ctypes.len) |param_index| { - const param_type = function_info.param_ctypes.at(param_index, ctype_pool); - if (need_comma) try w.writeAll(", "); - need_comma = true; - const trailing = - try renderTypePrefix(pass, ctype_pool, zcu, w, param_type, .suffix, qualifiers); - if (qualifiers.contains(.@"const")) try w.print("{f}a{d}", .{ trailing, param_index }); - try renderTypeSuffix(pass, ctype_pool, zcu, w, param_type, .suffix, .{}); - } - if (function_info.varargs) { - if (need_comma) try w.writeAll(", "); - need_comma = true; - try w.writeAll("..."); - } - if (!need_comma) try w.writeAll("void"); - try w.writeByte(')'); - - try renderTypeSuffix(pass, ctype_pool, zcu, w, function_info.return_ctype, .suffix, .{}); - }, - } -} -fn renderFields( - zcu: *Zcu, - w: *Writer, - ctype_pool: *const CType.Pool, - aggregate_info: CType.Info.Aggregate, - indent: usize, -) !void { - try w.writeAll("{\n"); - for (0..aggregate_info.fields.len) |field_index| { - const field_info = aggregate_info.fields.at(field_index, ctype_pool); - try w.splatByteAll(' ', indent + 1); - switch (field_info.alignas.abiOrder()) { - .lt => { - std.debug.assert(aggregate_info.@"packed"); - if (field_info.alignas.@"align" != .@"1") try w.print("zig_under_align({}) ", .{ - field_info.alignas.toByteUnits(), - }); - }, - .eq => if (aggregate_info.@"packed" and field_info.alignas.@"align" != .@"1") - try w.print("zig_align({}) ", .{field_info.alignas.toByteUnits()}), - .gt => { - std.debug.assert(field_info.alignas.@"align" != .@"1"); - try w.print("zig_align({}) ", .{field_info.alignas.toByteUnits()}); - }, - } - const trailing = try renderTypePrefix( - .flush, - ctype_pool, - zcu, - w, - field_info.ctype, - .suffix, - .{}, - ); - try w.print("{f}{f}", .{ trailing, fmtCTypePoolString(field_info.name, ctype_pool, true) }); - try renderTypeSuffix(.flush, ctype_pool, zcu, w, field_info.ctype, .suffix, .{}); - if (field_info.ctype.isNonString(ctype_pool)) try w.writeAll(" zig_nonstring"); - try w.writeAll(";\n"); - } - try w.splatByteAll(' ', indent); - try w.writeByte('}'); -} - -pub fn genTypeDecl( - zcu: *Zcu, - w: *Writer, - global_ctype_pool: *const CType.Pool, - global_ctype: CType, - pass: DeclGen.Pass, - decl_ctype_pool: *const CType.Pool, - decl_ctype: CType, - found_existing: bool, -) !void { - switch (global_ctype.info(global_ctype_pool)) { - .basic, .pointer, .array, .vector, .function => {}, - .aligned => |aligned_info| { - if (!found_existing) { - std.debug.assert(aligned_info.alignas.abiOrder().compare(.lt)); - try w.print("typedef zig_under_align({d}) ", .{aligned_info.alignas.toByteUnits()}); - try w.print("{f}", .{try renderTypePrefix( - .flush, - global_ctype_pool, - zcu, - w, - aligned_info.ctype, - .suffix, - .{}, - )}); - try renderAlignedTypeName(w, global_ctype); - try renderTypeSuffix(.flush, global_ctype_pool, zcu, w, aligned_info.ctype, .suffix, .{}); - try w.writeAll(";\n"); - } - switch (pass) { - .nav, .uav => { - try w.writeAll("typedef "); - _ = try renderTypePrefix(.flush, global_ctype_pool, zcu, w, global_ctype, .suffix, .{}); - try w.writeByte(' '); - _ = try renderTypePrefix(pass, decl_ctype_pool, zcu, w, decl_ctype, .suffix, .{}); - try w.writeAll(";\n"); - }, - .flush => {}, - } - }, - .fwd_decl => |fwd_decl_info| switch (fwd_decl_info.name) { - .anon => switch (pass) { - .nav, .uav => { - try w.writeAll("typedef "); - _ = try renderTypePrefix(.flush, global_ctype_pool, zcu, w, global_ctype, .suffix, .{}); - try w.writeByte(' '); - _ = try renderTypePrefix(pass, decl_ctype_pool, zcu, w, decl_ctype, .suffix, .{}); - try w.writeAll(";\n"); - }, - .flush => {}, - }, - .index => |index| if (!found_existing) { - const ip = &zcu.intern_pool; - const ty: Type = .fromInterned(index); - _ = try renderTypePrefix(.flush, global_ctype_pool, zcu, w, global_ctype, .suffix, .{}); - try w.writeByte(';'); - const file_scope = ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip); - if (!zcu.fileByIndex(file_scope).mod.?.strip) try w.print(" /* {f} */", .{ - ty.containerTypeName(ip).fmt(ip), - }); - try w.writeByte('\n'); - }, - }, - .aggregate => |aggregate_info| switch (aggregate_info.name) { - .anon => {}, - .fwd_decl => |fwd_decl| if (!found_existing) { - try renderFwdDeclTypeName( - zcu, - w, - fwd_decl, - fwd_decl.info(global_ctype_pool).fwd_decl, - if (aggregate_info.@"packed") "zig_packed(" else "", - ); - try w.writeByte(' '); - try renderFields(zcu, w, global_ctype_pool, aggregate_info, 0); - if (aggregate_info.@"packed") try w.writeByte(')'); - try w.writeAll(";\n"); - }, - }, - } -} pub fn genGlobalAsm(zcu: *Zcu, w: *Writer) !void { for (zcu.global_assembly.values()) |asm_source| { @@ -2713,200 +2059,128 @@ pub fn genGlobalAsm(zcu: *Zcu, w: *Writer) !void { } } -pub fn genErrDecls(o: *Object) Error!void { - const pt = o.dg.pt; - const zcu = pt.zcu; +pub fn genErrDecls( + zcu: *const Zcu, + w: *Writer, + slice_const_u8_sentinel_0_type_name: []const u8, +) Writer.Error!void { const ip = &zcu.intern_pool; - const w = &o.code.writer; - var max_name_len: usize = 0; - // do not generate an invalid empty enum when the global error set is empty const names = ip.global_error_set.getNamesFromMainThread(); + // Don't generate an invalid empty enum if the global error set is empty! if (names.len > 0) { - try w.writeAll("enum {"); - o.indent(); - try o.newline(); + try w.writeAll("enum {\n"); for (names, 1..) |name_nts, value| { - const name = name_nts.toSlice(ip); - max_name_len = @max(name.len, max_name_len); - const err_val = try pt.intern(.{ .err = .{ - .ty = .anyerror_type, - .name = name_nts, - } }); - try o.dg.renderValue(w, Value.fromInterned(err_val), .Other); - try w.print(" = {d}u,", .{value}); - try o.newline(); + try w.writeByte(' '); + try renderErrorName(w, name_nts.toSlice(ip)); + try w.print(" = {d}u,\n", .{value}); } - try o.outdent(); - try w.writeAll("};"); - try o.newline(); - } - const array_identifier = "zig_errorName"; - const name_prefix = array_identifier ++ "_"; - const name_buf = try o.dg.gpa.alloc(u8, name_prefix.len + max_name_len); - defer o.dg.gpa.free(name_buf); - - @memcpy(name_buf[0..name_prefix.len], name_prefix); - for (names) |name| { - const name_slice = name.toSlice(ip); - @memcpy(name_buf[name_prefix.len..][0..name_slice.len], name_slice); - const identifier = name_buf[0 .. name_prefix.len + name_slice.len]; - - const name_ty = try pt.arrayType(.{ - .len = name_slice.len, - .child = .u8_type, - .sentinel = .zero_u8, - }); - const name_val = try pt.intern(.{ .aggregate = .{ - .ty = name_ty.toIntern(), - .storage = .{ .bytes = name.toString() }, - } }); + try w.writeAll("};\n"); + } - try w.writeAll("static "); - try o.dg.renderTypeAndName( - w, - name_ty, - .{ .identifier = identifier }, - Const, - .none, - .complete, + for (names) |name_nts| { + const name = name_nts.toSlice(ip); + try w.print( + "static uint8_t const zig_errorName_{f}[] = {f};\n", + .{ fmtIdentUnsolo(name), fmtStringLiteral(name, 0) }, ); - try w.writeAll(" = "); - try o.dg.renderValue(w, Value.fromInterned(name_val), .StaticInitializer); - try w.writeByte(';'); - try o.newline(); } - const name_array_ty = try pt.arrayType(.{ - .len = 1 + names.len, - .child = .slice_const_u8_sentinel_0_type, - }); - - try w.writeAll("static "); - try o.dg.renderTypeAndName( - w, - name_array_ty, - .{ .identifier = array_identifier }, - Const, - .none, - .complete, + try w.print( + "static {s} const zig_errorName[{d}] = {{", + .{ slice_const_u8_sentinel_0_type_name, names.len }, ); - try w.writeAll(" = {"); - for (names, 1..) |name_nts, val| { + if (names.len > 0) try w.writeByte('\n'); + for (names) |name_nts| { const name = name_nts.toSlice(ip); - if (val > 1) try w.writeAll(", "); - try w.print("{{" ++ name_prefix ++ "{f}, {f}}}", .{ - fmtIdentUnsolo(name), - try o.dg.fmtIntLiteralDec(try pt.intValue(.usize, name.len), .StaticInitializer), + try w.print( + " {{zig_errorName_{f},{d}}},\n", + .{ fmtIdentUnsolo(name), name.len }, + ); + } + try w.writeAll("};\n"); +} + +pub fn genTagNameFn( + zcu: *const Zcu, + w: *Writer, + slice_const_u8_sentinel_0_type_name: []const u8, + enum_ty: Type, + enum_type_name: []const u8, +) Writer.Error!void { + const ip = &zcu.intern_pool; + const loaded_enum = ip.loadEnumType(enum_ty.toIntern()); + assert(loaded_enum.field_names.len > 0); + if (Type.fromInterned(loaded_enum.int_tag_type).bitSize(zcu) > 64) { + @panic("TODO CBE: tagName for enum over 64 bits"); + } + + try w.print("static {s} zig_tagName_{f}__{d}({s} tag) {{\n", .{ + slice_const_u8_sentinel_0_type_name, + fmtIdentUnsolo(loaded_enum.name.toSlice(ip)), + @intFromEnum(enum_ty.toIntern()), + enum_type_name, + }); + for (loaded_enum.field_names.get(ip), 0..) |field_name, field_index| { + try w.print(" static uint8_t const name{d}[] = {f};\n", .{ + field_index, fmtStringLiteral(field_name.toSlice(ip), 0), }); } - try w.writeAll("};"); - try o.newline(); + + try w.writeAll(" switch (tag) {\n"); + const field_values = loaded_enum.field_values.get(ip); + for (loaded_enum.field_names.get(ip), 0..) |field_name, field_index| { + const field_int: i65 = int: { + if (field_values.len == 0) break :int field_index; + const field_val: Value = .fromInterned(field_values[field_index]); + break :int field_val.getUnsignedInt(zcu) orelse field_val.toSignedInt(zcu); + }; + try w.print(" case {d}: return ({s}){{name{d},{d}}};\n", .{ + field_int, + slice_const_u8_sentinel_0_type_name, + field_index, + field_name.toSlice(ip).len, + }); + } + try w.writeAll( + \\ } + \\ zig_unreachable(); + \\} + \\ + ); } -pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFnMap.Entry) Error!void { - const pt = o.dg.pt; - const zcu = pt.zcu; +pub fn genLazyCallModifierFn( + dg: *DeclGen, + fn_nav: InternPool.Nav.Index, + kind: enum { never_tail, never_inline }, + w: *Writer, +) Error!void { + const zcu = dg.pt.zcu; const ip = &zcu.intern_pool; - const ctype_pool = &o.dg.ctype_pool; - const w = &o.code.writer; - const key = lazy_fn.key_ptr.*; - const val = lazy_fn.value_ptr; - switch (key) { - .tag_name => |enum_ty_ip| { - const enum_ty: Type = .fromInterned(enum_ty_ip); - const name_slice_ty: Type = .slice_const_u8_sentinel_0; - - try w.writeAll("static "); - try o.dg.renderType(w, name_slice_ty); - try w.print(" {f}(", .{val.fn_name.fmt(lazy_ctype_pool)}); - try o.dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, Const, .none, .complete); - try w.writeAll(") {"); - o.indent(); - try o.newline(); - try w.writeAll("switch (tag) {"); - o.indent(); - try o.newline(); - const tag_names = enum_ty.enumFields(zcu); - for (0..tag_names.len) |tag_index| { - const tag_name = tag_names.get(ip)[tag_index]; - const tag_name_len = tag_name.length(ip); - const tag_val = try pt.enumValueFieldIndex(enum_ty, @intCast(tag_index)); - - const name_ty = try pt.arrayType(.{ - .len = tag_name_len, - .child = .u8_type, - .sentinel = .zero_u8, - }); - const name_val = try pt.intern(.{ .aggregate = .{ - .ty = name_ty.toIntern(), - .storage = .{ .bytes = tag_name.toString() }, - } }); - try w.print("case {f}: {{", .{ - try o.dg.fmtIntLiteralDec(try tag_val.intFromEnum(enum_ty, pt), .Other), - }); - o.indent(); - try o.newline(); - try w.writeAll("static "); - try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, .none, .complete); - try w.writeAll(" = "); - try o.dg.renderValue(w, Value.fromInterned(name_val), .StaticInitializer); - try w.writeByte(';'); - try o.newline(); - try w.writeAll("return ("); - try o.dg.renderType(w, name_slice_ty); - try w.print("){{{f}, {f}}};", .{ - fmtIdentUnsolo("name"), - try o.dg.fmtIntLiteralDec(try pt.intValue(.usize, tag_name_len), .Other), - }); - try o.newline(); - try o.outdent(); - try w.writeByte('}'); - try o.newline(); - } - try o.outdent(); - try w.writeByte('}'); - try o.newline(); - try airUnreach(o); - try o.outdent(); - try w.writeByte('}'); - try o.newline(); - }, - .never_tail, .never_inline => |fn_nav_index| { - const fn_val = zcu.navValue(fn_nav_index); - const fn_ctype = try o.dg.ctypeFromType(fn_val.typeOf(zcu), .complete); - const fn_info = fn_ctype.info(ctype_pool).function; - const fn_name = fmtCTypePoolString(val.fn_name, lazy_ctype_pool, true); - - const fwd = &o.dg.fwd_decl.writer; - try fwd.print("static zig_{s} ", .{@tagName(key)}); - try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).getAlignment(), .forward, .{ - .fmt_ctype_pool_string = fn_name, - }); - try fwd.writeAll(";\n"); + const fn_val = zcu.navValue(fn_nav); - try w.print("zig_{s} ", .{@tagName(key)}); - try o.dg.renderFunctionSignature(w, fn_val, .none, .complete, .{ - .fmt_ctype_pool_string = fn_name, - }); - try w.writeAll(" {"); - o.indent(); - try o.newline(); - try w.writeAll("return "); - try o.dg.renderNavName(w, fn_nav_index); - try w.writeByte('('); - for (0..fn_info.param_ctypes.len) |arg| { - if (arg > 0) try w.writeAll(", "); - try w.print("a{d}", .{arg}); - } - try w.writeAll(");"); - try o.newline(); - try o.outdent(); - try w.writeByte('}'); - try o.newline(); - }, + try w.print("static zig_{t} ", .{kind}); + try dg.renderFunctionSignature(w, fn_val, .none, .definition, switch (kind) { + .never_tail => .{ .nav_never_tail = fn_nav }, + .never_inline => .{ .nav_never_inline = fn_nav }, + }); + try w.writeAll(" {\n return "); + try renderNavName(w, fn_nav, ip); + try w.writeByte('('); + { + const func_type = ip.indexToKey(fn_val.typeOf(zcu).toIntern()).func_type; + var c_param_index: u32 = 0; + for (func_type.param_types.get(ip)) |param_ty_ip| { + const param_ty: Type = .fromInterned(param_ty_ip); + if (!param_ty.hasRuntimeBits(zcu)) continue; + if (c_param_index != 0) try w.writeAll(", "); + try w.print("a{d}", .{c_param_index}); + c_param_index += 1; + } } + try w.writeAll(");\n}\n"); } pub fn generate( @@ -2925,110 +2199,109 @@ pub fn generate( const func = zcu.funcInfo(func_index); + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + var function: Function = .{ .value_map = .init(gpa), .air = air.*, .liveness = liveness.*.?, .func_index = func_index, - .object = .{ - .dg = .{ - .gpa = gpa, - .pt = pt, - .mod = zcu.navFileScope(func.owner_nav).mod.?, - .error_msg = null, - .pass = .{ .nav = func.owner_nav }, - .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked, - .expected_block = null, - .fwd_decl = .init(gpa), - .ctype_pool = .empty, - .scratch = .empty, - .uavs = .empty, - }, - .code_header = .init(gpa), - .code = .init(gpa), - .indent_counter = 0, + .dg = .{ + .gpa = gpa, + .arena = arena.allocator(), + .pt = pt, + .mod = zcu.navFileScope(func.owner_nav).mod.?, + .error_msg = null, + .owner_nav = func.owner_nav.toOptional(), + .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked, + .expected_block = null, + .ctype_deps = .empty, + .uavs = .empty, }, - .lazy_fns = .empty, + .code = .init(gpa), + .indent_counter = 0, + .need_tag_name_funcs = .empty, + .need_never_tail_funcs = .empty, + .need_never_inline_funcs = .empty, }; defer { - function.object.code_header.deinit(); - function.object.code.deinit(); - function.object.dg.fwd_decl.deinit(); - function.object.dg.ctype_pool.deinit(gpa); - function.object.dg.scratch.deinit(gpa); - function.object.dg.uavs.deinit(gpa); + function.code.deinit(); + function.dg.ctype_deps.deinit(gpa); + function.dg.uavs.deinit(gpa); function.deinit(); } - try function.object.dg.ctype_pool.init(gpa); - genFunc(&function) catch |err| switch (err) { - error.AnalysisFail => return zcu.codegenFailMsg(func.owner_nav, function.object.dg.error_msg.?), - error.OutOfMemory => return error.OutOfMemory, + var fwd_decl: Writer.Allocating = .init(gpa); + defer fwd_decl.deinit(); + + var code_header: Writer.Allocating = .init(gpa); + defer code_header.deinit(); + + genFunc(&function, &fwd_decl.writer, &code_header.writer) catch |err| switch (err) { + error.AnalysisFail => return zcu.codegenFailMsg(func.owner_nav, function.dg.error_msg.?), error.WriteFailed => return error.OutOfMemory, + error.OutOfMemory => |e| return e, }; var mir: Mir = .{ - .uavs = .empty, - .code = &.{}, - .code_header = &.{}, .fwd_decl = &.{}, - .ctype_pool = .empty, - .lazy_fns = .empty, + .code_header = &.{}, + .code = &.{}, + .ctype_deps = function.dg.ctype_deps.move(), + .need_uavs = function.dg.uavs.move(), + .need_tag_name_funcs = function.need_tag_name_funcs.move(), + .need_never_tail_funcs = function.need_never_tail_funcs.move(), + .need_never_inline_funcs = function.need_never_inline_funcs.move(), }; errdefer mir.deinit(gpa); - mir.uavs = function.object.dg.uavs.move(); - mir.code_header = try function.object.code_header.toOwnedSlice(); - mir.code = try function.object.code.toOwnedSlice(); - mir.fwd_decl = try function.object.dg.fwd_decl.toOwnedSlice(); - mir.ctype_pool = function.object.dg.ctype_pool.move(); - mir.lazy_fns = function.lazy_fns.move(); + mir.fwd_decl = try fwd_decl.toOwnedSlice(); + mir.code_header = try code_header.toOwnedSlice(); + mir.code = try function.code.toOwnedSlice(); return mir; } -pub fn genFunc(f: *Function) Error!void { +pub fn genFunc(f: *Function, fwd_decl_writer: *Writer, header_writer: *Writer) Error!void { const tracy = trace(@src()); defer tracy.end(); - const o = &f.object; - const zcu = o.dg.pt.zcu; + const zcu = f.dg.pt.zcu; const ip = &zcu.intern_pool; - const gpa = o.dg.gpa; - const nav_index = o.dg.pass.nav; + const gpa = f.dg.gpa; + const nav_index = f.dg.owner_nav.unwrap().?; const nav_val = zcu.navValue(nav_index); const nav = ip.getNav(nav_index); - const fwd = &o.dg.fwd_decl.writer; - try fwd.writeAll("static "); - try o.dg.renderFunctionSignature( - fwd, + try fwd_decl_writer.writeAll("static "); + try f.dg.renderFunctionSignature( + fwd_decl_writer, nav_val, nav.status.fully_resolved.alignment, - .forward, + .forward_decl, .{ .nav = nav_index }, ); - try fwd.writeAll(";\n"); + try fwd_decl_writer.writeAll(";\n"); - const ch = &o.code_header.writer; if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |s| - try ch.print("zig_linksection_fn({f}) ", .{fmtStringLiteral(s, null)}); - try o.dg.renderFunctionSignature( - ch, + try header_writer.print("zig_linksection_fn({f}) ", .{fmtStringLiteral(s, null)}); + try f.dg.renderFunctionSignature( + header_writer, nav_val, .none, - .complete, + .definition, .{ .nav = nav_index }, ); - try ch.writeAll(" {\n "); + try header_writer.writeAll(" {\n "); f.free_locals_map.clearRetainingCapacity(); const main_body = f.air.getMainBody(); - o.indent(); + f.indent(); try genBodyResolveState(f, undefined, &.{}, main_body, true); - try o.outdent(); - try o.code.writer.writeByte('}'); - try o.newline(); - if (o.dg.expected_block) |_| + try f.outdent(); + try f.code.writer.writeByte('}'); + try f.newline(); + if (f.dg.expected_block) |_| return f.fail("runtime code not allowed in naked function", .{}); // Take advantage of the free_locals map to bucket locals per type. All @@ -3042,155 +2315,204 @@ pub fn genFunc(f: *Function) Error!void { if (!should_emit) continue; const local = f.locals.items[local_index]; log.debug("inserting local {d} into free_locals", .{local_index}); - const gop = try free_locals.getOrPut(gpa, local.getType()); + const gop = try free_locals.getOrPut(gpa, local); if (!gop.found_existing) gop.value_ptr.* = .{}; try gop.value_ptr.putNoClobber(gpa, local_index, {}); } const SortContext = struct { + zcu: *const Zcu, keys: []const LocalType, pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool { - const lhs_ty = ctx.keys[lhs_index]; - const rhs_ty = ctx.keys[rhs_index]; - return lhs_ty.alignas.order(rhs_ty.alignas).compare(.gt); + const lhs = ctx.keys[lhs_index]; + const rhs = ctx.keys[rhs_index]; + const lhs_align = switch (lhs.alignment) { + .none => lhs.type.abiAlignment(ctx.zcu), + else => |a| a, + }; + const rhs_align = switch (rhs.alignment) { + .none => rhs.type.abiAlignment(ctx.zcu), + else => |a| a, + }; + return Alignment.compareStrict(lhs_align, .gt, rhs_align); } }; - free_locals.sort(SortContext{ .keys = free_locals.keys() }); + free_locals.sort(SortContext{ + .zcu = zcu, + .keys = free_locals.keys(), + }); for (free_locals.values()) |list| { for (list.keys()) |local_index| { const local = f.locals.items[local_index]; - try o.dg.renderCTypeAndName(ch, local.ctype, .{ .local = local_index }, .{}, local.flags.alignas); - try ch.writeAll(";\n "); + try f.dg.renderTypeAndName(header_writer, local.type, .{ .local = local_index }, .{}, local.alignment); + try header_writer.writeAll(";\n "); } } } -pub fn genDecl(o: *Object) Error!void { +pub fn genDecl(dg: *DeclGen, w: *Writer) Error!void { const tracy = trace(@src()); defer tracy.end(); - const pt = o.dg.pt; + const pt = dg.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - const nav = ip.getNav(o.dg.pass.nav); + const nav = ip.getNav(dg.owner_nav.unwrap().?); const nav_ty: Type = .fromInterned(nav.typeOf(ip)); - if (!nav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return; - switch (ip.indexToKey(nav.status.fully_resolved.val)) { - .@"extern" => |@"extern"| { - if (!ip.isFunctionType(nav_ty.toIntern())) return o.dg.renderFwdDecl(o.dg.pass.nav, .{ - .is_const = @"extern".is_const, - .is_threadlocal = @"extern".is_threadlocal, - .linkage = @"extern".linkage, - .visibility = @"extern".visibility, - }); + const is_const: bool, const is_threadlocal: bool, const init_val: Value = switch (ip.indexToKey(nav.status.fully_resolved.val)) { + else => .{ true, false, .fromInterned(nav.status.fully_resolved.val) }, + .variable => |v| .{ false, v.is_threadlocal, .fromInterned(v.init) }, + .@"extern" => return, + }; - const fwd = &o.dg.fwd_decl.writer; - try fwd.writeAll("zig_extern "); - try o.dg.renderFunctionSignature( - fwd, - Value.fromInterned(nav.status.fully_resolved.val), - nav.status.fully_resolved.alignment, - .forward, - .{ .@"export" = .{ - .main_name = nav.name, - .extern_name = nav.name, - } }, - ); - try fwd.writeAll(";\n"); - }, - .variable => |variable| { - try o.dg.renderFwdDecl(o.dg.pass.nav, .{ - .is_const = false, - .is_threadlocal = variable.is_threadlocal, - .linkage = .internal, - .visibility = .default, - }); - const w = &o.code.writer; - if (variable.is_threadlocal and !o.dg.mod.single_threaded) try w.writeAll("zig_threadlocal "); - if (nav.status.fully_resolved.@"linksection".toSlice(&zcu.intern_pool)) |s| - try w.print("zig_linksection({f}) ", .{fmtStringLiteral(s, null)}); - try o.dg.renderTypeAndName( - w, - nav_ty, - .{ .nav = o.dg.pass.nav }, - .{}, - nav.status.fully_resolved.alignment, - .complete, - ); - try w.writeAll(" = "); - try o.dg.renderValue(w, Value.fromInterned(variable.init), .StaticInitializer); - try w.writeByte(';'); - try o.newline(); - }, - else => try genDeclValue( - o, - Value.fromInterned(nav.status.fully_resolved.val), - .{ .nav = o.dg.pass.nav }, - nav.status.fully_resolved.alignment, - nav.status.fully_resolved.@"linksection", - ), + if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |s| { + try w.print("zig_linksection({f}) ", .{fmtStringLiteral(s, null)}); } + + // We don't bother underaligning---it's unnecessary and hurts compatibility. + const a = nav.status.fully_resolved.alignment; + if (a != .none and a.compareStrict(.gt, nav_ty.abiAlignment(zcu))) { + try w.print("zig_align({d}) ", .{a.toByteUnits().?}); + } + + try genDeclValue(dg, w, .{ + .name = .{ .nav = dg.owner_nav.unwrap().? }, + .@"const" = is_const, + .@"threadlocal" = is_threadlocal, + .init_val = init_val, + }); } +pub fn genDeclFwd(dg: *DeclGen, w: *Writer) Error!void { + const tracy = trace(@src()); + defer tracy.end(); -pub fn genDeclValue( - o: *Object, - val: Value, - decl_c_value: CValue, - alignment: Alignment, - @"linksection": InternPool.OptionalNullTerminatedString, -) Error!void { - const zcu = o.dg.pt.zcu; - const ty = val.typeOf(zcu); + const pt = dg.pt; + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const nav = ip.getNav(dg.owner_nav.unwrap().?); + const nav_ty: Type = .fromInterned(nav.typeOf(ip)); - const fwd = &o.dg.fwd_decl.writer; - try fwd.writeAll("static "); - try o.dg.renderTypeAndName(fwd, ty, decl_c_value, Const, alignment, .complete); - try fwd.writeAll(";\n"); + const is_const: bool, const is_threadlocal: bool, const init_val: Value = switch (ip.indexToKey(nav.status.fully_resolved.val)) { + else => .{ true, false, .fromInterned(nav.status.fully_resolved.val) }, + .variable => |v| .{ false, v.is_threadlocal, .fromInterned(v.init) }, - const w = &o.code.writer; - if (@"linksection".toSlice(&zcu.intern_pool)) |s| - try w.print("zig_linksection({f}) ", .{fmtStringLiteral(s, null)}); - try o.dg.renderTypeAndName(w, ty, decl_c_value, Const, alignment, .complete); + .@"extern" => |@"extern"| switch (nav_ty.zigTypeTag(zcu)) { + .@"fn" => { + try w.writeAll("zig_extern "); + try dg.renderFunctionSignature( + w, + Value.fromInterned(nav.status.fully_resolved.val), + nav.status.fully_resolved.alignment, + .forward_decl, + .{ .@"export" = .{ + .main_name = nav.name, + .extern_name = nav.name, + } }, + ); + try w.writeAll(";\n"); + return; + }, + else => { + switch (@"extern".linkage) { + .internal => try w.writeAll("static "), + .strong => try w.print("zig_extern zig_visibility({t}) ", .{@"extern".visibility}), + .weak => try w.print("zig_extern zig_weak_linkage zig_visibility({t}) ", .{@"extern".visibility}), + .link_once => return dg.fail("TODO: CBE: implement linkonce linkage?", .{}), + } + if (@"extern".is_threadlocal and !dg.mod.single_threaded) { + try w.writeAll("zig_threadlocal "); + } + try dg.renderTypeAndName( + w, + .fromInterned(nav.typeOf(ip)), + .{ .nav = dg.owner_nav.unwrap().? }, + .{ .@"const" = @"extern".is_const }, + nav.getAlignment(), + ); + try w.writeAll(";\n"); + return; + }, + }, + }; + + // We don't bother underaligning---it's unnecessary and hurts compatibility. + const a = nav.status.fully_resolved.alignment; + if (a != .none and a.compareStrict(.gt, nav_ty.abiAlignment(zcu))) { + try w.print("zig_align({d}) ", .{a.toByteUnits().?}); + } + + try genDeclValueFwd(dg, w, .{ + .name = .{ .nav = dg.owner_nav.unwrap().? }, + .@"const" = is_const, + .@"threadlocal" = is_threadlocal, + .init_val = init_val, + }); +} +pub fn genDeclValue(dg: *DeclGen, w: *Writer, options: struct { + name: CValue, + @"const": bool, + @"threadlocal": bool, + init_val: Value, +}) Error!void { + const zcu = dg.pt.zcu; + const ty = options.init_val.typeOf(zcu); + if (options.@"threadlocal" and !dg.mod.single_threaded) { + try w.writeAll("zig_threadlocal "); + } + try dg.renderTypeAndName(w, ty, options.name, .{ .@"const" = options.@"const" }, .none); try w.writeAll(" = "); - try o.dg.renderValue(w, val, .StaticInitializer); - try w.writeByte(';'); - try o.newline(); + try dg.renderValue(w, options.init_val, .static_initializer); + try w.writeAll(";\n"); +} +pub fn genDeclValueFwd(dg: *DeclGen, w: *Writer, options: struct { + name: CValue, + @"const": bool, + @"threadlocal": bool, + init_val: Value, +}) Error!void { + const zcu = dg.pt.zcu; + const ty = options.init_val.typeOf(zcu); + try w.writeAll("static "); + if (options.@"threadlocal" and !dg.mod.single_threaded) { + try w.writeAll("zig_threadlocal "); + } + try dg.renderTypeAndName(w, ty, options.name, .{ .@"const" = options.@"const" }, .none); + try w.writeAll(";\n"); } -pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index) !void { +pub fn genExports(dg: *DeclGen, w: *Writer, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index) !void { const zcu = dg.pt.zcu; const ip = &zcu.intern_pool; - const fwd = &dg.fwd_decl.writer; const main_name = export_indices[0].ptr(zcu).opts.name; - try fwd.writeAll("#define "); + try w.writeAll("#define "); switch (exported) { - .nav => |nav| try dg.renderNavName(fwd, nav), - .uav => |uav| try DeclGen.renderUavName(fwd, Value.fromInterned(uav)), + .nav => |nav| try renderNavName(w, nav, ip), + .uav => |uav| try renderUavName(w, Value.fromInterned(uav)), } - try fwd.writeByte(' '); - try fwd.print("{f}", .{fmtIdentSolo(main_name.toSlice(ip))}); - try fwd.writeByte('\n'); + try w.writeByte(' '); + try w.print("{f}", .{fmtIdentSolo(main_name.toSlice(ip))}); + try w.writeByte('\n'); const exported_val = exported.getValue(zcu); if (ip.isFunctionType(exported_val.typeOf(zcu).toIntern())) return for (export_indices) |export_index| { const @"export" = export_index.ptr(zcu); - try fwd.writeAll("zig_extern "); - if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage_fn "); + try w.writeAll("zig_extern "); + if (@"export".opts.linkage == .weak) try w.writeAll("zig_weak_linkage_fn "); try dg.renderFunctionSignature( - fwd, + w, exported.getValue(zcu), exported.getAlign(zcu), - .forward, + .forward_decl, .{ .@"export" = .{ .main_name = main_name, .extern_name = @"export".opts.name, } }, ); - try fwd.writeAll(";\n"); + try w.writeAll(";\n"); }; const is_const = switch (ip.indexToKey(exported_val.toIntern())) { .func => unreachable, @@ -3200,39 +2522,38 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const }; for (export_indices) |export_index| { const @"export" = export_index.ptr(zcu); - try fwd.writeAll("zig_extern "); - if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage "); - if (@"export".opts.section.toSlice(ip)) |s| try fwd.print("zig_linksection({f}) ", .{ + try w.writeAll("zig_extern "); + if (@"export".opts.linkage == .weak) try w.writeAll("zig_weak_linkage "); + if (@"export".opts.section.toSlice(ip)) |s| try w.print("zig_linksection({f}) ", .{ fmtStringLiteral(s, null), }); const extern_name = @"export".opts.name.toSlice(ip); const is_mangled = isMangledIdent(extern_name, true); const is_export = @"export".opts.name != main_name; try dg.renderTypeAndName( - fwd, + w, exported.getValue(zcu).typeOf(zcu), .{ .identifier = extern_name }, - CQualifiers.init(.{ .@"const" = is_const }), + .{ .@"const" = is_const }, exported.getAlign(zcu), - .complete, ); if (is_mangled and is_export) { - try fwd.print(" zig_mangled_export({f}, {f}, {f})", .{ + try w.print(" zig_mangled_export({f}, {f}, {f})", .{ fmtIdentSolo(extern_name), fmtStringLiteral(extern_name, null), fmtStringLiteral(main_name.toSlice(ip), null), }); } else if (is_mangled) { - try fwd.print(" zig_mangled({f}, {f})", .{ + try w.print(" zig_mangled({f}, {f})", .{ fmtIdentSolo(extern_name), fmtStringLiteral(extern_name, null), }); } else if (is_export) { - try fwd.print(" zig_export({f}, {f})", .{ + try w.print(" zig_export({f}, {f})", .{ fmtStringLiteral(main_name.toSlice(ip), null), fmtStringLiteral(extern_name, null), }); } - try fwd.writeAll(";\n"); + try w.writeAll(";\n"); } } @@ -3241,15 +2562,15 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const /// have been added to `free_locals_map`. For a version of this function that restores this state, /// see `genBodyResolveState`. fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void { - const w = &f.object.code.writer; + const w = &f.code.writer; if (body.len == 0) { try w.writeAll("{}"); } else { try w.writeByte('{'); - f.object.indent(); - try f.object.newline(); + f.indent(); + try f.newline(); try genBodyInner(f, body); - try f.object.outdent(); + try f.outdent(); try w.writeByte('}'); } } @@ -3263,13 +2584,13 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void { fn genBodyResolveState(f: *Function, inst: Air.Inst.Index, leading_deaths: []const Air.Inst.Index, body: []const Air.Inst.Index, inner: bool) Error!void { if (body.len == 0) { // Don't go to the expense of cloning everything! - if (!inner) try f.object.code.writer.writeAll("{}"); + if (!inner) try f.code.writer.writeAll("{}"); return; } // TODO: we can probably avoid the copies in some other common cases too. - const gpa = f.object.dg.gpa; + const gpa = f.dg.gpa; // Save the original value_map and free_locals_map so that we can restore them after the body. var old_value_map = try f.value_map.clone(); @@ -3310,13 +2631,13 @@ fn genBodyResolveState(f: *Function, inst: Air.Inst.Index, leading_deaths: []con } fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void { - const zcu = f.object.dg.pt.zcu; + const zcu = f.dg.pt.zcu; const ip = &zcu.intern_pool; const air_tags = f.air.instructions.items(.tag); const air_datas = f.air.instructions.items(.data); for (body) |inst| { - if (f.object.dg.expected_block) |_| + if (f.dg.expected_block) |_| return f.fail("runtime code not allowed in naked function", .{}); if (f.liveness.isUnused(inst) and !f.air.mustLower(inst, ip)) continue; @@ -3585,8 +2906,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void { .ret => return airRet(f, inst, false), .ret_safe => return airRet(f, inst, false), // TODO .ret_load => return airRet(f, inst, true), - .trap => return airTrap(f, &f.object.code.writer), - .unreach => return airUnreach(&f.object), + .trap => return airTrap(f), + .unreach => return airUnreach(f), // Instructions which may be `noreturn`. .block => res: { @@ -3629,177 +2950,159 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [ const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); + try f.writeCValue(w, local, .other); + try w.writeAll(" = "); if (is_ptr) { try w.writeByte('&'); try f.writeCValueDerefMember(w, operand, .{ .identifier = field_name }); } else try f.writeCValueMember(w, operand, .{ .identifier = field_name }); - try a.end(f, w); + try w.writeByte(';'); + try f.newline(); return local; } fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { - const zcu = f.object.dg.pt.zcu; + const zcu = f.dg.pt.zcu; const inst_ty = f.typeOfIndex(inst); const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - if (!inst_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return .none; - } + assert(inst_ty.hasRuntimeBits(zcu)); const ptr = try f.resolveInst(bin_op.lhs); const index = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); - try f.writeCValue(w, ptr, .Other); + try f.writeCValue(w, local, .other); + try w.writeAll(" = "); + switch (f.typeOf(bin_op.lhs).ptrSize(zcu)) { + .one => try f.writeCValueDerefMember(w, ptr, .{ .identifier = "array" }), + .many, .c => try f.writeCValue(w, ptr, .other), + .slice => unreachable, + } try w.writeByte('['); - try f.writeCValue(w, index, .Other); - try w.writeByte(']'); - try a.end(f, w); + try f.writeCValue(w, index, .other); + try w.writeAll("];"); + try f.newline(); return local; } fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; const inst_ty = f.typeOfIndex(inst); const ptr_ty = f.typeOf(bin_op.lhs); - const elem_has_bits = ptr_ty.elemType2(zcu).hasRuntimeBitsIgnoreComptime(zcu); + assert(ptr_ty.indexableElem(zcu).hasRuntimeBits(zcu)); const ptr = try f.resolveInst(bin_op.lhs); const index = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); - try w.writeByte('('); - try f.renderType(w, inst_ty); - try w.writeByte(')'); - if (elem_has_bits) try w.writeByte('&'); - if (elem_has_bits and ptr_ty.ptrSize(zcu) == .one) { - // It's a pointer to an array, so we need to de-reference. - try f.writeCValueDeref(w, ptr); - } else try f.writeCValue(w, ptr, .Other); - if (elem_has_bits) { - try w.writeByte('['); - try f.writeCValue(w, index, .Other); - try w.writeByte(']'); + try f.writeCValue(w, local, .other); + try w.writeAll(" = "); + try w.writeByte('&'); + if (ptr_ty.ptrSize(zcu) == .one) { + // `*[n]T` was turned into a pointer to `struct { T array[n]; }` + try f.writeCValueDerefMember(w, ptr, .{ .identifier = "array" }); + } else { + try f.writeCValue(w, ptr, .other); } - try a.end(f, w); + try w.writeByte('['); + try f.writeCValue(w, index, .other); + try w.writeAll("];"); + try f.newline(); return local; } fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { - const zcu = f.object.dg.pt.zcu; + const zcu = f.dg.pt.zcu; const inst_ty = f.typeOfIndex(inst); const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - if (!inst_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return .none; - } + assert(inst_ty.hasRuntimeBits(zcu)); const slice = try f.resolveInst(bin_op.lhs); const index = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); + try f.writeCValue(w, local, .other); + try w.writeAll(" = "); try f.writeCValueMember(w, slice, .{ .identifier = "ptr" }); try w.writeByte('['); - try f.writeCValue(w, index, .Other); - try w.writeByte(']'); - try a.end(f, w); + try f.writeCValue(w, index, .other); + try w.writeAll("];"); + try f.newline(); return local; } fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; const inst_ty = f.typeOfIndex(inst); const slice_ty = f.typeOf(bin_op.lhs); - const elem_ty = slice_ty.elemType2(zcu); - const elem_has_bits = elem_ty.hasRuntimeBitsIgnoreComptime(zcu); + const elem_ty = slice_ty.childType(zcu); + assert(elem_ty.hasRuntimeBits(zcu)); const slice = try f.resolveInst(bin_op.lhs); const index = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); - if (elem_has_bits) try w.writeByte('&'); + try f.writeCValue(w, local, .other); + try w.writeAll(" = "); + try w.writeByte('&'); try f.writeCValueMember(w, slice, .{ .identifier = "ptr" }); - if (elem_has_bits) { - try w.writeByte('['); - try f.writeCValue(w, index, .Other); - try w.writeByte(']'); - } - try a.end(f, w); + try w.writeByte('['); + try f.writeCValue(w, index, .other); + try w.writeAll("];"); + try f.newline(); return local; } fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { - const zcu = f.object.dg.pt.zcu; + const zcu = f.dg.pt.zcu; const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const inst_ty = f.typeOfIndex(inst); - if (!inst_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return .none; - } + assert(inst_ty.hasRuntimeBits(zcu)); const array = try f.resolveInst(bin_op.lhs); const index = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); - try f.writeCValue(w, array, .Other); + try f.writeCValue(w, local, .other); + try w.writeAll(" = "); + try f.writeCValueMember(w, array, .{ .identifier = "array" }); try w.writeByte('['); - try f.writeCValue(w, index, .Other); - try w.writeByte(']'); - try a.end(f, w); + try f.writeCValue(w, index, .other); + try w.writeAll("];"); + try f.newline(); return local; } fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const inst_ty = f.typeOfIndex(inst); const elem_ty = inst_ty.childType(zcu); - if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return .{ .undef = inst_ty }; + if (!elem_ty.hasRuntimeBits(zcu)) return .{ .undef = inst_ty }; const local = try f.allocLocalValue(.{ - .ctype = try f.ctypeFromType(elem_ty, .complete), - .alignas = CType.AlignAs.fromAlignment(.{ - .@"align" = inst_ty.ptrInfo(zcu).flags.alignment, - .abi = elem_ty.abiAlignment(zcu), - }), + .type = elem_ty, + .alignment = inst_ty.ptrInfo(zcu).flags.alignment, }); log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); try f.allocs.put(zcu.gpa, local.new_local, true); @@ -3810,11 +3113,11 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { // For packed aggregates, we zero-initialize to try and work around a design flaw // related to how `packed`, `undefined`, and RLS interact. See comment in `airStore` // for details. - const w = &f.object.code.writer; + const w = &f.code.writer; try w.print("memset(&t{d}, 0x00, sizeof(", .{local.new_local}); try f.renderType(w, elem_ty); try w.writeAll("));"); - try f.object.newline(); + try f.newline(); }, .auto, .@"extern" => {}, }, @@ -3825,18 +3128,15 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { } fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const inst_ty = f.typeOfIndex(inst); const elem_ty = inst_ty.childType(zcu); - if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return .{ .undef = inst_ty }; + if (!elem_ty.hasRuntimeBits(zcu)) return .{ .undef = inst_ty }; const local = try f.allocLocalValue(.{ - .ctype = try f.ctypeFromType(elem_ty, .complete), - .alignas = CType.AlignAs.fromAlignment(.{ - .@"align" = inst_ty.ptrInfo(zcu).flags.alignment, - .abi = elem_ty.abiAlignment(zcu), - }), + .type = elem_ty, + .alignment = inst_ty.ptrInfo(zcu).flags.alignment, }); log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); try f.allocs.put(zcu.gpa, local.new_local, true); @@ -3847,11 +3147,11 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { // For packed aggregates, we zero-initialize to try and work around a design flaw // related to how `packed`, `undefined`, and RLS interact. See comment in `airStore` // for details. - const w = &f.object.code.writer; + const w = &f.code.writer; try w.print("memset(&t{d}, 0x00, sizeof(", .{local.new_local}); try f.renderType(w, elem_ty); try w.writeAll("));"); - try f.object.newline(); + try f.newline(); }, .auto, .@"extern" => {}, }, @@ -3862,24 +3162,18 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { } fn airArg(f: *Function, inst: Air.Inst.Index) !CValue { - const inst_ty = f.typeOfIndex(inst); - const inst_ctype = try f.ctypeFromType(inst_ty, .parameter); - const i = f.next_arg_index; f.next_arg_index += 1; - const result: CValue = if (inst_ctype.eql(try f.ctypeFromType(inst_ty, .complete))) - .{ .arg = i } - else - .{ .arg_array = i }; + const result: CValue = .{ .arg = i }; if (f.liveness.isUnused(inst)) { - const w = &f.object.code.writer; + const w = &f.code.writer; try w.writeByte('('); try f.renderType(w, .void); try w.writeByte(')'); - try f.writeCValue(w, result, .Other); + try f.writeCValue(w, result, .other); try w.writeByte(';'); - try f.object.newline(); + try f.newline(); return .none; } @@ -3887,7 +3181,7 @@ fn airArg(f: *Function, inst: Air.Inst.Index) !CValue { } fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; @@ -3900,10 +3194,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { // bit-pointers we see here are vector element pointers. assert(ptr_info.packed_offset.host_size == 0 or ptr_info.flags.vector_index != .none); - if (!src_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - try reap(f, inst, &.{ty_op.operand}); - return .none; - } + assert(src_ty.hasRuntimeBits(zcu)); const operand = try f.resolveInst(ty_op.operand); @@ -3913,94 +3204,69 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { ptr_info.flags.alignment.order(src_ty.abiAlignment(zcu)).compare(.gte) else true; - const is_array = lowersToArray(src_ty, zcu); - const need_memcpy = !is_aligned or is_array; - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, src_ty); const v = try Vectorize.start(f, inst, w, ptr_ty); - if (need_memcpy) { - try w.writeAll("memcpy("); - if (!is_array) try w.writeByte('&'); - try f.writeCValue(w, local, .Other); + if (!is_aligned) { + try w.writeAll("memcpy(&"); + try f.writeCValue(w, local, .other); try v.elem(f, w); try w.writeAll(", (const char *)"); - try f.writeCValue(w, operand, .Other); + try f.writeCValue(w, operand, .other); try v.elem(f, w); try w.writeAll(", sizeof("); try f.renderType(w, src_ty); try w.writeAll("))"); } else { - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try v.elem(f, w); try w.writeAll(" = "); try f.writeCValueDeref(w, operand); try v.elem(f, w); } try w.writeByte(';'); - try f.object.newline(); + try f.newline(); try v.end(f, inst, w); return local; } fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !void { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const w = &f.object.code.writer; + const w = &f.code.writer; const op_inst = un_op.toIndex(); const op_ty = f.typeOf(un_op); const ret_ty = if (is_ptr) op_ty.childType(zcu) else op_ty; - const ret_ctype = try f.ctypeFromType(ret_ty, .parameter); if (op_inst != null and f.air.instructions.items(.tag)[@intFromEnum(op_inst.?)] == .call_always_tail) { try reap(f, inst, &.{un_op}); _ = try airCall(f, op_inst.?, .always_tail); - } else if (ret_ctype.index != .void) { + } else if (ret_ty.hasRuntimeBits(zcu)) { const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); - var deref = is_ptr; - const is_array = lowersToArray(ret_ty, zcu); - const ret_val = if (is_array) ret_val: { - const array_local = try f.allocAlignedLocal(inst, .{ - .ctype = ret_ctype, - .alignas = CType.AlignAs.fromAbiAlignment(ret_ty.abiAlignment(zcu)), - }); - try w.writeAll("memcpy("); - try f.writeCValueMember(w, array_local, .{ .identifier = "array" }); - try w.writeAll(", "); - if (deref) - try f.writeCValueDeref(w, operand) - else - try f.writeCValue(w, operand, .FunctionArgument); - deref = false; - try w.writeAll(", sizeof("); - try f.renderType(w, ret_ty); - try w.writeAll("));"); - try f.object.newline(); - break :ret_val array_local; - } else operand; try w.writeAll("return "); - if (deref) - try f.writeCValueDeref(w, ret_val) - else - try f.writeCValue(w, ret_val, .Other); - try w.writeAll(";\n"); - if (is_array) { - try freeLocal(f, inst, ret_val.new_local, null); + if (is_ptr) { + try f.writeCValueDeref(w, operand); + } else switch (operand) { + // Instead of 'return &local', emit 'return undefined'. + .local_ref => try f.dg.renderUndefValue(w, ret_ty, .other), + else => try f.writeCValue(w, operand, .other), } + try w.writeAll(";\n"); } else { try reap(f, inst, &.{un_op}); // Not even allowed to return void in a naked function. - if (!f.object.dg.is_naked_fn) try w.writeAll("return;\n"); + if (!f.dg.is_naked_fn) try w.writeAll("return;\n"); } } fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; @@ -4012,23 +3278,26 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { const operand_ty = f.typeOf(ty_op.operand); const scalar_ty = operand_ty.scalarType(zcu); - if (f.object.dg.intCastIsNoop(inst_scalar_ty, scalar_ty)) return f.moveCValue(inst, inst_ty, operand); + // `intCastIsNoop` doesn't apply to vectors because every vector lowers to a different C struct. + if (inst_ty.zigTypeTag(zcu) != .vector and f.dg.intCastIsNoop(inst_scalar_ty, scalar_ty)) { + return f.moveCValue(inst, inst_ty, operand); + } - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, operand_ty); - const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete)); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try v.elem(f, w); - try a.assign(f, w); - try f.renderIntCast(w, inst_scalar_ty, operand, v, scalar_ty, .Other); - try a.end(f, w); + try w.writeAll(" = "); + try f.renderIntCast(w, inst_scalar_ty, operand, v, scalar_ty, .other); + try w.writeByte(';'); + try f.newline(); try v.end(f, inst, w); return local; } fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; @@ -4050,13 +3319,12 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { const need_mask = dest_bits < 8 or !std.math.isPowerOfTwo(dest_bits); if (!need_cast and !need_lo and !need_mask) return f.moveCValue(inst, inst_ty, operand); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, operand_ty); - const a = try Assignment.start(f, w, try f.ctypeFromType(inst_scalar_ty, .complete)); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try v.elem(f, w); - try a.assign(f, w); + try w.writeAll(" = "); if (need_cast) { try w.writeByte('('); try f.renderType(w, inst_scalar_ty); @@ -4064,18 +3332,18 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { } if (need_lo) { try w.writeAll("zig_lo_"); - try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty); + try f.dg.renderTypeForBuiltinFnName(w, scalar_ty); try w.writeByte('('); } if (!need_mask) { - try f.writeCValue(w, operand, .Other); + try f.writeCValue(w, operand, .other); try v.elem(f, w); } else switch (dest_int_info.signedness) { .unsigned => { try w.writeAll("zig_and_"); - try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty); + try f.dg.renderTypeForBuiltinFnName(w, scalar_ty); try w.writeByte('('); - try f.writeCValue(w, operand, .FunctionArgument); + try f.writeCValue(w, operand, .other); try v.elem(f, w); try w.print(", {f})", .{ try f.fmtIntLiteralHex(try inst_scalar_ty.maxIntScalar(pt, scalar_ty)), @@ -4087,7 +3355,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { const shift_val = try pt.intValue(.u8, c_bits - dest_bits); try w.writeAll("zig_shr_"); - try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty); + try f.dg.renderTypeForBuiltinFnName(w, scalar_ty); if (c_bits == 128) { try w.print("(zig_bitCast_i{d}(", .{c_bits}); } else { @@ -4099,7 +3367,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { } else { try w.print("(uint{d}_t)", .{c_bits}); } - try f.writeCValue(w, operand, .FunctionArgument); + try f.writeCValue(w, operand, .other); try v.elem(f, w); if (c_bits == 128) try w.writeByte(')'); try w.print(", {f})", .{try f.fmtIntLiteralDec(shift_val)}); @@ -4108,13 +3376,14 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { }, } if (need_lo) try w.writeByte(')'); - try a.end(f, w); + try w.writeByte(';'); + try f.newline(); try v.end(f, inst, w); return local; } fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; // *a = b; const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; @@ -4132,7 +3401,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |v| v.isUndef(zcu) else false; - const w = &f.object.code.writer; + const w = &f.code.writer; if (val_is_undef) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); if (safety and ptr_info.packed_offset.host_size == 0) { @@ -4152,11 +3421,11 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { }, }; try w.writeAll("memset("); - try f.writeCValue(w, ptr_val, .FunctionArgument); + try f.writeCValue(w, ptr_val, .other); try w.print(", {s}, sizeof(", .{byte_str}); try f.renderType(w, .fromInterned(ptr_info.child)); try w.writeAll("));"); - try f.object.newline(); + try f.newline(); } return .none; } @@ -4165,46 +3434,29 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { ptr_info.flags.alignment.order(src_ty.abiAlignment(zcu)).compare(.gte) else true; - const is_array = lowersToArray(.fromInterned(ptr_info.child), zcu); - const need_memcpy = !is_aligned or is_array; const src_val = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - const src_scalar_ctype = try f.ctypeFromType(src_ty.scalarType(zcu), .complete); - if (need_memcpy) { - // For this memcpy to safely work we need the rhs to have the same - // underlying type as the lhs (i.e. they must both be arrays of the same underlying type). - assert(src_ty.eql(.fromInterned(ptr_info.child), zcu)); - - // If the source is a constant, writeCValue will emit a brace initialization - // so work around this by initializing into new local. - // TODO this should be done by manually initializing elements of the dest array - const array_src = if (src_val == .constant) blk: { - const new_local = try f.allocLocal(inst, src_ty); - try f.writeCValue(w, new_local, .Other); - try w.writeAll(" = "); - try f.writeCValue(w, src_val, .Other); - try w.writeByte(';'); - try f.object.newline(); - - break :blk new_local; - } else src_val; + if (!is_aligned) { + // For this memcpy to safely work we need the rhs to have the same + // underlying type as the lhs (i.e. they must both be arrays of the same underlying type). + assert(src_ty.eql(.fromInterned(ptr_info.child), zcu)); const v = try Vectorize.start(f, inst, w, ptr_ty); try w.writeAll("memcpy((char *)"); - try f.writeCValue(w, ptr_val, .FunctionArgument); + try f.writeCValue(w, ptr_val, .other); try v.elem(f, w); - try w.writeAll(", "); - if (!is_array) try w.writeByte('&'); - try f.writeCValue(w, array_src, .FunctionArgument); + try w.writeAll(", &"); + switch (src_val) { + .constant => |val| try f.dg.renderValueAsLvalue(w, val), + else => try f.writeCValue(w, src_val, .other), + } try v.elem(f, w); try w.writeAll(", sizeof("); try f.renderType(w, src_ty); - try w.writeAll("))"); - try f.freeCValue(inst, array_src); - try w.writeByte(';'); - try f.object.newline(); + try w.writeAll("));"); + try f.newline(); try v.end(f, inst, w); } else { switch (ptr_val) { @@ -4216,20 +3468,20 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { else => {}, } const v = try Vectorize.start(f, inst, w, ptr_ty); - const a = try Assignment.start(f, w, src_scalar_ctype); try f.writeCValueDeref(w, ptr_val); try v.elem(f, w); - try a.assign(f, w); - try f.writeCValue(w, src_val, .Other); + try w.writeAll(" = "); + try f.writeCValue(w, src_val, .other); try v.elem(f, w); - try a.end(f, w); + try w.writeByte(';'); + try f.newline(); try v.end(f, inst, w); } return .none; } fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: BuiltinInfo) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; @@ -4242,7 +3494,9 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: const operand_ty = f.typeOf(bin_op.lhs); const scalar_ty = operand_ty.scalarType(zcu); - const w = &f.object.code.writer; + const ref_arg = lowersToBigInt(scalar_ty, zcu); + + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, operand_ty); try f.writeCValueMember(w, local, .{ .field = 1 }); @@ -4250,26 +3504,28 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: try w.writeAll(" = zig_"); try w.writeAll(operation); try w.writeAll("o_"); - try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty); + try f.dg.renderTypeForBuiltinFnName(w, scalar_ty); try w.writeAll("(&"); try f.writeCValueMember(w, local, .{ .field = 0 }); try v.elem(f, w); try w.writeAll(", "); - try f.writeCValue(w, lhs, .FunctionArgument); + if (ref_arg) try w.writeByte('&'); + try f.writeCValue(w, lhs, .other); try v.elem(f, w); try w.writeAll(", "); - try f.writeCValue(w, rhs, .FunctionArgument); + if (ref_arg) try w.writeByte('&'); + try f.writeCValue(w, rhs, .other); if (f.typeOf(bin_op.rhs).isVector(zcu)) try v.elem(f, w); - try f.object.dg.renderBuiltinInfo(w, scalar_ty, info); + try f.dg.renderBuiltinInfo(w, scalar_ty, info); try w.writeAll(");"); - try f.object.newline(); + try f.newline(); try v.end(f, inst, w); return local; } fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_ty = f.typeOf(ty_op.operand); @@ -4281,17 +3537,17 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.typeOfIndex(inst); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, operand_ty); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try v.elem(f, w); try w.writeAll(" = "); try w.writeByte('!'); - try f.writeCValue(w, op, .Other); + try f.writeCValue(w, op, .other); try v.elem(f, w); try w.writeByte(';'); - try f.object.newline(); + try f.newline(); try v.end(f, inst, w); return local; @@ -4304,7 +3560,7 @@ fn airBinOp( operation: []const u8, info: BuiltinInfo, ) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const operand_ty = f.typeOf(bin_op.lhs); @@ -4318,21 +3574,21 @@ fn airBinOp( const inst_ty = f.typeOfIndex(inst); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, operand_ty); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try v.elem(f, w); try w.writeAll(" = "); - try f.writeCValue(w, lhs, .Other); + try f.writeCValue(w, lhs, .other); try v.elem(f, w); try w.writeByte(' '); try w.writeAll(operator); try w.writeByte(' '); - try f.writeCValue(w, rhs, .Other); + try f.writeCValue(w, rhs, .other); try v.elem(f, w); try w.writeByte(';'); - try f.object.newline(); + try f.newline(); try v.end(f, inst, w); return local; @@ -4344,7 +3600,7 @@ fn airCmpOp( data: anytype, operator: std.math.CompareOperator, ) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const lhs_ty = f.typeOf(data.lhs); const scalar_ty = lhs_ty.scalarType(zcu); @@ -4369,26 +3625,26 @@ fn airCmpOp( const rhs_ty = f.typeOf(data.rhs); const need_cast = lhs_ty.isSinglePointer(zcu) or rhs_ty.isSinglePointer(zcu); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, lhs_ty); - const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete)); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try v.elem(f, w); - try a.assign(f, w); + try w.writeAll(" = "); if (lhs != .undef and lhs.eql(rhs)) try w.writeAll(switch (operator) { .lt, .neq, .gt => "false", .lte, .eq, .gte => "true", }) else { if (need_cast) try w.writeAll("(void*)"); - try f.writeCValue(w, lhs, .Other); + try f.writeCValue(w, lhs, .other); try v.elem(f, w); try w.writeAll(compareOperatorC(operator)); if (need_cast) try w.writeAll("(void*)"); - try f.writeCValue(w, rhs, .Other); + try f.writeCValue(w, rhs, .other); try v.elem(f, w); } - try a.end(f, w); + try w.writeByte(';'); + try f.newline(); try v.end(f, inst, w); return local; @@ -4399,9 +3655,8 @@ fn airEquality( inst: Air.Inst.Index, operator: std.math.CompareOperator, ) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; - const ctype_pool = &f.object.dg.ctype_pool; const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const operand_ty = f.typeOf(bin_op.lhs); @@ -4422,54 +3677,64 @@ fn airEquality( const rhs = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - const w = &f.object.code.writer; + if (lhs.eql(rhs)) { + // Avoid emitting a tautological comparison. + return .{ .constant = .makeBool(switch (operator) { + .eq, .lte, .gte => true, + .neq, .lt, .gt => false, + }) }; + } + + const w = &f.code.writer; const local = try f.allocLocal(inst, .bool); - const a = try Assignment.start(f, w, .bool); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); + try f.writeCValue(w, local, .other); + try w.writeAll(" = "); - const operand_ctype = try f.ctypeFromType(operand_ty, .complete); - if (lhs != .undef and lhs.eql(rhs)) try w.writeAll(switch (operator) { - .lt, .lte, .gte, .gt => unreachable, - .neq => "false", - .eq => "true", - }) else switch (operand_ctype.info(ctype_pool)) { - .basic, .pointer => { - try f.writeCValue(w, lhs, .Other); - try w.writeAll(compareOperatorC(operator)); - try f.writeCValue(w, rhs, .Other); - }, - .aligned, .array, .vector, .fwd_decl, .function => unreachable, - .aggregate => |aggregate| if (aggregate.fields.len == 2 and - (aggregate.fields.at(0, ctype_pool).name.index == .is_null or - aggregate.fields.at(1, ctype_pool).name.index == .is_null)) - { - try f.writeCValueMember(w, lhs, .{ .identifier = "is_null" }); - try w.writeAll(" || "); - try f.writeCValueMember(w, rhs, .{ .identifier = "is_null" }); - try w.writeAll(" ? "); - try f.writeCValueMember(w, lhs, .{ .identifier = "is_null" }); - try w.writeAll(compareOperatorC(operator)); - try f.writeCValueMember(w, rhs, .{ .identifier = "is_null" }); - try w.writeAll(" : "); - try f.writeCValueMember(w, lhs, .{ .identifier = "payload" }); - try w.writeAll(compareOperatorC(operator)); - try f.writeCValueMember(w, rhs, .{ .identifier = "payload" }); - } else for (0..aggregate.fields.len) |field_index| { - if (field_index > 0) try w.writeAll(switch (operator) { - .lt, .lte, .gte, .gt => unreachable, - .eq => " && ", - .neq => " || ", - }); - const field_name: CValue = .{ - .ctype_pool_string = aggregate.fields.at(field_index, ctype_pool).name, - }; - try f.writeCValueMember(w, lhs, field_name); - try w.writeAll(compareOperatorC(operator)); - try f.writeCValueMember(w, rhs, field_name); + switch (operand_ty.zigTypeTag(zcu)) { + .optional => switch (CType.classifyOptional(operand_ty, zcu)) { + .npv_payload => unreachable, // opv optional + + .error_set, .ptr_like => {}, + + .slice_like => unreachable, // equality is not defined on slices + + .opv_payload => { + try f.writeCValueMember(w, lhs, .{ .identifier = "is_null" }); + try w.writeAll(compareOperatorC(operator)); + try f.writeCValueMember(w, rhs, .{ .identifier = "is_null" }); + try w.writeByte(';'); + try f.newline(); + return local; + }, + + .@"struct" => { + // `lhs.is_null || rhs.is_null ? lhs.is_null == rhs.is_null : lhs.payload == rhs.payload` + try f.writeCValueMember(w, lhs, .{ .identifier = "is_null" }); + try w.writeAll(" || "); + try f.writeCValueMember(w, rhs, .{ .identifier = "is_null" }); + try w.writeAll(" ? "); + try f.writeCValueMember(w, lhs, .{ .identifier = "is_null" }); + try w.writeAll(compareOperatorC(operator)); + try f.writeCValueMember(w, rhs, .{ .identifier = "is_null" }); + try w.writeAll(" : "); + try f.writeCValueMember(w, lhs, .{ .identifier = "payload" }); + try w.writeAll(compareOperatorC(operator)); + try f.writeCValueMember(w, rhs, .{ .identifier = "payload" }); + try w.writeByte(';'); + try f.newline(); + return local; + }, }, + .bool, .int, .pointer, .@"enum", .error_set => {}, + .@"struct", .@"union" => assert(operand_ty.containerLayout(zcu) == .@"packed"), + else => unreachable, } - try a.end(f, w); + + try f.writeCValue(w, lhs, .other); + try w.writeAll(compareOperatorC(operator)); + try f.writeCValue(w, rhs, .other); + try w.writeByte(';'); + try f.newline(); return local; } @@ -4480,18 +3745,18 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, .bool); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(" = "); - try f.writeCValue(w, operand, .Other); + try f.writeCValue(w, operand, .other); try w.print(" < sizeof({f}) / sizeof(*{0f});", .{fmtIdentSolo("zig_errorName")}); - try f.object.newline(); + try f.newline(); return local; } fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; @@ -4502,40 +3767,36 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { const inst_ty = f.typeOfIndex(inst); const inst_scalar_ty = inst_ty.scalarType(zcu); - const elem_ty = inst_scalar_ty.elemType2(zcu); - if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) return f.moveCValue(inst, inst_ty, lhs); - const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete); + const elem_ty = inst_scalar_ty.indexableElem(zcu); + assert(elem_ty.hasRuntimeBits(zcu)); const local = try f.allocLocal(inst, inst_ty); - const w = &f.object.code.writer; + const w = &f.code.writer; const v = try Vectorize.start(f, inst, w, inst_ty); - const a = try Assignment.start(f, w, inst_scalar_ctype); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try v.elem(f, w); - try a.assign(f, w); + try w.writeAll(" = "); // We must convert to and from integer types to prevent UB if the operation // results in a NULL pointer, or if LHS is NULL. The operation is only UB // if the result is NULL and then dereferenced. try w.writeByte('('); - try f.renderCType(w, inst_scalar_ctype); + try f.renderType(w, inst_scalar_ty); try w.writeAll(")(((uintptr_t)"); - try f.writeCValue(w, lhs, .Other); + try f.writeCValue(w, lhs, .other); try v.elem(f, w); - try w.writeAll(") "); - try w.writeByte(operator); - try w.writeAll(" ("); - try f.writeCValue(w, rhs, .Other); + try w.print(") {c} (", .{operator}); + try f.writeCValue(w, rhs, .other); try v.elem(f, w); try w.writeAll("*sizeof("); try f.renderType(w, elem_ty); - try w.writeAll(")))"); - try a.end(f, w); + try w.writeAll(")));"); + try f.newline(); try v.end(f, inst, w); return local; } fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []const u8) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; @@ -4549,36 +3810,34 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons const rhs = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, inst_ty); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try v.elem(f, w); // (lhs <> rhs) ? lhs : rhs try w.writeAll(" = ("); - try f.writeCValue(w, lhs, .Other); + try f.writeCValue(w, lhs, .other); try v.elem(f, w); try w.writeByte(' '); try w.writeByte(operator); try w.writeByte(' '); - try f.writeCValue(w, rhs, .Other); + try f.writeCValue(w, rhs, .other); try v.elem(f, w); try w.writeAll(") ? "); - try f.writeCValue(w, lhs, .Other); + try f.writeCValue(w, lhs, .other); try v.elem(f, w); try w.writeAll(" : "); - try f.writeCValue(w, rhs, .Other); + try f.writeCValue(w, rhs, .other); try v.elem(f, w); try w.writeByte(';'); - try f.object.newline(); + try f.newline(); try v.end(f, inst, w); return local; } fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; - const zcu = pt.zcu; const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; @@ -4587,24 +3846,22 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const inst_ty = f.typeOfIndex(inst); - const ptr_ty = inst_ty.slicePtrFieldType(zcu); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); - { - const a = try Assignment.start(f, w, try f.ctypeFromType(ptr_ty, .complete)); - try f.writeCValueMember(w, local, .{ .identifier = "ptr" }); - try a.assign(f, w); - try f.writeCValue(w, ptr, .Other); - try a.end(f, w); - } - { - const a = try Assignment.start(f, w, .usize); - try f.writeCValueMember(w, local, .{ .identifier = "len" }); - try a.assign(f, w); - try f.writeCValue(w, len, .Other); - try a.end(f, w); - } + + try f.writeCValueMember(w, local, .{ .identifier = "ptr" }); + try w.writeAll(" = "); + try f.writeCValue(w, ptr, .other); + try w.writeByte(';'); + try f.newline(); + + try f.writeCValueMember(w, local, .{ .identifier = "len" }); + try w.writeAll(" = "); + try f.writeCValue(w, len, .other); + try w.writeByte(';'); + try f.newline(); + return local; } @@ -4613,14 +3870,14 @@ fn airCall( inst: Air.Inst.Index, modifier: std.builtin.CallModifier, ) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; // Not even allowed to call panic in a naked function. - if (f.object.dg.is_naked_fn) return .none; + if (f.dg.is_naked_fn) return .none; - const gpa = f.object.dg.gpa; - const w = &f.object.code.writer; + const gpa = f.dg.gpa; + const w = &f.code.writer; const call = f.air.unwrapCall(inst); const args = call.args; @@ -4629,27 +3886,11 @@ fn airCall( defer gpa.free(resolved_args); for (resolved_args, args) |*resolved_arg, arg| { const arg_ty = f.typeOf(arg); - const arg_ctype = try f.ctypeFromType(arg_ty, .parameter); - if (arg_ctype.index == .void) { + if (!arg_ty.hasRuntimeBits(zcu)) { resolved_arg.* = .none; continue; } resolved_arg.* = try f.resolveInst(arg); - if (!arg_ctype.eql(try f.ctypeFromType(arg_ty, .complete))) { - const array_local = try f.allocAlignedLocal(inst, .{ - .ctype = arg_ctype, - .alignas = CType.AlignAs.fromAbiAlignment(arg_ty.abiAlignment(zcu)), - }); - try w.writeAll("memcpy("); - try f.writeCValueMember(w, array_local, .{ .identifier = "array" }); - try w.writeAll(", "); - try f.writeCValue(w, resolved_arg.*, .FunctionArgument); - try w.writeAll(", sizeof("); - try f.renderCType(w, arg_ctype); - try w.writeAll("));"); - try f.object.newline(); - resolved_arg.* = array_local; - } } const callee = try f.resolveInst(call.callee); @@ -4668,28 +3909,22 @@ fn airCall( }; const fn_info = zcu.typeToFunc(if (callee_is_ptr) callee_ty.childType(zcu) else callee_ty).?; const ret_ty: Type = .fromInterned(fn_info.return_type); - const ret_ctype: CType = if (ret_ty.isNoReturn(zcu)) - .void - else - try f.ctypeFromType(ret_ty, .parameter); const result_local = result: { if (modifier == .always_tail) { try w.writeAll("zig_always_tail return "); break :result .none; - } else if (ret_ctype.index == .void) { + } else if (!ret_ty.hasRuntimeBits(zcu)) { break :result .none; } else if (f.liveness.isUnused(inst)) { - try w.writeByte('('); - try f.renderCType(w, .void); - try w.writeByte(')'); + try w.writeAll("(void)"); break :result .none; } else { const local = try f.allocAlignedLocal(inst, .{ - .ctype = ret_ctype, - .alignas = CType.AlignAs.fromAbiAlignment(ret_ty.abiAlignment(zcu)), + .type = ret_ty, + .alignment = .none, }); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(" = "); break :result local; } @@ -4716,8 +3951,19 @@ fn airCall( if (!callee_is_ptr) try w.writeByte('&'); } switch (modifier) { - .auto, .always_tail => try f.object.dg.renderNavName(w, fn_nav), - inline .never_tail, .never_inline => |m| try w.writeAll(try f.getLazyFnName(@unionInit(LazyFnKey, @tagName(m), fn_nav))), + .auto, .always_tail => try renderNavName(w, fn_nav, ip), + .never_tail => { + try f.need_never_tail_funcs.put(gpa, fn_nav, {}); + try w.print("zig_never_tail_{f}__{d}", .{ + fmtIdentUnsolo(ip.getNav(fn_nav).name.toSlice(ip)), @intFromEnum(fn_nav), + }); + }, + .never_inline => { + try f.need_never_inline_funcs.put(gpa, fn_nav, {}); + try w.print("zig_never_inline_{f}__{d}", .{ + fmtIdentUnsolo(ip.getNav(fn_nav).name.toSlice(ip)), @intFromEnum(fn_nav), + }); + }, else => unreachable, } if (need_cast) try w.writeByte(')'); @@ -4730,7 +3976,7 @@ fn airCall( else => unreachable, } // Fall back to function pointer call. - try f.writeCValue(w, callee, .Other); + try f.writeCValue(w, callee, .other); } try w.writeByte('('); @@ -4739,38 +3985,20 @@ fn airCall( if (resolved_arg == .none) continue; if (need_comma) try w.writeAll(", "); need_comma = true; - try f.writeCValue(w, resolved_arg, .FunctionArgument); - try f.freeCValue(inst, resolved_arg); + try f.writeCValue(w, resolved_arg, .other); } try w.writeAll(");"); switch (modifier) { .always_tail => try w.writeByte('\n'), - else => try f.object.newline(), + else => try f.newline(), } - const result = result: { - if (result_local == .none or !lowersToArray(ret_ty, zcu)) - break :result result_local; - - const array_local = try f.allocLocal(inst, ret_ty); - try w.writeAll("memcpy("); - try f.writeCValue(w, array_local, .FunctionArgument); - try w.writeAll(", "); - try f.writeCValueMember(w, result_local, .{ .identifier = "array" }); - try w.writeAll(", sizeof("); - try f.renderType(w, ret_ty); - try w.writeAll("));"); - try f.object.newline(); - try freeLocal(f, inst, result_local.new_local, null); - break :result array_local; - }; - - return result; + return result_local; } fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue { const dbg_stmt = f.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; - const w = &f.object.code.writer; + const w = &f.code.writer; // TODO re-evaluate whether to emit these or not. If we naively emit // these directives, the output file will report bogus line numbers because // every newline after the #line directive adds one to the line. @@ -4779,32 +4007,32 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue { // newlines until the next dbg_stmt occurs. // Perhaps an additional compilation option is in order? //try w.print("#line {d}", .{dbg_stmt.line + 1}); - //try f.object.newline(); + //try f.newline(); try w.print("/* file:{d}:{d} */", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 }); - try f.object.newline(); + try f.newline(); return .none; } fn airDbgEmptyStmt(f: *Function, _: Air.Inst.Index) !CValue { - try f.object.code.writer.writeAll("(void)0;"); - try f.object.newline(); + try f.code.writer.writeAll("(void)0;"); + try f.newline(); return .none; } fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; const block = f.air.unwrapDbgBlock(inst); const owner_nav = ip.getNav(zcu.funcInfo(block.func).owner_nav); - const w = &f.object.code.writer; + const w = &f.code.writer; try w.print("/* inline:{f} */", .{owner_nav.fqn.fmt(&zcu.intern_pool)}); - try f.object.newline(); + try f.newline(); return lowerBlock(f, inst, block.body); } fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const tag = f.air.instructions.items(.tag)[@intFromEnum(inst)]; const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; @@ -4813,9 +4041,9 @@ fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand); try reap(f, inst, &.{pl_op.operand}); - const w = &f.object.code.writer; + const w = &f.code.writer; try w.print("/* {s}:{s} */", .{ @tagName(tag), name.toSlice(f.air) }); - try f.object.newline(); + try f.newline(); return .none; } @@ -4825,21 +4053,21 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { } fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const liveness_block = f.liveness.getBlock(inst); const block_id = f.next_block_index; f.next_block_index += 1; - const w = &f.object.code.writer; + const w = &f.code.writer; const inst_ty = f.typeOfIndex(inst); - const result = if (inst_ty.hasRuntimeBitsIgnoreComptime(zcu) and !f.liveness.isUnused(inst)) + const result = if (inst_ty.hasRuntimeBits(zcu) and !f.liveness.isUnused(inst)) try f.allocLocal(inst, inst_ty) else .none; - try f.blocks.putNoClobber(f.object.dg.gpa, inst, .{ + try f.blocks.putNoClobber(f.dg.gpa, inst, .{ .block_id = block_id, .result = result, }); @@ -4854,23 +4082,23 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index) } // noreturn blocks have no `br` instructions reaching them, so we don't want a label - if (f.object.dg.is_naked_fn) { - if (f.object.dg.expected_block) |expected_block| { + if (f.dg.is_naked_fn) { + if (f.dg.expected_block) |expected_block| { if (block_id != expected_block) return f.fail("runtime code not allowed in naked function", .{}); - f.object.dg.expected_block = null; + f.dg.expected_block = null; } } else if (!f.typeOfIndex(inst).isNoReturn(zcu)) { // label must be followed by an expression, include an empty one. try w.print("\nzig_block_{d}:;", .{block_id}); - try f.object.newline(); + try f.newline(); } return result; } fn airTry(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const unwrapped_try = f.air.unwrapTry(inst); const body = unwrapped_try.else_body; const err_union_ty = f.air.typeOf(unwrapped_try.error_union, &pt.zcu.intern_pool); @@ -4878,7 +4106,7 @@ fn airTry(f: *Function, inst: Air.Inst.Index) !CValue { } fn airTryPtr(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const unwrapped_try = f.air.unwrapTryPtr(inst); const body = unwrapped_try.else_body; const err_union_ty = f.air.typeOf(unwrapped_try.error_union_ptr, &pt.zcu.intern_pool).childType(pt.zcu); @@ -4893,46 +4121,38 @@ fn lowerTry( err_union_ty: Type, is_ptr: bool, ) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const err_union = try f.resolveInst(operand); const inst_ty = f.typeOfIndex(inst); const liveness_condbr = f.liveness.getCondBr(inst); - const w = &f.object.code.writer; + const w = &f.code.writer; const payload_ty = err_union_ty.errorUnionPayload(zcu); - const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(zcu); - if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) { - try w.writeAll("if ("); - if (!payload_has_bits) { - if (is_ptr) - try f.writeCValueDeref(w, err_union) - else - try f.writeCValue(w, err_union, .Other); - } else { - // Reap the operand so that it can be reused inside genBody. - // Remember we must avoid calling reap() twice for the same operand - // in this function. - try reap(f, inst, &.{operand}); - if (is_ptr) - try f.writeCValueDerefMember(w, err_union, .{ .identifier = "error" }) - else - try f.writeCValueMember(w, err_union, .{ .identifier = "error" }); - } - try w.writeAll(") "); + try w.writeAll("if ("); - try genBodyResolveState(f, inst, liveness_condbr.else_deaths, body, false); - try f.object.newline(); - if (f.object.dg.expected_block) |_| - return f.fail("runtime code not allowed in naked function", .{}); - } + // Reap the operand so that it can be reused inside genBody. + // Remember we must avoid calling reap() twice for the same operand + // in this function. + try reap(f, inst, &.{operand}); + if (is_ptr) + try f.writeCValueDerefMember(w, err_union, .{ .identifier = "error" }) + else + try f.writeCValueMember(w, err_union, .{ .identifier = "error" }); + + try w.writeAll(") "); + + try genBodyResolveState(f, inst, liveness_condbr.else_deaths, body, false); + try f.newline(); + if (f.dg.expected_block) |_| + return f.fail("runtime code not allowed in naked function", .{}); // Now we have the "then branch" (in terms of the liveness data); process any deaths. for (liveness_condbr.then_deaths) |death| { try die(f, inst, death.toRef()); } - if (!payload_has_bits) { + if (!payload_ty.hasRuntimeBits(zcu)) { if (!is_ptr) { return .none; } else { @@ -4945,14 +4165,14 @@ fn lowerTry( if (f.liveness.isUnused(inst)) return .none; const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); + try f.writeCValue(w, local, .other); + try w.writeAll(" = "); if (is_ptr) { try w.writeByte('&'); try f.writeCValueDerefMember(w, err_union, .{ .identifier = "payload" }); } else try f.writeCValueMember(w, err_union, .{ .identifier = "payload" }); - try a.end(f, w); + try w.writeByte(';'); + try f.newline(); return local; } @@ -4960,25 +4180,24 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void { const branch = f.air.instructions.items(.data)[@intFromEnum(inst)].br; const block = f.blocks.get(branch.block_inst).?; const result = block.result; - const w = &f.object.code.writer; + const w = &f.code.writer; - if (f.object.dg.is_naked_fn) { + if (f.dg.is_naked_fn) { if (result != .none) return f.fail("runtime code not allowed in naked function", .{}); - f.object.dg.expected_block = block.block_id; + f.dg.expected_block = block.block_id; return; } // If result is .none then the value of the block is unused. if (result != .none) { - const operand_ty = f.typeOf(branch.operand); const operand = try f.resolveInst(branch.operand); try reap(f, inst, &.{branch.operand}); - const a = try Assignment.start(f, w, try f.ctypeFromType(operand_ty, .complete)); - try f.writeCValue(w, result, .Other); - try a.assign(f, w); - try f.writeCValue(w, operand, .Other); - try a.end(f, w); + try f.writeCValue(w, result, .other); + try w.writeAll(" = "); + try f.writeCValue(w, operand, .other); + try w.writeByte(';'); + try f.newline(); } try w.print("goto zig_block_{d};\n", .{block.block_id}); @@ -4986,14 +4205,14 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void { fn airRepeat(f: *Function, inst: Air.Inst.Index) !void { const repeat = f.air.instructions.items(.data)[@intFromEnum(inst)].repeat; - try f.object.code.writer.print("goto zig_loop_{d};\n", .{@intFromEnum(repeat.loop_inst)}); + try f.code.writer.print("goto zig_loop_{d};\n", .{@intFromEnum(repeat.loop_inst)}); } fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const br = f.air.instructions.items(.data)[@intFromEnum(inst)].br; - const w = &f.object.code.writer; + const w = &f.code.writer; if (try f.air.value(br.operand, pt)) |cond_val| { // Comptime-known dispatch. Iterate the cases to find the correct @@ -5022,11 +4241,11 @@ fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void { // Runtime-known dispatch. Set the switch condition, and branch back. const cond = try f.resolveInst(br.operand); const cond_local = f.loop_switch_conds.get(br.block_inst).?; - try f.writeCValue(w, .{ .local = cond_local }, .Other); + try f.writeCValue(w, .{ .local = cond_local }, .other); try w.writeAll(" = "); - try f.writeCValue(w, cond, .Other); + try f.writeCValue(w, cond, .other); try w.writeByte(';'); - try f.object.newline(); + try f.newline(); try w.print("goto zig_switch_{d}_loop;\n", .{@intFromEnum(br.block_inst)}); } @@ -5043,11 +4262,10 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { } fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; - const target = &f.object.dg.mod.resolved_target.result; - const ctype_pool = &f.object.dg.ctype_pool; - const w = &f.object.code.writer; + const target = &f.dg.mod.resolved_target.result; + const w = &f.code.writer; if (operand_ty.isAbiInt(zcu) and dest_ty.isAbiInt(zcu)) { const src_info = dest_ty.intInfo(zcu); @@ -5058,26 +4276,16 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal if (dest_ty.isPtrAtRuntime(zcu) or operand_ty.isPtrAtRuntime(zcu)) { const local = try f.allocLocal(null, dest_ty); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(" = ("); try f.renderType(w, dest_ty); try w.writeByte(')'); - try f.writeCValue(w, operand, .Other); + try f.writeCValue(w, operand, .other); try w.writeByte(';'); - try f.object.newline(); + try f.newline(); return local; } - const operand_lval = if (operand == .constant) blk: { - const operand_local = try f.allocLocal(null, operand_ty); - try f.writeCValue(w, operand_local, .Other); - try w.writeAll(" = "); - try f.writeCValue(w, operand, .Other); - try w.writeByte(';'); - try f.object.newline(); - break :blk operand_local; - } else operand; - const local = try f.allocLocal(null, dest_ty); // On big-endian targets, copying ABI integers with padding bits is awkward, because the padding bits are at the low bytes of the value. // We need to offset the source or destination pointer appropriately and copy the right number of bytes. @@ -5085,141 +4293,134 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal // e.g. [10]u8 -> u80. We need to offset the destination so that we copy to the least significant bits of the integer. const offset = dest_ty.abiSize(zcu) - operand_ty.abiSize(zcu); try w.writeAll("memcpy((char *)&"); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.print(" + {d}, &", .{offset}); - try f.writeCValue(w, operand_lval, .Other); + switch (operand) { + .constant => |val| try f.dg.renderValueAsLvalue(w, val), + else => try f.writeCValue(w, operand, .other), + } try w.print(", {d});", .{operand_ty.abiSize(zcu)}); } else if (target.cpu.arch.endian() == .big and operand_ty.isAbiInt(zcu) and !dest_ty.isAbiInt(zcu)) { // e.g. u80 -> [10]u8. We need to offset the source so that we copy from the least significant bits of the integer. const offset = operand_ty.abiSize(zcu) - dest_ty.abiSize(zcu); try w.writeAll("memcpy(&"); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(", (const char *)&"); - try f.writeCValue(w, operand_lval, .Other); + switch (operand) { + .constant => |val| try f.dg.renderValueAsLvalue(w, val), + else => try f.writeCValue(w, operand, .other), + } try w.print(" + {d}, {d});", .{ offset, dest_ty.abiSize(zcu) }); } else { try w.writeAll("memcpy(&"); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(", &"); - try f.writeCValue(w, operand_lval, .Other); + switch (operand) { + .constant => |val| try f.dg.renderValueAsLvalue(w, val), + else => try f.writeCValue(w, operand, .other), + } try w.print(", {d});", .{@min(dest_ty.abiSize(zcu), operand_ty.abiSize(zcu))}); } - try f.object.newline(); + try f.newline(); // Ensure padding bits have the expected value. if (dest_ty.isAbiInt(zcu)) { - const dest_ctype = try f.ctypeFromType(dest_ty, .complete); - const dest_info = dest_ty.intInfo(zcu); - var bits: u16 = dest_info.bits; - var wrap_ctype: ?CType = null; - var need_bitcasts = false; - - try f.writeCValue(w, local, .Other); - switch (dest_ctype.info(ctype_pool)) { - else => {}, - .array => |array_info| { - try w.print("[{d}]", .{switch (target.cpu.arch.endian()) { - .little => array_info.len - 1, - .big => 0, - }}); - wrap_ctype = array_info.elem_ctype.toSignedness(dest_info.signedness); - need_bitcasts = wrap_ctype.?.index == .zig_i128; - bits -= 1; - bits %= @as(u16, @intCast(f.byteSize(array_info.elem_ctype) * 8)); - bits += 1; + switch (CType.classifyInt(dest_ty, zcu)) { + .void => unreachable, // opv + .small => { + try f.writeCValue(w, local, .other); + try w.writeAll(" = zig_wrap_"); + try f.dg.renderTypeForBuiltinFnName(w, dest_ty); + try w.writeByte('('); + try f.writeCValue(w, local, .other); + try f.dg.renderBuiltinInfo(w, dest_ty, .bits); + try w.writeAll(");"); + try f.newline(); }, - } - try w.writeAll(" = "); - if (need_bitcasts) { - try w.writeAll("zig_bitCast_"); - try f.object.dg.renderCTypeForBuiltinFnName(w, wrap_ctype.?.toUnsigned()); - try w.writeByte('('); - } - try w.writeAll("zig_wrap_"); - const info_ty = try pt.intType(dest_info.signedness, bits); - if (wrap_ctype) |ctype| - try f.object.dg.renderCTypeForBuiltinFnName(w, ctype) - else - try f.object.dg.renderTypeForBuiltinFnName(w, info_ty); - try w.writeByte('('); - if (need_bitcasts) { - try w.writeAll("zig_bitCast_"); - try f.object.dg.renderCTypeForBuiltinFnName(w, wrap_ctype.?); - try w.writeByte('('); - } - try f.writeCValue(w, local, .Other); - switch (dest_ctype.info(ctype_pool)) { - else => {}, - .array => |array_info| try w.print("[{d}]", .{ - switch (target.cpu.arch.endian()) { - .little => array_info.len - 1, + .big => |big| { + const dest_info = dest_ty.intInfo(zcu); + const padding_index: u16 = switch (target.cpu.arch.endian()) { + .little => big.limbs_len - 1, .big => 0, - }, - }), + }; + const wrap_bits = ((dest_info.bits - 1) % big.limb_size.bits()) + 1; + if (big.limb_size != .@"128" or dest_info.signedness == .unsigned) { + try f.writeCValueMember(w, local, .{ .identifier = "limbs" }); + try w.print("[{d}] = zig_wrap_{c}{d}(", .{ + padding_index, + signAbbrev(dest_info.signedness), + big.limb_size.bits(), + }); + try f.writeCValueMember(w, local, .{ .identifier = "limbs" }); + try w.print("[{d}], {d});", .{ padding_index, wrap_bits }); + } else { + try f.writeCValueMember(w, local, .{ .identifier = "limbs" }); + try w.print("[{d}] = zig_bitCast_u128(zig_wrap_i128(zig_bitCast_i128(", .{ + padding_index, + }); + try f.writeCValueMember(w, local, .{ .identifier = "limbs" }); + try w.print("[{d}]), {d}));", .{ padding_index, wrap_bits }); + try f.newline(); + } + }, } - if (need_bitcasts) try w.writeByte(')'); - try f.object.dg.renderBuiltinInfo(w, info_ty, .bits); - if (need_bitcasts) try w.writeByte(')'); - try w.writeAll(");"); - try f.object.newline(); } - try f.freeCValue(null, operand_lval); return local; } -fn airTrap(f: *Function, w: *Writer) !void { +fn airTrap(f: *Function) !void { // Not even allowed to call trap in a naked function. - if (f.object.dg.is_naked_fn) return; - try w.writeAll("zig_trap();\n"); + if (f.dg.is_naked_fn) return; + try f.code.writer.writeAll("zig_trap();\n"); } fn airBreakpoint(f: *Function) !CValue { - const w = &f.object.code.writer; + const w = &f.code.writer; try w.writeAll("zig_breakpoint();"); - try f.object.newline(); + try f.newline(); return .none; } fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, .usize); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(" = ("); try f.renderType(w, .usize); try w.writeAll(")zig_return_address();"); - try f.object.newline(); + try f.newline(); return local; } fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue { - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, .usize); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(" = ("); try f.renderType(w, .usize); try w.writeAll(")zig_frame_address();"); - try f.object.newline(); + try f.newline(); return local; } -fn airUnreach(o: *Object) !void { +fn airUnreach(f: *Function) !void { // Not even allowed to call unreachable in a naked function. - if (o.dg.is_naked_fn) return; - try o.code.writer.writeAll("zig_unreachable();\n"); + if (f.dg.is_naked_fn) return; + try f.code.writer.writeAll("zig_unreachable();\n"); } fn airLoop(f: *Function, inst: Air.Inst.Index) !void { const block = f.air.unwrapBlock(inst); - const w = &f.object.code.writer; + const w = &f.code.writer; // `repeat` instructions matching this loop will branch to // this label. Since we need a label for arbitrary `repeat` // anyway, there's actually no need to use a "real" looping // construct at all! try w.print("zig_loop_{d}:", .{@intFromEnum(inst)}); - try f.object.newline(); + try f.newline(); try genBodyInner(f, block.body); // no need to restore state, we're noreturn } @@ -5230,15 +4431,15 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !void { const then_body = cond_br.then_body; const else_body = cond_br.else_body; const liveness_condbr = f.liveness.getCondBr(inst); - const w = &f.object.code.writer; + const w = &f.code.writer; try w.writeAll("if ("); - try f.writeCValue(w, cond, .Other); + try f.writeCValue(w, cond, .other); try w.writeAll(") "); try genBodyResolveState(f, inst, liveness_condbr.then_deaths, then_body, false); - try f.object.newline(); - if (else_body.len > 0) if (f.object.dg.expected_block) |_| + try f.newline(); + if (else_body.len > 0) if (f.dg.expected_block) |_| return f.fail("runtime code not allowed in naked function", .{}); // We don't need to use `genBodyResolveState` for the else block, because this instruction is @@ -5256,23 +4457,23 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !void { } fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; - const gpa = f.object.dg.gpa; + const gpa = f.dg.gpa; const switch_br = f.air.unwrapSwitch(inst); const init_condition = try f.resolveInst(switch_br.operand); try reap(f, inst, &.{switch_br.operand}); const condition_ty = f.typeOf(switch_br.operand); - const w = &f.object.code.writer; + const w = &f.code.writer; // For dispatches, we will create a local alloc to contain the condition value. // This may not result in optimal codegen for switch loops, but it minimizes the // amount of C code we generate, which is probably more desirable here (and is simpler). const condition = if (is_dispatch_loop) cond: { const new_local = try f.allocLocal(inst, condition_ty); - try f.copyCValue(try f.ctypeFromType(condition_ty, .complete), new_local, init_condition); + try f.copyCValue(new_local, init_condition); try w.print("zig_switch_{d}_loop:", .{@intFromEnum(inst)}); - try f.object.newline(); + try f.newline(); try f.loop_switch_conds.put(gpa, inst, new_local.new_local); break :cond new_local; } else init_condition; @@ -5294,9 +4495,9 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void try f.renderType(w, lowered_condition_ty); try w.writeByte(')'); } - try f.writeCValue(w, condition, .Other); + try f.writeCValue(w, condition, .other); try w.writeAll(") {"); - f.object.indent(); + f.indent(); const liveness = try f.liveness.getSwitchBr(gpa, inst, switch_br.cases_len + 1); defer gpa.free(liveness.deaths); @@ -5309,7 +4510,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void continue; } for (case.items) |item| { - try f.object.newline(); + try f.newline(); try w.writeAll("case "); const item_value = try f.air.value(item, pt); // If `item_value` is a pointer with a known integer address, print the address @@ -5326,28 +4527,28 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void try f.renderType(w, .usize); try w.writeByte(')'); } - try f.object.dg.renderValue(w, (try f.air.value(item, pt)).?, .Other); + try f.dg.renderValue(w, (try f.air.value(item, pt)).?, .other); } try w.writeByte(':'); } try w.writeAll(" {"); - f.object.indent(); - try f.object.newline(); + f.indent(); + try f.newline(); if (is_dispatch_loop) { try w.print("zig_switch_{d}_dispatch_{d}:;", .{ @intFromEnum(inst), case.idx }); - try f.object.newline(); + try f.newline(); } try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true); - try f.object.outdent(); + try f.outdent(); try w.writeByte('}'); - if (f.object.dg.expected_block) |_| + if (f.dg.expected_block) |_| return f.fail("runtime code not allowed in naked function", .{}); // The case body must be noreturn so we don't need to insert a break. } const else_body = it.elseBody(); - try f.object.newline(); + try f.newline(); try w.writeAll("default: "); if (any_range_cases) { @@ -5360,33 +4561,33 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void try w.writeAll("if ("); for (case.items, 0..) |item, item_i| { if (item_i != 0) try w.writeAll(" || "); - try f.writeCValue(w, condition, .Other); + try f.writeCValue(w, condition, .other); try w.writeAll(" == "); - try f.object.dg.renderValue(w, (try f.air.value(item, pt)).?, .Other); + try f.dg.renderValue(w, (try f.air.value(item, pt)).?, .other); } for (case.ranges, 0..) |range, range_i| { if (case.items.len != 0 or range_i != 0) try w.writeAll(" || "); // "(x >= lower && x <= upper)" try w.writeByte('('); - try f.writeCValue(w, condition, .Other); + try f.writeCValue(w, condition, .other); try w.writeAll(" >= "); - try f.object.dg.renderValue(w, (try f.air.value(range[0], pt)).?, .Other); + try f.dg.renderValue(w, (try f.air.value(range[0], pt)).?, .other); try w.writeAll(" && "); - try f.writeCValue(w, condition, .Other); + try f.writeCValue(w, condition, .other); try w.writeAll(" <= "); - try f.object.dg.renderValue(w, (try f.air.value(range[1], pt)).?, .Other); + try f.dg.renderValue(w, (try f.air.value(range[1], pt)).?, .other); try w.writeByte(')'); } try w.writeAll(") {"); - f.object.indent(); - try f.object.newline(); + f.indent(); + try f.newline(); if (is_dispatch_loop) { try w.print("zig_switch_{d}_dispatch_{d}: ", .{ @intFromEnum(inst), case.idx }); } try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true); - try f.object.outdent(); + try f.outdent(); try w.writeByte('}'); - if (f.object.dg.expected_block) |_| + if (f.dg.expected_block) |_| return f.fail("runtime code not allowed in naked function", .{}); } } @@ -5400,16 +4601,16 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void try die(f, inst, death.toRef()); } try genBody(f, else_body); - if (f.object.dg.expected_block) |_| + if (f.dg.expected_block) |_| return f.fail("runtime code not allowed in naked function", .{}); - } else try airUnreach(&f.object); - try f.object.newline(); - try f.object.outdent(); + } else try airUnreach(f); + try f.newline(); + try f.outdent(); try w.writeAll("}\n"); } fn asmInputNeedsLocal(f: *Function, constraint: []const u8, value: CValue) bool { - const dg = f.object.dg; + const dg = f.dg; const target = &dg.mod.resolved_target.result; return switch (constraint[0]) { '{' => true, @@ -5429,28 +4630,28 @@ fn asmInputNeedsLocal(f: *Function, constraint: []const u8, value: CValue) bool } fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const unwrapped_asm = f.air.unwrapAsm(inst); const is_volatile = unwrapped_asm.is_volatile; - const gpa = f.object.dg.gpa; + const gpa = f.dg.gpa; const outputs = unwrapped_asm.outputs; const inputs = unwrapped_asm.inputs; const result = result: { - const w = &f.object.code.writer; + const w = &f.code.writer; const inst_ty = f.typeOfIndex(inst); - const inst_local = if (inst_ty.hasRuntimeBitsIgnoreComptime(zcu)) local: { + const inst_local = if (inst_ty.hasRuntimeBits(zcu)) local: { const inst_local = try f.allocLocalValue(.{ - .ctype = try f.ctypeFromType(inst_ty, .complete), - .alignas = CType.AlignAs.fromAbiAlignment(inst_ty.abiAlignment(zcu)), + .type = inst_ty, + .alignment = .none, }); if (f.wantSafety()) { - try f.writeCValue(w, inst_local, .Other); + try f.writeCValue(w, inst_local, .other); try w.writeAll(" = "); - try f.writeCValue(w, .{ .undef = inst_ty }, .Other); + try f.writeCValue(w, .{ .undef = inst_ty }, .other); try w.writeByte(';'); - try f.object.newline(); + try f.newline(); } break :local inst_local; } else .none; @@ -5471,20 +4672,20 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const output_ty = if (output.operand == .none) inst_ty else f.typeOf(output.operand).childType(zcu); try w.writeAll("register "); const output_local = try f.allocLocalValue(.{ - .ctype = try f.ctypeFromType(output_ty, .complete), - .alignas = CType.AlignAs.fromAbiAlignment(output_ty.abiAlignment(zcu)), + .type = output_ty, + .alignment = .none, }); try f.allocs.put(gpa, output_local.new_local, false); - try f.object.dg.renderTypeAndName(w, output_ty, output_local, .{}, .none, .complete); + try f.dg.renderTypeAndName(w, output_ty, output_local, .{}, .none); try w.writeAll(" __asm(\""); try w.writeAll(constraint["={".len .. constraint.len - "}".len]); try w.writeAll("\")"); if (f.wantSafety()) { try w.writeAll(" = "); - try f.writeCValue(w, .{ .undef = output_ty }, .Other); + try f.writeCValue(w, .{ .undef = output_ty }, .other); } try w.writeByte(';'); - try f.object.newline(); + try f.newline(); } } @@ -5504,29 +4705,29 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const input_ty = f.typeOf(input.operand); if (is_reg) try w.writeAll("register "); const input_local = try f.allocLocalValue(.{ - .ctype = try f.ctypeFromType(input_ty, .complete), - .alignas = CType.AlignAs.fromAbiAlignment(input_ty.abiAlignment(zcu)), + .type = input_ty, + .alignment = .none, }); try f.allocs.put(gpa, input_local.new_local, false); // Do not render the declaration as `const` qualified if we're generating an // explicit `register` local, as GCC will ignore the constraint completely. - try f.object.dg.renderTypeAndName(w, input_ty, input_local, if (is_reg) .{} else Const, .none, .complete); + try f.dg.renderTypeAndName(w, input_ty, input_local, .{ .@"const" = is_reg }, .none); if (is_reg) { try w.writeAll(" __asm(\""); try w.writeAll(constraint["{".len .. constraint.len - "}".len]); try w.writeAll("\")"); } try w.writeAll(" = "); - try f.writeCValue(w, input_val, .Other); + try f.writeCValue(w, input_val, .other); try w.writeByte(';'); - try f.object.newline(); + try f.newline(); } } { const asm_source = unwrapped_asm.source; - var stack = std.heap.stackFallback(256, f.object.dg.gpa); + var stack = std.heap.stackFallback(256, f.dg.gpa); const allocator = stack.get(); const fixed_asm_source = try allocator.alloc(u8, asm_source.len); defer allocator.free(fixed_asm_source); @@ -5592,10 +4793,10 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const is_reg = constraint[1] == '{'; try w.print("{f}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint, null)}); if (is_reg) { - try f.writeCValue(w, .{ .local = locals_index }, .Other); + try f.writeCValue(w, .{ .local = locals_index }, .other); locals_index += 1; } else if (output.operand == .none) { - try f.writeCValue(w, inst_local, .FunctionArgument); + try f.writeCValue(w, inst_local, .other); } else { try f.writeCValueDeref(w, try f.resolveInst(output.operand)); } @@ -5619,57 +4820,54 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const input_local_idx = locals_index; locals_index += 1; break :local .{ .local = input_local_idx }; - } else input_val, .Other); + } else input_val, .other); try w.writeByte(')'); } try w.writeByte(':'); const ip = &zcu.intern_pool; - const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate; - const struct_type: Type = .fromInterned(aggregate.ty); - switch (aggregate.storage) { - .elems => |elems| for (elems, 0..) |elem, i| switch (elem) { - .bool_true => { - const field_name = struct_type.structFieldName(i, zcu).toSlice(ip).?; - assert(field_name.len != 0); - - const target = &f.object.dg.mod.resolved_target.result; - var c_name_buf: [16]u8 = undefined; - const name = - if ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'r') name: { - // Convert "rN" to "$N" - const c_name = (&c_name_buf)[0..field_name.len]; - @memcpy(c_name, field_name); - c_name_buf[0] = '$'; - break :name c_name; - } else if ((target.cpu.arch.isMIPS() and (mem.startsWith(u8, field_name, "fcc") or field_name[0] == 'w')) or - ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'f') or - (target.cpu.arch == .kvx and !mem.eql(u8, field_name, "memory"))) name: { - // "$" prefix for these registers - c_name_buf[0] = '$'; - @memcpy((&c_name_buf)[1..][0..field_name.len], field_name); - break :name (&c_name_buf)[0 .. 1 + field_name.len]; - } else if (target.cpu.arch.isSPARC() and - (mem.eql(u8, field_name, "ccr") or mem.eql(u8, field_name, "icc") or mem.eql(u8, field_name, "xcc"))) name: { - // C compilers just use `icc` to encompass all of these. - break :name "icc"; - } else field_name; - - try w.print(" {f}", .{fmtStringLiteral(name, null)}); - (try w.writableArray(1))[0] = ','; - }, - .bool_false => continue, - else => unreachable, - }, - .repeated_elem => |elem| switch (elem) { - .bool_true => @panic("TODO"), - .bool_false => {}, - else => unreachable, - }, - .bytes => @panic("TODO"), + const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers); + const clobbers_ty = clobbers_val.typeOf(zcu); + var clobbers_bigint_buf: Value.BigIntSpace = undefined; + const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu); + for (0..clobbers_ty.structFieldCount(zcu)) |field_index| { + assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type); + const limb_bits = @bitSizeOf(std.math.big.Limb); + if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false + switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) { + 0 => continue, // field is false + 1 => {}, // field is true + } + const field_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?; + assert(field_name.len != 0); + + const target = &f.dg.mod.resolved_target.result; + var c_name_buf: [16]u8 = undefined; + const name = + if ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'r') name: { + // Convert "rN" to "$N" + const c_name = (&c_name_buf)[0..field_name.len]; + @memcpy(c_name, field_name); + c_name_buf[0] = '$'; + break :name c_name; + } else if ((target.cpu.arch.isMIPS() and (mem.startsWith(u8, field_name, "fcc") or field_name[0] == 'w')) or + ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'f') or + (target.cpu.arch == .kvx and !mem.eql(u8, field_name, "memory"))) name: { + // "$" prefix for these registers + c_name_buf[0] = '$'; + @memcpy((&c_name_buf)[1..][0..field_name.len], field_name); + break :name (&c_name_buf)[0 .. 1 + field_name.len]; + } else if (target.cpu.arch.isSPARC() and + (mem.eql(u8, field_name, "ccr") or mem.eql(u8, field_name, "icc") or mem.eql(u8, field_name, "xcc"))) name: { + // C compilers just use `icc` to encompass all of these. + break :name "icc"; + } else field_name; + + try w.print(" {f}", .{fmtStringLiteral(name, null)}); + (try w.writableArray(1))[0] = ','; } w.undo(1); // erase the last comma try w.writeAll(");"); - try f.object.newline(); + try f.newline(); locals_index = locals_begin; it = unwrapped_asm.iterateOutputs(); @@ -5683,10 +4881,10 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { else try f.resolveInst(output.operand)); try w.writeAll(" = "); - try f.writeCValue(w, .{ .local = locals_index }, .Other); + try f.writeCValue(w, .{ .local = locals_index }, .other); locals_index += 1; try w.writeByte(';'); - try f.object.newline(); + try f.newline(); } } @@ -5708,147 +4906,145 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { fn airIsNull( f: *Function, inst: Air.Inst.Index, - operator: std.math.CompareOperator, + operator: enum { eq, neq }, is_ptr: bool, ) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; - const ctype_pool = &f.object.dg.ctype_pool; const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const w = &f.object.code.writer; + const w = &f.code.writer; const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); const local = try f.allocLocal(inst, .bool); - const a = try Assignment.start(f, w, .bool); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); + try f.writeCValue(w, local, .other); + try w.writeAll(" = "); const operand_ty = f.typeOf(un_op); const optional_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty; - const opt_ctype = try f.ctypeFromType(optional_ty, .complete); - const rhs = switch (opt_ctype.info(ctype_pool)) { - .basic, .pointer => rhs: { - if (is_ptr) - try f.writeCValueDeref(w, operand) - else - try f.writeCValue(w, operand, .Other); - break :rhs if (opt_ctype.isBool()) - "true" - else if (opt_ctype.isInteger()) - "0" - else - "NULL"; + + const pre: []const u8, const maybe_field: ?[]const u8, const post: []const u8 = switch (operator) { + // zig fmt: off + .eq => switch (CType.classifyOptional(optional_ty, zcu)) { + .npv_payload => unreachable, // opv optional + .error_set => .{ "", null, " == 0" }, + .ptr_like => .{ "", null, " == NULL" }, + .slice_like => .{ "", "ptr", " == NULL" }, + .opv_payload => .{ "", "is_null", "" }, + .@"struct" => .{ "", "is_null", "" }, }, - .aligned, .array, .vector, .fwd_decl, .function => unreachable, - .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) { - .is_null, .payload => rhs: { - if (is_ptr) - try f.writeCValueDerefMember(w, operand, .{ .identifier = "is_null" }) - else - try f.writeCValueMember(w, operand, .{ .identifier = "is_null" }); - break :rhs "true"; - }, - .ptr, .len => rhs: { - if (is_ptr) - try f.writeCValueDerefMember(w, operand, .{ .identifier = "ptr" }) - else - try f.writeCValueMember(w, operand, .{ .identifier = "ptr" }); - break :rhs "NULL"; - }, - else => unreachable, + .neq => switch (CType.classifyOptional(optional_ty, zcu)) { + .npv_payload => unreachable, // opv optional + .error_set => .{ "", null, " != 0" }, + .ptr_like => .{ "", null, " != NULL" }, + .slice_like => .{ "", "ptr", " != NULL" }, + .opv_payload => .{ "!", "is_null", "" }, + .@"struct" => .{ "!", "is_null", "" }, }, + // zig fmt: on }; - try w.writeAll(compareOperatorC(operator)); - try w.writeAll(rhs); - try a.end(f, w); + + try w.writeAll(pre); + if (maybe_field) |field| { + if (is_ptr) { + try f.writeCValueDerefMember(w, operand, .{ .identifier = field }); + } else { + try f.writeCValueMember(w, operand, .{ .identifier = field }); + } + } else { + if (is_ptr) { + try f.writeCValueDeref(w, operand); + } else { + try f.writeCValue(w, operand, .other); + } + } + try w.writeAll(post); + + try w.writeByte(';'); + try f.newline(); return local; } fn airOptionalPayload(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; - const ctype_pool = &f.object.dg.ctype_pool; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const inst_ty = f.typeOfIndex(inst); const operand_ty = f.typeOf(ty_op.operand); const opt_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty; - const opt_ctype = try f.ctypeFromType(opt_ty, .complete); - if (opt_ctype.isBool()) return if (is_ptr) .{ .undef = inst_ty } else .none; const operand = try f.resolveInst(ty_op.operand); - switch (opt_ctype.info(ctype_pool)) { - .basic, .pointer => return f.moveCValue(inst, inst_ty, operand), - .aligned, .array, .vector, .fwd_decl, .function => unreachable, - .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) { - .is_null, .payload => { - const w = &f.object.code.writer; - const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); - if (is_ptr) { - try w.writeByte('&'); - try f.writeCValueDerefMember(w, operand, .{ .identifier = "payload" }); - } else try f.writeCValueMember(w, operand, .{ .identifier = "payload" }); - try a.end(f, w); - return local; - }, - .ptr, .len => return f.moveCValue(inst, inst_ty, operand), - else => unreachable, + + switch (CType.classifyOptional(opt_ty, zcu)) { + .npv_payload => unreachable, // opv optional + + .opv_payload => return if (is_ptr) .{ .undef = inst_ty } else .none, + + .error_set, + .ptr_like, + .slice_like, + => return f.moveCValue(inst, inst_ty, operand), + + .@"struct" => { + const w = &f.code.writer; + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(w, local, .other); + try w.writeAll(" = "); + if (is_ptr) { + try w.writeByte('&'); + try f.writeCValueDerefMember(w, operand, .{ .identifier = "payload" }); + } else try f.writeCValueMember(w, operand, .{ .identifier = "payload" }); + try w.writeByte(';'); + try f.newline(); + return local; }, } } fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const w = &f.object.code.writer; + const w = &f.code.writer; const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); const operand_ty = f.typeOf(ty_op.operand); + const opt_ty = operand_ty.childType(zcu); const inst_ty = f.typeOfIndex(inst); - const opt_ctype = try f.ctypeFromType(operand_ty.childType(zcu), .complete); - switch (opt_ctype.info(&f.object.dg.ctype_pool)) { - .basic => { - const a = try Assignment.start(f, w, opt_ctype); - try f.writeCValueDeref(w, operand); - try a.assign(f, w); - try f.object.dg.renderValue(w, Value.false, .Other); - try a.end(f, w); - return .none; - }, - .pointer => { - if (f.liveness.isUnused(inst)) return .none; - const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, w, opt_ctype); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); - try f.writeCValue(w, operand, .Other); - try a.end(f, w); - return local; + + switch (CType.classifyOptional(opt_ty, zcu)) { + .npv_payload => unreachable, // opv optional + + .opv_payload => { + try f.writeCValueDerefMember(w, operand, .{ .identifier = "is_null" }); + try w.writeAll(" = "); + try f.dg.renderValue(w, .false, .other); + try w.writeByte(';'); + try f.newline(); + return .{ .undef = inst_ty }; }, - .aligned, .array, .vector, .fwd_decl, .function => unreachable, - .aggregate => { - { - const a = try Assignment.start(f, w, opt_ctype); - try f.writeCValueDerefMember(w, operand, .{ .identifier = "is_null" }); - try a.assign(f, w); - try f.object.dg.renderValue(w, Value.false, .Other); - try a.end(f, w); - } + + .error_set, + .ptr_like, + .slice_like, + => return f.moveCValue(inst, inst_ty, operand), + + .@"struct" => { + try f.writeCValueDerefMember(w, operand, .{ .identifier = "is_null" }); + try w.writeAll(" = "); + try f.dg.renderValue(w, .false, .other); + try w.writeByte(';'); + try f.newline(); if (f.liveness.isUnused(inst)) return .none; const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, w, opt_ctype); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); - try w.writeByte('&'); + try f.writeCValue(w, local, .other); + try w.writeAll(" = &"); try f.writeCValueDerefMember(w, operand, .{ .identifier = "payload" }); - try a.end(f, w); + try w.writeByte(';'); + try f.newline(); return local; }, } @@ -5870,12 +5066,12 @@ fn fieldLocation( .struct_type => { const loaded_struct = ip.loadStructType(container_ty.toIntern()); return switch (loaded_struct.layout) { - .auto, .@"extern" => if (!container_ty.hasRuntimeBitsIgnoreComptime(zcu)) + .auto, .@"extern" => if (!container_ty.hasRuntimeBits(zcu)) .begin - else if (!field_ptr_ty.childType(zcu).hasRuntimeBitsIgnoreComptime(zcu)) - .{ .byte_offset = loaded_struct.offsets.get(ip)[field_index] } + else if (!field_ptr_ty.childType(zcu).hasRuntimeBits(zcu)) + .{ .byte_offset = loaded_struct.field_offsets.get(ip)[field_index] } else - .{ .field = .{ .identifier = loaded_struct.fieldName(ip, field_index).toSlice(ip) } }, + .{ .field = .{ .identifier = loaded_struct.field_names.get(ip)[field_index].toSlice(ip) } }, .@"packed" => if (field_ptr_ty.ptrInfo(zcu).packed_offset.host_size == 0) .{ .byte_offset = @divExact(zcu.structPackedFieldBitOffset(loaded_struct, field_index) + container_ptr_ty.ptrInfo(zcu).packed_offset.bit_offset, 8) } @@ -5883,27 +5079,29 @@ fn fieldLocation( .begin, }; }, - .tuple_type => return if (!container_ty.hasRuntimeBitsIgnoreComptime(zcu)) + .tuple_type => return if (!container_ty.hasRuntimeBits(zcu)) .begin - else if (!field_ptr_ty.childType(zcu).hasRuntimeBitsIgnoreComptime(zcu)) + else if (!field_ptr_ty.childType(zcu).hasRuntimeBits(zcu)) .{ .byte_offset = container_ty.structFieldOffset(field_index, zcu) } else .{ .field = .{ .field = field_index } }, .union_type => { const loaded_union = ip.loadUnionType(container_ty.toIntern()); - switch (loaded_union.flagsUnordered(ip).layout) { - .auto, .@"extern" => { + switch (loaded_union.layout) { + .auto => { const field_ty: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) - return if (loaded_union.hasTag(ip) and !container_ty.unionHasAllZeroBitFieldTypes(zcu)) - .{ .field = .{ .identifier = "payload" } } - else - .begin; - const field_name = loaded_union.loadTagType(ip).names.get(ip)[field_index]; - return .{ .field = if (loaded_union.hasTag(ip)) - .{ .payload_identifier = field_name.toSlice(ip) } - else - .{ .identifier = field_name.toSlice(ip) } }; + if (!field_ty.hasRuntimeBits(zcu)) { + if (container_ty.unionHasAllZeroBitFieldTypes(zcu)) return .begin; + return .{ .field = .{ .identifier = "payload" } }; + } + const field_name = ip.loadEnumType(loaded_union.enum_tag_type).field_names.get(ip)[field_index]; + return .{ .field = .{ .payload_identifier = field_name.toSlice(ip) } }; + }, + .@"extern" => { + const field_ty: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBits(zcu)) return .begin; + const field_name = ip.loadEnumType(loaded_union.enum_tag_type).field_names.get(ip)[field_index]; + return .{ .field = .{ .identifier = field_name.toSlice(ip) } }; }, .@"packed" => return .begin, } @@ -5940,7 +5138,7 @@ fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue } fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = f.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; @@ -5952,26 +5150,26 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { const field_ptr_val = try f.resolveInst(extra.field_ptr); try reap(f, inst, &.{extra.field_ptr}); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, container_ptr_ty); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(" = ("); try f.renderType(w, container_ptr_ty); try w.writeByte(')'); switch (fieldLocation(container_ptr_ty, field_ptr_ty, extra.field_index, zcu)) { - .begin => try f.writeCValue(w, field_ptr_val, .Other), + .begin => try f.writeCValue(w, field_ptr_val, .other), .field => |field| { const u8_ptr_ty = try pt.adjustPtrTypeChild(field_ptr_ty, .u8); try w.writeAll("(("); try f.renderType(w, u8_ptr_ty); try w.writeByte(')'); - try f.writeCValue(w, field_ptr_val, .Other); + try f.writeCValue(w, field_ptr_val, .other); try w.writeAll(" - offsetof("); try f.renderType(w, container_ty); try w.writeAll(", "); - try f.writeCValue(w, field, .Other); + try f.writeCValue(w, field, .other); try w.writeAll("))"); }, .byte_offset => |byte_offset| { @@ -5980,7 +5178,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { try w.writeAll("(("); try f.renderType(w, u8_ptr_ty); try w.writeByte(')'); - try f.writeCValue(w, field_ptr_val, .Other); + try f.writeCValue(w, field_ptr_val, .other); try w.print(" - {f})", .{ try f.fmtIntLiteralDec(try pt.intValue(.usize, byte_offset)), }); @@ -5988,7 +5186,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { } try w.writeByte(';'); - try f.object.newline(); + try f.newline(); return local; } @@ -5999,23 +5197,19 @@ fn fieldPtr( container_ptr_val: CValue, field_index: u32, ) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; - const container_ty = container_ptr_ty.childType(zcu); const field_ptr_ty = f.typeOfIndex(inst); - // Ensure complete type definition is visible before accessing fields. - _ = try f.ctypeFromType(container_ty, .complete); - - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, field_ptr_ty); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(" = ("); try f.renderType(w, field_ptr_ty); try w.writeByte(')'); switch (fieldLocation(container_ptr_ty, field_ptr_ty, field_index, zcu)) { - .begin => try f.writeCValue(w, container_ptr_val, .Other), + .begin => try f.writeCValue(w, container_ptr_val, .other), .field => |field| { try w.writeByte('&'); try f.writeCValueDerefMember(w, container_ptr_val, field); @@ -6026,7 +5220,7 @@ fn fieldPtr( try w.writeAll("(("); try f.renderType(w, u8_ptr_ty); try w.writeByte(')'); - try f.writeCValue(w, container_ptr_val, .Other); + try f.writeCValue(w, container_ptr_val, .other); try w.print(" + {f})", .{ try f.fmtIntLiteralDec(try pt.intValue(.usize, byte_offset)), }); @@ -6034,61 +5228,51 @@ fn fieldPtr( } try w.writeByte(';'); - try f.object.newline(); + try f.newline(); return local; } fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = f.air.extraData(Air.StructField, ty_pl.payload).data; const inst_ty = f.typeOfIndex(inst); - if (!inst_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - try reap(f, inst, &.{extra.struct_operand}); - return .none; - } + assert(inst_ty.hasRuntimeBits(zcu)); const struct_byval = try f.resolveInst(extra.struct_operand); try reap(f, inst, &.{extra.struct_operand}); const struct_ty = f.typeOf(extra.struct_operand); - const w = &f.object.code.writer; - - // Ensure complete type definition is visible before accessing fields. - _ = try f.ctypeFromType(struct_ty, .complete); + const w = &f.code.writer; assert(struct_ty.containerLayout(zcu) != .@"packed"); // `Air.Legalize.Feature.expand_packed_struct_field_val` handles this case const field_name: CValue = switch (ip.indexToKey(struct_ty.toIntern())) { .struct_type => .{ .identifier = struct_ty.structFieldName(extra.field_index, zcu).unwrap().?.toSlice(ip) }, .union_type => name: { const union_type = ip.loadUnionType(struct_ty.toIntern()); - const enum_tag_ty: Type = .fromInterned(union_type.enum_tag_ty); + const enum_tag_ty: Type = .fromInterned(union_type.enum_tag_type); const field_name_str = enum_tag_ty.enumFieldName(extra.field_index, zcu).toSlice(ip); - if (union_type.hasTag(ip)) { - break :name .{ .payload_identifier = field_name_str }; - } else { - break :name .{ .identifier = field_name_str }; - } + break :name .{ .payload_identifier = field_name_str }; }, .tuple_type => .{ .field = extra.field_index }, else => unreachable, }; const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); + try f.writeCValue(w, local, .other); + try w.writeAll(" = "); try f.writeCValueMember(w, struct_byval, field_name); - try a.end(f, w); + try w.writeByte(';'); + try f.newline(); return local; } /// *(E!T) -> E /// Note that the result is never a pointer. fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; @@ -6098,37 +5282,23 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { try reap(f, inst, &.{ty_op.operand}); const operand_is_ptr = operand_ty.zigTypeTag(zcu) == .pointer; - const error_union_ty = if (operand_is_ptr) operand_ty.childType(zcu) else operand_ty; - const error_ty = error_union_ty.errorUnionSet(zcu); - const payload_ty = error_union_ty.errorUnionPayload(zcu); const local = try f.allocLocal(inst, inst_ty); - if (!payload_ty.hasRuntimeBits(zcu) and operand == .local and operand.local == local.new_local) { - // The store will be 'x = x'; elide it. - return local; - } - - const w = &f.object.code.writer; - try f.writeCValue(w, local, .Other); + const w = &f.code.writer; + try f.writeCValue(w, local, .other); try w.writeAll(" = "); - if (!payload_ty.hasRuntimeBits(zcu)) - try f.writeCValue(w, operand, .Other) - else if (error_ty.errorSetIsEmpty(zcu)) - try w.print("{f}", .{ - try f.fmtIntLiteralDec(try pt.intValue(try pt.errorIntType(), 0)), - }) - else if (operand_is_ptr) + if (operand_is_ptr) try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" }) else try f.writeCValueMember(w, operand, .{ .identifier = "error" }); try w.writeByte(';'); - try f.object.newline(); + try f.newline(); return local; } fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; @@ -6138,154 +5308,124 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu const operand_ty = f.typeOf(ty_op.operand); const error_union_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty; - const w = &f.object.code.writer; + const w = &f.code.writer; if (!error_union_ty.errorUnionPayload(zcu).hasRuntimeBits(zcu)) { - if (!is_ptr) return .none; - + assert(is_ptr); // opv bug in sema const local = try f.allocLocal(inst, inst_ty); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(" = ("); try f.renderType(w, inst_ty); try w.writeByte(')'); - try f.writeCValue(w, operand, .Other); + try f.writeCValue(w, operand, .other); try w.writeByte(';'); - try f.object.newline(); + try f.newline(); return local; } const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); + try f.writeCValue(w, local, .other); + try w.writeAll(" = "); if (is_ptr) { try w.writeByte('&'); try f.writeCValueDerefMember(w, operand, .{ .identifier = "payload" }); } else try f.writeCValueMember(w, operand, .{ .identifier = "payload" }); - try a.end(f, w); + try w.writeByte(';'); + try f.newline(); return local; } fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { - const ctype_pool = &f.object.dg.ctype_pool; + const zcu = f.dg.pt.zcu; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const inst_ty = f.typeOfIndex(inst); - const inst_ctype = try f.ctypeFromType(inst_ty, .complete); - if (inst_ctype.isBool()) return .{ .constant = Value.true }; const operand = try f.resolveInst(ty_op.operand); - switch (inst_ctype.info(ctype_pool)) { - .basic, .pointer => return f.moveCValue(inst, inst_ty, operand), - .aligned, .array, .vector, .fwd_decl, .function => unreachable, - .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) { - .is_null, .payload => { - const operand_ctype = try f.ctypeFromType(f.typeOf(ty_op.operand), .complete); - const w = &f.object.code.writer; - const local = try f.allocLocal(inst, inst_ty); - { - const a = try Assignment.start(f, w, .bool); - try f.writeCValueMember(w, local, .{ .identifier = "is_null" }); - try a.assign(f, w); - try w.writeAll("false"); - try a.end(f, w); - } - { - const a = try Assignment.start(f, w, operand_ctype); - try f.writeCValueMember(w, local, .{ .identifier = "payload" }); - try a.assign(f, w); - try f.writeCValue(w, operand, .Other); - try a.end(f, w); - } - return local; - }, - .ptr, .len => return f.moveCValue(inst, inst_ty, operand), - else => unreachable, + + switch (CType.classifyOptional(inst_ty, zcu)) { + .npv_payload => unreachable, // opv optional + + .opv_payload => unreachable, // opv bug in Sema + + .error_set, + .ptr_like, + .slice_like, + => return f.moveCValue(inst, inst_ty, operand), + + .@"struct" => { + const w = &f.code.writer; + const local = try f.allocLocal(inst, inst_ty); + + try f.writeCValueMember(w, local, .{ .identifier = "is_null" }); + try w.writeAll(" = false;"); + try f.newline(); + + try f.writeCValueMember(w, local, .{ .identifier = "payload" }); + try w.writeAll(" = "); + try f.writeCValue(w, operand, .other); + try w.writeByte(';'); + try f.newline(); + + return local; }, } } fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const inst_ty = f.typeOfIndex(inst); const payload_ty = inst_ty.errorUnionPayload(zcu); - const repr_is_err = !payload_ty.hasRuntimeBitsIgnoreComptime(zcu); - const err_ty = inst_ty.errorUnionSet(zcu); const err = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); - if (repr_is_err and err == .local and err.local == local.new_local) { - // The store will be 'x = x'; elide it. - return local; - } - - if (!repr_is_err) { - const a = try Assignment.start(f, w, try f.ctypeFromType(payload_ty, .complete)); + if (payload_ty.hasRuntimeBits(zcu)) { try f.writeCValueMember(w, local, .{ .identifier = "payload" }); - try a.assign(f, w); - try f.object.dg.renderUndefValue(w, payload_ty, .Other); - try a.end(f, w); - } - { - const a = try Assignment.start(f, w, try f.ctypeFromType(err_ty, .complete)); - if (repr_is_err) - try f.writeCValue(w, local, .Other) - else - try f.writeCValueMember(w, local, .{ .identifier = "error" }); - try a.assign(f, w); - try f.writeCValue(w, err, .Other); - try a.end(f, w); + try w.writeAll(" = "); + try f.dg.renderUndefValue(w, payload_ty, .other); + try w.writeByte(';'); + try f.newline(); } + + try f.writeCValueMember(w, local, .{ .identifier = "error" }); + try w.writeAll(" = "); + try f.writeCValue(w, err, .other); + try w.writeByte(';'); + try f.newline(); + return local; } fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; - const zcu = pt.zcu; - const w = &f.object.code.writer; + const pt = f.dg.pt; + const w = &f.code.writer; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const inst_ty = f.typeOfIndex(inst); const operand = try f.resolveInst(ty_op.operand); - const operand_ty = f.typeOf(ty_op.operand); - const error_union_ty = operand_ty.childType(zcu); - const payload_ty = error_union_ty.errorUnionPayload(zcu); const err_int_ty = try pt.errorIntType(); const no_err = try pt.intValue(err_int_ty, 0); try reap(f, inst, &.{ty_op.operand}); // First, set the non-error value. - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - const a = try Assignment.start(f, w, try f.ctypeFromType(operand_ty, .complete)); - try f.writeCValueDeref(w, operand); - try a.assign(f, w); - try w.print("{f}", .{try f.fmtIntLiteralDec(no_err)}); - try a.end(f, w); - return .none; - } - { - const a = try Assignment.start(f, w, try f.ctypeFromType(err_int_ty, .complete)); - try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" }); - try a.assign(f, w); - try w.print("{f}", .{try f.fmtIntLiteralDec(no_err)}); - try a.end(f, w); - } + try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" }); + try w.print(" = {f};", .{try f.fmtIntLiteralDec(no_err)}); + try f.newline(); // Then return the payload pointer (only if it is used) if (f.liveness.isUnused(inst)) return .none; const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); - try w.writeByte('&'); + try f.writeCValue(w, local, .other); + try w.writeAll(" = &"); try f.writeCValueDerefMember(w, operand, .{ .identifier = "payload" }); - try a.end(f, w); + try w.writeByte(';'); + try f.newline(); return local; } @@ -6305,131 +5445,96 @@ fn airSaveErrReturnTraceIndex(f: *Function, inst: Air.Inst.Index) !CValue { } fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const inst_ty = f.typeOfIndex(inst); const payload_ty = inst_ty.errorUnionPayload(zcu); const payload = try f.resolveInst(ty_op.operand); - const repr_is_err = !payload_ty.hasRuntimeBitsIgnoreComptime(zcu); - const err_ty = inst_ty.errorUnionSet(zcu); + assert(payload_ty.hasRuntimeBits(zcu)); try reap(f, inst, &.{ty_op.operand}); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); - if (!repr_is_err) { - const a = try Assignment.start(f, w, try f.ctypeFromType(payload_ty, .complete)); - try f.writeCValueMember(w, local, .{ .identifier = "payload" }); - try a.assign(f, w); - try f.writeCValue(w, payload, .Other); - try a.end(f, w); - } - { - const a = try Assignment.start(f, w, try f.ctypeFromType(err_ty, .complete)); - if (repr_is_err) - try f.writeCValue(w, local, .Other) - else - try f.writeCValueMember(w, local, .{ .identifier = "error" }); - try a.assign(f, w); - try f.object.dg.renderValue(w, try pt.intValue(try pt.errorIntType(), 0), .Other); - try a.end(f, w); - } + + try f.writeCValueMember(w, local, .{ .identifier = "payload" }); + try w.writeAll(" = "); + try f.writeCValue(w, payload, .other); + try w.writeByte(';'); + try f.newline(); + + try f.writeCValueMember(w, local, .{ .identifier = "error" }); + try w.writeAll(" = "); + try f.dg.renderValue(w, try pt.intValue(try pt.errorIntType(), 0), .other); + try w.writeByte(';'); + try f.newline(); + return local; } fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const u8) !CValue { - const pt = f.object.dg.pt; - const zcu = pt.zcu; + const pt = f.dg.pt; const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const w = &f.object.code.writer; + const w = &f.code.writer; const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); - const operand_ty = f.typeOf(un_op); const local = try f.allocLocal(inst, .bool); - const err_union_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty; - const payload_ty = err_union_ty.errorUnionPayload(zcu); - const error_ty = err_union_ty.errorUnionSet(zcu); - const a = try Assignment.start(f, w, .bool); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); + try f.writeCValue(w, local, .other); + try w.writeAll(" = "); const err_int_ty = try pt.errorIntType(); - if (!error_ty.errorSetIsEmpty(zcu)) - if (payload_ty.hasRuntimeBits(zcu)) - if (is_ptr) - try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" }) - else - try f.writeCValueMember(w, operand, .{ .identifier = "error" }) - else - try f.writeCValue(w, operand, .Other) + if (is_ptr) + try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" }) else - try f.object.dg.renderValue(w, try pt.intValue(err_int_ty, 0), .Other); - try w.writeByte(' '); - try w.writeAll(operator); - try w.writeByte(' '); - try f.object.dg.renderValue(w, try pt.intValue(err_int_ty, 0), .Other); - try a.end(f, w); + try f.writeCValueMember(w, operand, .{ .identifier = "error" }); + try w.print(" {s} ", .{operator}); + try f.dg.renderValue(w, try pt.intValue(err_int_ty, 0), .other); + try w.writeByte(';'); + try f.newline(); return local; } fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; - const ctype_pool = &f.object.dg.ctype_pool; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); const inst_ty = f.typeOfIndex(inst); - const ptr_ty = inst_ty.slicePtrFieldType(zcu); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); const operand_ty = f.typeOf(ty_op.operand); const array_ty = operand_ty.childType(zcu); - { - const a = try Assignment.start(f, w, try f.ctypeFromType(ptr_ty, .complete)); - try f.writeCValueMember(w, local, .{ .identifier = "ptr" }); - try a.assign(f, w); - if (operand == .undef) { - try f.writeCValue(w, .{ .undef = inst_ty.slicePtrFieldType(zcu) }, .Other); - } else { - const ptr_ctype = try f.ctypeFromType(ptr_ty, .complete); - const ptr_child_ctype = ptr_ctype.info(ctype_pool).pointer.elem_ctype; - const elem_ty = array_ty.childType(zcu); - const elem_ctype = try f.ctypeFromType(elem_ty, .complete); - if (!ptr_child_ctype.eql(elem_ctype)) { - try w.writeByte('('); - try f.renderCType(w, ptr_ctype); - try w.writeByte(')'); - } - const operand_ctype = try f.ctypeFromType(operand_ty, .complete); - const operand_child_ctype = operand_ctype.info(ctype_pool).pointer.elem_ctype; - if (operand_child_ctype.info(ctype_pool) == .array) { - try w.writeByte('&'); - try f.writeCValueDeref(w, operand); - try w.print("[{f}]", .{try f.fmtIntLiteralDec(.zero_usize)}); - } else try f.writeCValue(w, operand, .Other); - } - try a.end(f, w); - } - { - const a = try Assignment.start(f, w, .usize); - try f.writeCValueMember(w, local, .{ .identifier = "len" }); - try a.assign(f, w); - try w.print("{f}", .{ - try f.fmtIntLiteralDec(try pt.intValue(.usize, array_ty.arrayLen(zcu))), - }); - try a.end(f, w); - } + // We have a `*[n]T`, which was turned into to a pointer to `struct { T array[n]; }`. + // Ideally we would want to use 'operand->array' to convert to a `T *` (we get a `T []` + // which decays to a pointer), but if the element type is zero-bit or the array length is + // zero, there will not be an `array` member (the array type lowers to `void`). We cannot + // check the type layout here because it may not be resolved, so in this instance, we must + // use a pointer cast. + try f.writeCValueMember(w, local, .{ .identifier = "ptr" }); + try w.writeAll(" = ("); + try f.dg.renderType(w, inst_ty.slicePtrFieldType(zcu)); + try w.writeByte(')'); + try f.writeCValue(w, operand, .other); + try w.writeByte(';'); + try f.newline(); + + try f.writeCValueMember(w, local, .{ .identifier = "len" }); + try w.print(" = {f}", .{ + try f.fmtIntLiteralDec(try pt.intValue(.usize, array_ty.arrayLen(zcu))), + }); + try w.writeByte(';'); + try f.newline(); return local; } fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; @@ -6439,7 +5544,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { try reap(f, inst, &.{ty_op.operand}); const operand_ty = f.typeOf(ty_op.operand); const scalar_ty = operand_ty.scalarType(zcu); - const target = &f.object.dg.mod.resolved_target.result; + const target = &f.dg.mod.resolved_target.result; const operation = if (inst_scalar_ty.isRuntimeFloat() and scalar_ty.isRuntimeFloat()) if (inst_scalar_ty.floatBits(target) < scalar_ty.floatBits(target)) "trunc" else "extend" else if (inst_scalar_ty.isInt(zcu) and scalar_ty.isRuntimeFloat()) @@ -6449,16 +5554,15 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { else unreachable; - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, operand_ty); - const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete)); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try v.elem(f, w); - try a.assign(f, w); + try w.writeAll(" = "); if (inst_scalar_ty.isInt(zcu) and scalar_ty.isRuntimeFloat()) { try w.writeAll("zig_wrap_"); - try f.object.dg.renderTypeForBuiltinFnName(w, inst_scalar_ty); + try f.dg.renderTypeForBuiltinFnName(w, inst_scalar_ty); try w.writeByte('('); } try w.writeAll("zig_"); @@ -6466,14 +5570,15 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { try w.writeAll(compilerRtAbbrev(scalar_ty, zcu, target)); try w.writeAll(compilerRtAbbrev(inst_scalar_ty, zcu, target)); try w.writeByte('('); - try f.writeCValue(w, operand, .FunctionArgument); + try f.writeCValue(w, operand, .other); try v.elem(f, w); try w.writeByte(')'); if (inst_scalar_ty.isInt(zcu) and scalar_ty.isRuntimeFloat()) { - try f.object.dg.renderBuiltinInfo(w, inst_scalar_ty, .bits); + try f.dg.renderBuiltinInfo(w, inst_scalar_ty, .bits); try w.writeByte(')'); } - try a.end(f, w); + try w.writeByte(';'); + try f.newline(); try v.end(f, inst, w); return local; @@ -6486,7 +5591,7 @@ fn airUnBuiltinCall( operation: []const u8, info: BuiltinInfo, ) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const operand = try f.resolveInst(operand_ref); @@ -6496,30 +5601,32 @@ fn airUnBuiltinCall( const operand_ty = f.typeOf(operand_ref); const scalar_ty = operand_ty.scalarType(zcu); - const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete); - const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array; + const ref_ret = lowersToBigInt(inst_scalar_ty, zcu); + const ref_arg = lowersToBigInt(scalar_ty, zcu); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, operand_ty); if (!ref_ret) { - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try v.elem(f, w); try w.writeAll(" = "); } try w.print("zig_{s}_", .{operation}); - try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty); + try f.dg.renderTypeForBuiltinFnName(w, scalar_ty); try w.writeByte('('); if (ref_ret) { - try f.writeCValue(w, local, .FunctionArgument); + try w.writeByte('&'); + try f.writeCValue(w, local, .other); try v.elem(f, w); try w.writeAll(", "); } - try f.writeCValue(w, operand, .FunctionArgument); + if (ref_arg) try w.writeByte('&'); + try f.writeCValue(w, operand, .other); try v.elem(f, w); - try f.object.dg.renderBuiltinInfo(w, scalar_ty, info); + try f.dg.renderBuiltinInfo(w, scalar_ty, info); try w.writeAll(");"); - try f.object.newline(); + try f.newline(); try v.end(f, inst, w); return local; @@ -6531,13 +5638,12 @@ fn airBinBuiltinCall( operation: []const u8, info: BuiltinInfo, ) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const operand_ty = f.typeOf(bin_op.lhs); - const operand_ctype = try f.ctypeFromType(operand_ty, .complete); - const is_big = operand_ctype.info(&f.object.dg.ctype_pool) == .array; + const is_big = lowersToBigInt(operand_ty, zcu); const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); @@ -6547,32 +5653,35 @@ fn airBinBuiltinCall( const inst_scalar_ty = inst_ty.scalarType(zcu); const scalar_ty = operand_ty.scalarType(zcu); - const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete); - const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array; + const ref_ret = lowersToBigInt(inst_scalar_ty, zcu); + const ref_arg = lowersToBigInt(scalar_ty, zcu); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); if (is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const v = try Vectorize.start(f, inst, w, operand_ty); if (!ref_ret) { - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try v.elem(f, w); try w.writeAll(" = "); } try w.print("zig_{s}_", .{operation}); - try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty); + try f.dg.renderTypeForBuiltinFnName(w, scalar_ty); try w.writeByte('('); if (ref_ret) { - try f.writeCValue(w, local, .FunctionArgument); + try w.writeByte('&'); + try f.writeCValue(w, local, .other); try v.elem(f, w); try w.writeAll(", "); } - try f.writeCValue(w, lhs, .FunctionArgument); + if (ref_arg) try w.writeByte('&'); + try f.writeCValue(w, lhs, .other); try v.elem(f, w); try w.writeAll(", "); - try f.writeCValue(w, rhs, .FunctionArgument); + if (ref_arg) try w.writeByte('&'); + try f.writeCValue(w, rhs, .other); if (f.typeOf(bin_op.rhs).isVector(zcu)) try v.elem(f, w); - try f.object.dg.renderBuiltinInfo(w, scalar_ty, info); + try f.dg.renderBuiltinInfo(w, scalar_ty, info); try w.writeAll(");\n"); try v.end(f, inst, w); @@ -6587,7 +5696,7 @@ fn airCmpBuiltinCall( operation: enum { cmp, operator }, info: BuiltinInfo, ) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const lhs = try f.resolveInst(data.lhs); const rhs = try f.resolveInst(data.rhs); @@ -6598,14 +5707,14 @@ fn airCmpBuiltinCall( const operand_ty = f.typeOf(data.lhs); const scalar_ty = operand_ty.scalarType(zcu); - const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete); - const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array; + const ref_ret = lowersToBigInt(inst_scalar_ty, zcu); + const ref_arg = lowersToBigInt(scalar_ty, zcu); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, operand_ty); if (!ref_ret) { - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try v.elem(f, w); try w.writeAll(" = "); } @@ -6613,33 +5722,36 @@ fn airCmpBuiltinCall( else => @tagName(operation), .operator => compareOperatorAbbrev(operator), }}); - try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty); + try f.dg.renderTypeForBuiltinFnName(w, scalar_ty); try w.writeByte('('); if (ref_ret) { - try f.writeCValue(w, local, .FunctionArgument); + try w.writeByte('&'); + try f.writeCValue(w, local, .other); try v.elem(f, w); try w.writeAll(", "); } - try f.writeCValue(w, lhs, .FunctionArgument); + if (ref_arg) try w.writeByte('&'); + try f.writeCValue(w, lhs, .other); try v.elem(f, w); try w.writeAll(", "); - try f.writeCValue(w, rhs, .FunctionArgument); + if (ref_arg) try w.writeByte('&'); + try f.writeCValue(w, rhs, .other); try v.elem(f, w); - try f.object.dg.renderBuiltinInfo(w, scalar_ty, info); + try f.dg.renderBuiltinInfo(w, scalar_ty, info); try w.writeByte(')'); if (!ref_ret) try w.print("{s}{f}", .{ compareOperatorC(operator), try f.fmtIntLiteralDec(try pt.intValue(.i32, 0)), }); try w.writeByte(';'); - try f.object.newline(); + try f.newline(); try v.end(f, inst, w); return local; } fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data; @@ -6649,9 +5761,8 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue const new_value = try f.resolveInst(extra.new_value); const ptr_ty = f.typeOf(extra.ptr); const ty = ptr_ty.childType(zcu); - const ctype = try f.ctypeFromType(ty, .complete); - const w = &f.object.code.writer; + const w = &f.code.writer; const new_value_mat = try Materialize.start(f, inst, ty, new_value); try reap(f, inst, &.{ extra.ptr, extra.expected_value, extra.new_value }); @@ -6662,13 +5773,11 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue const local = try f.allocLocal(inst, inst_ty); if (inst_ty.isPtrLikeOptional(zcu)) { - { - const a = try Assignment.start(f, w, ctype); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); - try f.writeCValue(w, expected_value, .Other); - try a.end(f, w); - } + try f.writeCValue(w, local, .other); + try w.writeAll(" = "); + try f.writeCValue(w, expected_value, .other); + try w.writeByte(';'); + try f.newline(); try w.writeAll("if ("); try w.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor}); @@ -6676,9 +5785,9 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue try w.writeByte(')'); if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile"); try w.writeAll(" *)"); - try f.writeCValue(w, ptr, .Other); + try f.writeCValue(w, ptr, .other); try w.writeAll(", "); - try f.writeCValue(w, local, .FunctionArgument); + try f.writeCValue(w, local, .other); try w.writeAll(", "); try new_value_mat.mat(f, w); try w.writeAll(", "); @@ -6686,56 +5795,49 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue try w.writeAll(", "); try writeMemoryOrder(w, extra.failureOrder()); try w.writeAll(", "); - try f.object.dg.renderTypeForBuiltinFnName(w, ty); + try f.dg.renderTypeForBuiltinFnName(w, ty); try w.writeAll(", "); try f.renderType(w, repr_ty); try w.writeByte(')'); try w.writeAll(") {"); - f.object.indent(); - try f.object.newline(); - { - const a = try Assignment.start(f, w, ctype); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); - try w.writeAll("NULL"); - try a.end(f, w); - } - try f.object.outdent(); + f.indent(); + try f.newline(); + + try f.writeCValue(w, local, .other); + try w.writeAll(" = NULL;"); + try f.newline(); + + try f.outdent(); try w.writeByte('}'); - try f.object.newline(); + try f.newline(); } else { - { - const a = try Assignment.start(f, w, ctype); - try f.writeCValueMember(w, local, .{ .identifier = "payload" }); - try a.assign(f, w); - try f.writeCValue(w, expected_value, .Other); - try a.end(f, w); - } - { - const a = try Assignment.start(f, w, .bool); - try f.writeCValueMember(w, local, .{ .identifier = "is_null" }); - try a.assign(f, w); - try w.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor}); - try f.renderType(w, ty); - try w.writeByte(')'); - if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile"); - try w.writeAll(" *)"); - try f.writeCValue(w, ptr, .Other); - try w.writeAll(", "); - try f.writeCValueMember(w, local, .{ .identifier = "payload" }); - try w.writeAll(", "); - try new_value_mat.mat(f, w); - try w.writeAll(", "); - try writeMemoryOrder(w, extra.successOrder()); - try w.writeAll(", "); - try writeMemoryOrder(w, extra.failureOrder()); - try w.writeAll(", "); - try f.object.dg.renderTypeForBuiltinFnName(w, ty); - try w.writeAll(", "); - try f.renderType(w, repr_ty); - try w.writeByte(')'); - try a.end(f, w); - } + try f.writeCValueMember(w, local, .{ .identifier = "payload" }); + try w.writeAll(" = "); + try f.writeCValue(w, expected_value, .other); + try w.writeByte(';'); + try f.newline(); + + try f.writeCValueMember(w, local, .{ .identifier = "is_null" }); + try w.print(" = zig_cmpxchg_{s}((zig_atomic(", .{flavor}); + try f.renderType(w, ty); + try w.writeByte(')'); + if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile"); + try w.writeAll(" *)"); + try f.writeCValue(w, ptr, .other); + try w.writeAll(", "); + try f.writeCValueMember(w, local, .{ .identifier = "payload" }); + try w.writeAll(", "); + try new_value_mat.mat(f, w); + try w.writeAll(", "); + try writeMemoryOrder(w, extra.successOrder()); + try w.writeAll(", "); + try writeMemoryOrder(w, extra.failureOrder()); + try w.writeAll(", "); + try f.dg.renderTypeForBuiltinFnName(w, ty); + try w.writeAll(", "); + try f.renderType(w, repr_ty); + try w.writeAll(");"); + try f.newline(); } try new_value_mat.end(f, inst); @@ -6748,7 +5850,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue } fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const extra = f.air.extraData(Air.AtomicRmw, pl_op.payload).data; @@ -6758,7 +5860,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { const ptr = try f.resolveInst(pl_op.operand); const operand = try f.resolveInst(extra.operand); - const w = &f.object.code.writer; + const w = &f.code.writer; const operand_mat = try Materialize.start(f, inst, ty, operand); try reap(f, inst, &.{ pl_op.operand, extra.operand }); @@ -6771,7 +5873,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { try w.print("zig_atomicrmw_{s}", .{toAtomicRmwSuffix(extra.op())}); if (is_float) try w.writeAll("_float") else if (is_128) try w.writeAll("_int128"); try w.writeByte('('); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(", ("); const use_atomic = switch (extra.op()) { else => true, @@ -6783,17 +5885,17 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { if (use_atomic) try w.writeByte(')'); if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile"); try w.writeAll(" *)"); - try f.writeCValue(w, ptr, .Other); + try f.writeCValue(w, ptr, .other); try w.writeAll(", "); try operand_mat.mat(f, w); try w.writeAll(", "); try writeMemoryOrder(w, extra.ordering()); try w.writeAll(", "); - try f.object.dg.renderTypeForBuiltinFnName(w, ty); + try f.dg.renderTypeForBuiltinFnName(w, ty); try w.writeAll(", "); try f.renderType(w, repr_ty); try w.writeAll(");"); - try f.object.newline(); + try f.newline(); try operand_mat.end(f, inst); if (f.liveness.isUnused(inst)) { @@ -6805,7 +5907,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { } fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const atomic_load = f.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load; const ptr = try f.resolveInst(atomic_load.ptr); @@ -6819,31 +5921,31 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { ty; const inst_ty = f.typeOfIndex(inst); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); try w.writeAll("zig_atomic_load("); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(", (zig_atomic("); try f.renderType(w, ty); try w.writeByte(')'); if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile"); try w.writeAll(" *)"); - try f.writeCValue(w, ptr, .Other); + try f.writeCValue(w, ptr, .other); try w.writeAll(", "); try writeMemoryOrder(w, atomic_load.order); try w.writeAll(", "); - try f.object.dg.renderTypeForBuiltinFnName(w, ty); + try f.dg.renderTypeForBuiltinFnName(w, ty); try w.writeAll(", "); try f.renderType(w, repr_ty); try w.writeAll(");"); - try f.object.newline(); + try f.newline(); return local; } fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ptr_ty = f.typeOf(bin_op.lhs); @@ -6851,7 +5953,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa const ptr = try f.resolveInst(bin_op.lhs); const element = try f.resolveInst(bin_op.rhs); - const w = &f.object.code.writer; + const w = &f.code.writer; const element_mat = try Materialize.start(f, inst, ty, element); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); @@ -6865,32 +5967,22 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa try w.writeByte(')'); if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile"); try w.writeAll(" *)"); - try f.writeCValue(w, ptr, .Other); + try f.writeCValue(w, ptr, .other); try w.writeAll(", "); try element_mat.mat(f, w); try w.print(", {s}, ", .{order}); - try f.object.dg.renderTypeForBuiltinFnName(w, ty); + try f.dg.renderTypeForBuiltinFnName(w, ty); try w.writeAll(", "); try f.renderType(w, repr_ty); try w.writeAll(");"); - try f.object.newline(); + try f.newline(); try element_mat.end(f, inst); return .none; } -fn writeSliceOrPtr(f: *Function, w: *Writer, ptr: CValue, ptr_ty: Type) !void { - const pt = f.object.dg.pt; - const zcu = pt.zcu; - if (ptr_ty.isSlice(zcu)) { - try f.writeCValueMember(w, ptr, .{ .identifier = "ptr" }); - } else { - try f.writeCValue(w, ptr, .FunctionArgument); - } -} - fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const dest_ty = f.typeOf(bin_op.lhs); @@ -6899,7 +5991,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { const elem_ty = f.typeOf(bin_op.rhs); const elem_abi_size = elem_ty.abiSize(zcu); const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |val| val.isUndef(zcu) else false; - const w = &f.object.code.writer; + const w = &f.code.writer; if (val_is_undef) { if (!safety) { @@ -6913,153 +6005,128 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { try f.writeCValueMember(w, dest_slice, .{ .identifier = "ptr" }); try w.writeAll(", 0xaa, "); try f.writeCValueMember(w, dest_slice, .{ .identifier = "len" }); - if (elem_abi_size > 1) { - try w.print(" * {d}", .{elem_abi_size}); - } - try w.writeAll(");"); - try f.object.newline(); }, .one => { - const array_ty = dest_ty.childType(zcu); - const len = array_ty.arrayLen(zcu) * elem_abi_size; - - try f.writeCValue(w, dest_slice, .FunctionArgument); - try w.print(", 0xaa, {d});", .{len}); - try f.object.newline(); + try f.writeCValue(w, dest_slice, .other); + try w.print(", 0xaa, {d}", .{dest_ty.childType(zcu).arrayLen(zcu)}); }, .many, .c => unreachable, } + if (elem_abi_size > 0) try w.print(" * {d}", .{elem_abi_size}); + try w.writeAll(");"); + try f.newline(); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); return .none; } - if (elem_abi_size > 1 or dest_ty.isVolatilePtr(zcu)) { - // For the assignment in this loop, the array pointer needs to get - // casted to a regular pointer, otherwise an error like this occurs: - // error: array type 'uint32_t[20]' (aka 'unsigned int[20]') is not assignable - const elem_ptr_ty = try pt.ptrType(.{ - .child = elem_ty.toIntern(), - .flags = .{ - .size = .c, - }, - }); - - const index = try f.allocLocal(inst, .usize); - - try w.writeAll("for ("); - try f.writeCValue(w, index, .Other); - try w.writeAll(" = "); - try f.object.dg.renderValue(w, .zero_usize, .Other); - try w.writeAll("; "); - try f.writeCValue(w, index, .Other); - try w.writeAll(" != "); + if (elem_abi_size == 1 and !dest_ty.isVolatilePtr(zcu)) { + const bitcasted = try bitcast(f, .u8, value, elem_ty); + try w.writeAll("memset("); switch (dest_ty.ptrSize(zcu)) { .slice => { + try f.writeCValueMember(w, dest_slice, .{ .identifier = "ptr" }); + try w.writeAll(", "); + try f.writeCValue(w, bitcasted, .other); + try w.writeAll(", "); try f.writeCValueMember(w, dest_slice, .{ .identifier = "len" }); }, .one => { - const array_ty = dest_ty.childType(zcu); - try w.print("{d}", .{array_ty.arrayLen(zcu)}); + try f.writeCValue(w, dest_slice, .other); + try w.writeAll(", "); + try f.writeCValue(w, bitcasted, .other); + try w.print(", {d}", .{dest_ty.childType(zcu).arrayLen(zcu)}); }, .many, .c => unreachable, } - try w.writeAll("; ++"); - try f.writeCValue(w, index, .Other); - try w.writeAll(") "); - - const a = try Assignment.start(f, w, try f.ctypeFromType(elem_ty, .complete)); - try w.writeAll("(("); - try f.renderType(w, elem_ptr_ty); - try w.writeByte(')'); - try writeSliceOrPtr(f, w, dest_slice, dest_ty); - try w.writeAll(")["); - try f.writeCValue(w, index, .Other); - try w.writeByte(']'); - try a.assign(f, w); - try f.writeCValue(w, value, .Other); - try a.end(f, w); - + try w.writeAll(");"); + try f.newline(); + try f.freeCValue(inst, bitcasted); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - try freeLocal(f, inst, index.new_local, null); - return .none; } - const bitcasted = try bitcast(f, .u8, value, elem_ty); + // Fallback path: use a `for` loop. - try w.writeAll("memset("); + const index = try f.allocLocal(inst, .usize); + + try w.writeAll("for ("); + try f.writeCValue(w, index, .other); + try w.writeAll(" = "); + try f.dg.renderValue(w, .zero_usize, .other); + try w.writeAll("; "); + try f.writeCValue(w, index, .other); + try w.writeAll(" != "); switch (dest_ty.ptrSize(zcu)) { - .slice => { - try f.writeCValueMember(w, dest_slice, .{ .identifier = "ptr" }); - try w.writeAll(", "); - try f.writeCValue(w, bitcasted, .FunctionArgument); - try w.writeAll(", "); - try f.writeCValueMember(w, dest_slice, .{ .identifier = "len" }); - try w.writeAll(");"); - try f.object.newline(); - }, - .one => { - const array_ty = dest_ty.childType(zcu); - const len = array_ty.arrayLen(zcu) * elem_abi_size; + .slice => try f.writeCValueMember(w, dest_slice, .{ .identifier = "len" }), + .one => try w.print("{d}", .{dest_ty.childType(zcu).arrayLen(zcu)}), + .many, .c => unreachable, + } + try w.writeAll("; ++"); + try f.writeCValue(w, index, .other); + try w.writeAll(") "); - try f.writeCValue(w, dest_slice, .FunctionArgument); - try w.writeAll(", "); - try f.writeCValue(w, bitcasted, .FunctionArgument); - try w.print(", {d});", .{len}); - try f.object.newline(); - }, + switch (dest_ty.ptrSize(zcu)) { + .slice => try f.writeCValueMember(w, dest_slice, .{ .identifier = "ptr" }), + .one => try f.writeCValueDerefMember(w, dest_slice, .{ .identifier = "array" }), .many, .c => unreachable, } - try f.freeCValue(inst, bitcasted); + try w.writeByte('['); + try f.writeCValue(w, index, .other); + try w.writeAll("] = "); + try f.writeCValue(w, value, .other); + try w.writeByte(';'); + try f.newline(); + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); + try freeLocal(f, inst, index.new_local, null); + return .none; } fn airMemcpy(f: *Function, inst: Air.Inst.Index, function_paren: []const u8) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const dest_ptr = try f.resolveInst(bin_op.lhs); const src_ptr = try f.resolveInst(bin_op.rhs); const dest_ty = f.typeOf(bin_op.lhs); const src_ty = f.typeOf(bin_op.rhs); - const w = &f.object.code.writer; + const w = &f.code.writer; if (dest_ty.ptrSize(zcu) != .one) { try w.writeAll("if ("); - try writeArrayLen(f, dest_ptr, dest_ty); + try f.writeCValueMember(w, dest_ptr, .{ .identifier = "len" }); try w.writeAll(" != 0) "); } try w.writeAll(function_paren); - try writeSliceOrPtr(f, w, dest_ptr, dest_ty); + switch (dest_ty.ptrSize(zcu)) { + .slice => try f.writeCValueMember(w, dest_ptr, .{ .identifier = "ptr" }), + .one => try f.writeCValueDerefMember(w, dest_ptr, .{ .identifier = "array" }), + .many, .c => unreachable, + } try w.writeAll(", "); - try writeSliceOrPtr(f, w, src_ptr, src_ty); + switch (src_ty.ptrSize(zcu)) { + .slice => try f.writeCValueMember(w, src_ptr, .{ .identifier = "ptr" }), + .one => try f.writeCValueDerefMember(w, src_ptr, .{ .identifier = "array" }), + .many, .c => try f.writeCValue(w, src_ptr, .other), + } try w.writeAll(", "); - try writeArrayLen(f, dest_ptr, dest_ty); + switch (dest_ty.ptrSize(zcu)) { + .slice => try f.writeCValueMember(w, dest_ptr, .{ .identifier = "len" }), + .one => try w.print("{d}", .{dest_ty.childType(zcu).arrayLen(zcu)}), + .many, .c => unreachable, + } try w.writeAll(" * sizeof("); - try f.renderType(w, dest_ty.elemType2(zcu)); + try f.renderType(w, dest_ty.indexableElem(zcu)); try w.writeAll("));"); - try f.object.newline(); + try f.newline(); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); return .none; } -fn writeArrayLen(f: *Function, dest_ptr: CValue, dest_ty: Type) !void { - const pt = f.object.dg.pt; - const zcu = pt.zcu; - const w = &f.object.code.writer; - switch (dest_ty.ptrSize(zcu)) { - .one => try w.print("{f}", .{ - try f.fmtIntLiteralDec(try pt.intValue(.usize, dest_ty.childType(zcu).arrayLen(zcu))), - }), - .many, .c => unreachable, - .slice => try f.writeCValueMember(w, dest_ptr, .{ .identifier = "len" }), - } -} - fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const union_ptr = try f.resolveInst(bin_op.lhs); @@ -7069,19 +6136,18 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { const union_ty = f.typeOf(bin_op.lhs).childType(zcu); const layout = union_ty.unionGetLayout(zcu); if (layout.tag_size == 0) return .none; - const tag_ty = union_ty.unionTagTypeSafety(zcu).?; - const w = &f.object.code.writer; - const a = try Assignment.start(f, w, try f.ctypeFromType(tag_ty, .complete)); + const w = &f.code.writer; try f.writeCValueDerefMember(w, union_ptr, .{ .identifier = "tag" }); - try a.assign(f, w); - try f.writeCValue(w, new_tag, .Other); - try a.end(f, w); + try w.writeAll(" = "); + try f.writeCValue(w, new_tag, .other); + try w.writeByte(';'); + try f.newline(); return .none; } fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; @@ -7093,17 +6159,20 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { if (layout.tag_size == 0) return .none; const inst_ty = f.typeOfIndex(inst); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete)); - try f.writeCValue(w, local, .Other); - try a.assign(f, w); + try f.writeCValue(w, local, .other); + try w.writeAll(" = "); try f.writeCValueMember(w, operand, .{ .identifier = "tag" }); - try a.end(f, w); + try w.writeByte(';'); + try f.newline(); return local; } fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { + const zcu = f.dg.pt.zcu; + const ip = &zcu.intern_pool; + const gpa = zcu.comp.gpa; const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const inst_ty = f.typeOfIndex(inst); @@ -7111,15 +6180,17 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); - try f.writeCValue(w, local, .Other); - try w.print(" = {s}(", .{ - try f.getLazyFnName(.{ .tag_name = enum_ty.toIntern() }), + try f.writeCValue(w, local, .other); + try f.need_tag_name_funcs.put(gpa, enum_ty.toIntern(), {}); + try w.print(" = zig_tagName_{f}__{d}(", .{ + fmtIdentUnsolo(enum_ty.containerTypeName(ip).toSlice(ip)), + @intFromEnum(enum_ty.toIntern()), }); - try f.writeCValue(w, operand, .Other); + try f.writeCValue(w, operand, .other); try w.writeAll(");"); - try f.object.newline(); + try f.newline(); return local; } @@ -7127,40 +6198,37 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const w = &f.object.code.writer; + const w = &f.code.writer; const inst_ty = f.typeOfIndex(inst); const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); const local = try f.allocLocal(inst, inst_ty); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(" = zig_errorName["); - try f.writeCValue(w, operand, .Other); + try f.writeCValue(w, operand, .other); try w.writeAll(" - 1];"); - try f.object.newline(); + try f.newline(); return local; } fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; - const zcu = pt.zcu; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); const inst_ty = f.typeOfIndex(inst); - const inst_scalar_ty = inst_ty.scalarType(zcu); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, inst_ty); - const a = try Assignment.start(f, w, try f.ctypeFromType(inst_scalar_ty, .complete)); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try v.elem(f, w); - try a.assign(f, w); - try f.writeCValue(w, operand, .Other); - try a.end(f, w); + try w.writeAll(" = "); + try f.writeCValue(w, operand, .other); + try w.writeByte(';'); + try f.newline(); try v.end(f, inst, w); return local; @@ -7177,29 +6245,29 @@ fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.typeOfIndex(inst); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, inst_ty); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try v.elem(f, w); try w.writeAll(" = "); - try f.writeCValue(w, pred, .Other); + try f.writeCValue(w, pred, .other); try v.elem(f, w); try w.writeAll(" ? "); - try f.writeCValue(w, lhs, .Other); + try f.writeCValue(w, lhs, .other); try v.elem(f, w); try w.writeAll(" : "); - try f.writeCValue(w, rhs, .Other); + try f.writeCValue(w, rhs, .other); try v.elem(f, w); try w.writeByte(';'); - try f.object.newline(); + try f.newline(); try v.end(f, inst, w); return local; } fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const unwrapped = f.air.unwrapShuffleOne(zcu, inst); @@ -7207,22 +6275,22 @@ fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(unwrapped.operand); const inst_ty = unwrapped.result_ty; - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); try reap(f, inst, &.{unwrapped.operand}); // local cannot alias operand for (mask, 0..) |mask_elem, out_idx| { - try f.writeCValue(w, local, .Other); + try f.writeCValueMember(w, local, .{ .identifier = "array" }); try w.writeByte('['); - try f.object.dg.renderValue(w, try pt.intValue(.usize, out_idx), .Other); + try f.dg.renderValue(w, try pt.intValue(.usize, out_idx), .other); try w.writeAll("] = "); switch (mask_elem.unwrap()) { .elem => |src_idx| { - try f.writeCValue(w, operand, .Other); + try f.writeCValueMember(w, operand, .{ .identifier = "array" }); try w.writeByte('['); - try f.object.dg.renderValue(w, try pt.intValue(.usize, src_idx), .Other); + try f.dg.renderValue(w, try pt.intValue(.usize, src_idx), .other); try w.writeByte(']'); }, - .value => |val| try f.object.dg.renderValue(w, .fromInterned(val), .Other), + .value => |val| try f.dg.renderValue(w, .fromInterned(val), .other), } try w.writeAll(";\n"); } @@ -7231,7 +6299,7 @@ fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue { } fn airShuffleTwo(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const unwrapped = f.air.unwrapShuffleTwo(zcu, inst); @@ -7241,38 +6309,38 @@ fn airShuffleTwo(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = unwrapped.result_ty; const elem_ty = inst_ty.childType(zcu); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); try reap(f, inst, &.{ unwrapped.operand_a, unwrapped.operand_b }); // local cannot alias operands for (mask, 0..) |mask_elem, out_idx| { - try f.writeCValue(w, local, .Other); + try f.writeCValueMember(w, local, .{ .identifier = "array" }); try w.writeByte('['); - try f.object.dg.renderValue(w, try pt.intValue(.usize, out_idx), .Other); + try f.dg.renderValue(w, try pt.intValue(.usize, out_idx), .other); try w.writeAll("] = "); switch (mask_elem.unwrap()) { .a_elem => |src_idx| { - try f.writeCValue(w, operand_a, .Other); + try f.writeCValueMember(w, operand_a, .{ .identifier = "array" }); try w.writeByte('['); - try f.object.dg.renderValue(w, try pt.intValue(.usize, src_idx), .Other); + try f.dg.renderValue(w, try pt.intValue(.usize, src_idx), .other); try w.writeByte(']'); }, .b_elem => |src_idx| { - try f.writeCValue(w, operand_b, .Other); + try f.writeCValueMember(w, operand_b, .{ .identifier = "array" }); try w.writeByte('['); - try f.object.dg.renderValue(w, try pt.intValue(.usize, src_idx), .Other); + try f.dg.renderValue(w, try pt.intValue(.usize, src_idx), .other); try w.writeByte(']'); }, - .undef => try f.object.dg.renderUndefValue(w, elem_ty, .Other), + .undef => try f.dg.renderUndefValue(w, elem_ty, .other), } try w.writeByte(';'); - try f.object.newline(); + try f.newline(); } return local; } fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const reduce = f.air.instructions.items(.data)[@intFromEnum(inst)].reduce; @@ -7280,7 +6348,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(reduce.operand); try reap(f, inst, &.{reduce.operand}); const operand_ty = f.typeOf(reduce.operand); - const w = &f.object.code.writer; + const w = &f.code.writer; const use_operator = scalar_ty.bitSize(zcu) <= 64; const op: union(enum) { @@ -7327,10 +6395,10 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { // } const accum = try f.allocLocal(inst, scalar_ty); - try f.writeCValue(w, accum, .Other); + try f.writeCValue(w, accum, .other); try w.writeAll(" = "); - try f.object.dg.renderValue(w, switch (reduce.operation) { + try f.dg.renderValue(w, switch (reduce.operation) { .Or, .Xor => switch (scalar_ty.zigTypeTag(zcu)) { .bool => Value.false, .int => try pt.intValue(scalar_ty, 0), @@ -7366,58 +6434,58 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { .float => try pt.floatValue(scalar_ty, std.math.nan(f128)), else => unreachable, }, - }, .Other); + }, .other); try w.writeByte(';'); - try f.object.newline(); + try f.newline(); const v = try Vectorize.start(f, inst, w, operand_ty); - try f.writeCValue(w, accum, .Other); + try f.writeCValue(w, accum, .other); switch (op) { .builtin => |func| { try w.print(" = zig_{s}_", .{func.operation}); - try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty); + try f.dg.renderTypeForBuiltinFnName(w, scalar_ty); try w.writeByte('('); - try f.writeCValue(w, accum, .FunctionArgument); + try f.writeCValue(w, accum, .other); try w.writeAll(", "); - try f.writeCValue(w, operand, .Other); + try f.writeCValue(w, operand, .other); try v.elem(f, w); - try f.object.dg.renderBuiltinInfo(w, scalar_ty, func.info); + try f.dg.renderBuiltinInfo(w, scalar_ty, func.info); try w.writeByte(')'); }, .infix => |ass| { try w.writeAll(ass); - try f.writeCValue(w, operand, .Other); + try f.writeCValue(w, operand, .other); try v.elem(f, w); }, .ternary => |cmp| { try w.writeAll(" = "); - try f.writeCValue(w, accum, .Other); + try f.writeCValue(w, accum, .other); try w.writeAll(cmp); - try f.writeCValue(w, operand, .Other); + try f.writeCValue(w, operand, .other); try v.elem(f, w); try w.writeAll(" ? "); - try f.writeCValue(w, accum, .Other); + try f.writeCValue(w, accum, .other); try w.writeAll(" : "); - try f.writeCValue(w, operand, .Other); + try f.writeCValue(w, operand, .other); try v.elem(f, w); }, } try w.writeByte(';'); - try f.object.newline(); + try f.newline(); try v.end(f, inst, w); return accum; } fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const inst_ty = f.typeOfIndex(inst); const len: usize = @intCast(inst_ty.arrayLen(zcu)); const elements: []const Air.Inst.Ref = @ptrCast(f.air.extra.items[ty_pl.payload..][0..len]); - const gpa = f.object.dg.gpa; + const gpa = f.dg.gpa; const resolved_elements = try gpa.alloc(CValue, elements.len); defer gpa.free(resolved_elements); for (resolved_elements, elements) |*resolved_element, element| { @@ -7430,28 +6498,23 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { } } - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); switch (ip.indexToKey(inst_ty.toIntern())) { inline .array_type, .vector_type => |info, tag| { - const a: Assignment = .{ - .ctype = try f.ctypeFromType(.fromInterned(info.child), .complete), - }; for (resolved_elements, 0..) |element, i| { - try a.restart(f, w); - try f.writeCValue(w, local, .Other); - try w.print("[{d}]", .{i}); - try a.assign(f, w); - try f.writeCValue(w, element, .Other); - try a.end(f, w); + try f.writeCValueMember(w, local, .{ .identifier = "array" }); + try w.print("[{d}] = ", .{i}); + try f.writeCValue(w, element, .other); + try w.writeByte(';'); + try f.newline(); } if (tag == .array_type and info.sentinel != .none) { - try a.restart(f, w); - try f.writeCValue(w, local, .Other); - try w.print("[{d}]", .{info.len}); - try a.assign(f, w); - try f.object.dg.renderValue(w, Value.fromInterned(info.sentinel), .Other); - try a.end(f, w); + try f.writeCValueMember(w, local, .{ .identifier = "array" }); + try w.print("[{d}] = ", .{info.len}); + try f.dg.renderValue(w, Value.fromInterned(info.sentinel), .other); + try w.writeByte(';'); + try f.newline(); } }, .struct_type => { @@ -7461,13 +6524,13 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { var field_it = loaded_struct.iterateRuntimeOrder(ip); while (field_it.next()) |field_index| { const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!field_ty.hasRuntimeBits(zcu)) continue; - const a = try Assignment.start(f, w, try f.ctypeFromType(field_ty, .complete)); - try f.writeCValueMember(w, local, .{ .identifier = loaded_struct.fieldName(ip, field_index).toSlice(ip) }); - try a.assign(f, w); - try f.writeCValue(w, resolved_elements[field_index], .Other); - try a.end(f, w); + try f.writeCValueMember(w, local, .{ .identifier = loaded_struct.field_names.get(ip)[field_index].toSlice(ip) }); + try w.writeAll(" = "); + try f.writeCValue(w, resolved_elements[field_index], .other); + try w.writeByte(';'); + try f.newline(); } }, .@"packed" => unreachable, // `Air.Legalize.Feature.expand_packed_struct_init` handles this case @@ -7476,13 +6539,13 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { .tuple_type => |tuple_info| for (0..tuple_info.types.len) |field_index| { if (tuple_info.values.get(ip)[field_index] != .none) continue; const field_ty: Type = .fromInterned(tuple_info.types.get(ip)[field_index]); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!field_ty.hasRuntimeBits(zcu)) continue; - const a = try Assignment.start(f, w, try f.ctypeFromType(field_ty, .complete)); try f.writeCValueMember(w, local, .{ .field = field_index }); - try a.assign(f, w); - try f.writeCValue(w, resolved_elements[field_index], .Other); - try a.end(f, w); + try w.writeAll(" = "); + try f.writeCValue(w, resolved_elements[field_index], .other); + try w.writeByte(';'); + try f.newline(); }, else => unreachable, } @@ -7491,49 +6554,52 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { } fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = f.air.extraData(Air.UnionInit, ty_pl.payload).data; + const field_index = extra.field_index; const union_ty = f.typeOfIndex(inst); const loaded_union = ip.loadUnionType(union_ty.toIntern()); - const field_name = loaded_union.loadTagType(ip).names.get(ip)[extra.field_index]; - const payload_ty = f.typeOf(extra.init); + const loaded_enum = ip.loadEnumType(loaded_union.enum_tag_type); + const payload = try f.resolveInst(extra.init); try reap(f, inst, &.{extra.init}); - const w = &f.object.code.writer; - if (loaded_union.flagsUnordered(ip).layout == .@"packed") return f.moveCValue(inst, union_ty, payload); + const w = &f.code.writer; + if (loaded_union.layout == .@"packed") return f.moveCValue(inst, union_ty, payload); const local = try f.allocLocal(inst, union_ty); - const field: CValue = if (union_ty.unionTagTypeSafety(zcu)) |tag_ty| field: { - const layout = union_ty.unionGetLayout(zcu); - if (layout.tag_size != 0) { - const field_index = tag_ty.enumFieldIndex(field_name, zcu).?; - const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index); - - const a = try Assignment.start(f, w, try f.ctypeFromType(tag_ty, .complete)); - try f.writeCValueMember(w, local, .{ .identifier = "tag" }); - try a.assign(f, w); - try w.print("{f}", .{try f.fmtIntLiteralDec(try tag_val.intFromEnum(tag_ty, pt))}); - try a.end(f, w); + if (loaded_union.has_runtime_tag) { + try f.writeCValueMember(w, local, .{ .identifier = "tag" }); + if (loaded_enum.field_values.len == 0) { + // auto-numbered + try w.print(" = {d};", .{field_index}); + } else { + const tag_int_val: Value = .fromInterned(loaded_enum.field_values.get(ip)[field_index]); + try w.print(" = {f};", .{try f.fmtIntLiteralDec(tag_int_val)}); } - break :field .{ .payload_identifier = field_name.toSlice(ip) }; - } else .{ .identifier = field_name.toSlice(ip) }; - - const a = try Assignment.start(f, w, try f.ctypeFromType(payload_ty, .complete)); - try f.writeCValueMember(w, local, field); - try a.assign(f, w); - try f.writeCValue(w, payload, .Other); - try a.end(f, w); + try f.newline(); + } + + const field_name_slice = loaded_enum.field_names.get(ip)[field_index].toSlice(ip); + switch (loaded_union.layout) { + .auto => try f.writeCValueMember(w, local, .{ .payload_identifier = field_name_slice }), + .@"extern" => try f.writeCValueMember(w, local, .{ .identifier = field_name_slice }), + .@"packed" => unreachable, + } + try w.writeAll(" = "); + try f.writeCValue(w, payload, .other); + try w.writeByte(';'); + try f.newline(); return local; } fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const prefetch = f.air.instructions.items(.data)[@intFromEnum(inst)].prefetch; @@ -7541,16 +6607,16 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { const ptr = try f.resolveInst(prefetch.ptr); try reap(f, inst, &.{prefetch.ptr}); - const w = &f.object.code.writer; + const w = &f.code.writer; switch (prefetch.cache) { .data => { try w.writeAll("zig_prefetch("); if (ptr_ty.isSlice(zcu)) try f.writeCValueMember(w, ptr, .{ .identifier = "ptr" }) else - try f.writeCValue(w, ptr, .FunctionArgument); + try f.writeCValue(w, ptr, .other); try w.print(", {d}, {d});", .{ @intFromEnum(prefetch.rw), prefetch.locality }); - try f.object.newline(); + try f.newline(); }, // The available prefetch intrinsics do not accept a cache argument; only // address, rw, and locality. @@ -7563,14 +6629,14 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue { const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const w = &f.object.code.writer; + const w = &f.code.writer; const inst_ty = f.typeOfIndex(inst); const local = try f.allocLocal(inst, inst_ty); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(" = "); try w.print("zig_wasm_memory_size({d});", .{pl_op.payload}); - try f.object.newline(); + try f.newline(); return local; } @@ -7578,23 +6644,23 @@ fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue { fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue { const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const w = &f.object.code.writer; + const w = &f.code.writer; const inst_ty = f.typeOfIndex(inst); const operand = try f.resolveInst(pl_op.operand); try reap(f, inst, &.{pl_op.operand}); const local = try f.allocLocal(inst, inst_ty); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(" = "); try w.print("zig_wasm_memory_grow({d}, ", .{pl_op.payload}); - try f.writeCValue(w, operand, .FunctionArgument); + try f.writeCValue(w, operand, .other); try w.writeAll(");"); - try f.object.newline(); + try f.newline(); return local; } fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const bin_op = f.air.extraData(Air.Bin, pl_op.payload).data; @@ -7607,24 +6673,24 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.typeOfIndex(inst); const inst_scalar_ty = inst_ty.scalarType(zcu); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, w, inst_ty); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try v.elem(f, w); try w.writeAll(" = zig_fma_"); - try f.object.dg.renderTypeForBuiltinFnName(w, inst_scalar_ty); + try f.dg.renderTypeForBuiltinFnName(w, inst_scalar_ty); try w.writeByte('('); - try f.writeCValue(w, mulend1, .FunctionArgument); + try f.writeCValue(w, mulend1, .other); try v.elem(f, w); try w.writeAll(", "); - try f.writeCValue(w, mulend2, .FunctionArgument); + try f.writeCValue(w, mulend2, .other); try v.elem(f, w); try w.writeAll(", "); - try f.writeCValue(w, addend, .FunctionArgument); + try f.writeCValue(w, addend, .other); try v.elem(f, w); try w.writeAll(");"); - try f.object.newline(); + try f.newline(); try v.end(f, inst, w); return local; @@ -7632,34 +6698,33 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { fn airRuntimeNavPtr(f: *Function, inst: Air.Inst.Index) !CValue { const ty_nav = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, .fromInterned(ty_nav.ty)); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(" = "); - try f.object.dg.renderNav(w, ty_nav.nav, .Other); + try f.dg.renderNav(w, ty_nav.nav, .other); try w.writeByte(';'); - try f.object.newline(); + try f.newline(); return local; } fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; const inst_ty = f.typeOfIndex(inst); - const function_ty = zcu.navValue(f.object.dg.pass.nav).typeOf(zcu); - const function_info = (try f.ctypeFromType(function_ty, .complete)).info(&f.object.dg.ctype_pool).function; - assert(function_info.varargs); - const w = &f.object.code.writer; + assert(Value.fromInterned(f.func_index).typeOf(zcu).fnIsVarArgs(zcu)); + + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); try w.writeAll("va_start(*(va_list *)&"); - try f.writeCValue(w, local, .Other); - if (function_info.param_ctypes.len > 0) { + try f.writeCValue(w, local, .other); + if (f.next_arg_index > 0) { try w.writeAll(", "); - try f.writeCValue(w, .{ .arg = function_info.param_ctypes.len - 1 }, .FunctionArgument); + try f.writeCValue(w, .{ .arg = f.next_arg_index - 1 }, .other); } try w.writeAll(");"); - try f.object.newline(); + try f.newline(); return local; } @@ -7670,15 +6735,15 @@ fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue { const va_list = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(" = va_arg(*(va_list *)"); - try f.writeCValue(w, va_list, .Other); + try f.writeCValue(w, va_list, .other); try w.writeAll(", "); try f.renderType(w, ty_op.ty.toType()); try w.writeAll(");"); - try f.object.newline(); + try f.newline(); return local; } @@ -7688,11 +6753,11 @@ fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue { const va_list = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); - const w = &f.object.code.writer; + const w = &f.code.writer; try w.writeAll("va_end(*(va_list *)"); - try f.writeCValue(w, va_list, .Other); + try f.writeCValue(w, va_list, .other); try w.writeAll(");"); - try f.object.newline(); + try f.newline(); return .none; } @@ -7703,14 +6768,14 @@ fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue { const va_list = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); - const w = &f.object.code.writer; + const w = &f.code.writer; const local = try f.allocLocal(inst, inst_ty); try w.writeAll("va_copy(*(va_list *)&"); - try f.writeCValue(w, local, .Other); + try f.writeCValue(w, local, .other); try w.writeAll(", *(va_list *)"); - try f.writeCValue(w, va_list, .Other); + try f.writeCValue(w, va_list, .other); try w.writeAll(");"); - try f.object.newline(); + try f.newline(); return local; } @@ -8027,103 +7092,193 @@ fn undefPattern(comptime IntType: type) IntType { const FormatIntLiteralContext = struct { dg: *DeclGen, - int_info: InternPool.Key.IntType, - kind: CType.Kind, - ctype: CType, + loc: ValueRenderLocation, val: Value, + cty: CType, base: u8, case: std.fmt.Case, }; fn formatIntLiteral(data: FormatIntLiteralContext, w: *Writer) Writer.Error!void { - const pt = data.dg.pt; - const zcu = pt.zcu; - const target = &data.dg.mod.resolved_target.result; - const ctype_pool = &data.dg.ctype_pool; - - const ExpectedContents = struct { - const base = 10; - const bits = 128; - const limbs_count = BigInt.calcTwosCompLimbCount(bits); - - undef_limbs: [limbs_count]BigIntLimb, - wrap_limbs: [limbs_count]BigIntLimb, - to_string_buf: [bits]u8, - to_string_limbs: [BigInt.calcToStringLimbsBufferLen(limbs_count, base)]BigIntLimb, - }; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), data.dg.gpa); - const allocator = stack.get(); - - var undef_limbs: []BigIntLimb = &.{}; - defer allocator.free(undef_limbs); - - var int_buf: Value.BigIntSpace = undefined; - const int = if (data.val.isUndef(zcu)) blk: { - undef_limbs = allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(data.int_info.bits)) catch return error.WriteFailed; - @memset(undef_limbs, undefPattern(BigIntLimb)); - - var undef_int = BigInt.Mutable{ - .limbs = undef_limbs, - .len = undef_limbs.len, - .positive = true, - }; - undef_int.truncate(undef_int.toConst(), data.int_info.signedness, data.int_info.bits); - break :blk undef_int.toConst(); - } else data.val.toBigInt(&int_buf, zcu); - assert(int.fitsInTwosComp(data.int_info.signedness, data.int_info.bits)); - - const c_bits: usize = @intCast(data.ctype.byteSize(ctype_pool, data.dg.mod) * 8); - var one_limbs: [BigInt.calcLimbLen(1)]BigIntLimb = undefined; - const one = BigInt.Mutable.init(&one_limbs, 1).toConst(); - - var wrap = BigInt.Mutable{ - .limbs = allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(c_bits)) catch return error.WriteFailed, - .len = undefined, - .positive = undefined, - }; - defer allocator.free(wrap.limbs); - - const c_limb_info: struct { - ctype: CType, - count: usize, - endian: std.builtin.Endian, - homogeneous: bool, - } = switch (data.ctype.info(ctype_pool)) { - .basic => |basic_info| switch (basic_info) { - else => .{ - .ctype = .void, - .count = 1, - .endian = .little, - .homogeneous = true, + const dg = data.dg; + const zcu = dg.pt.zcu; + const target = &dg.mod.resolved_target.result; + + const val = data.val; + const ty = val.typeOf(zcu); + + assert(!val.isUndef(zcu)); + + var space: Value.BigIntSpace = undefined; + const val_bigint = val.toBigInt(&space, zcu); + + switch (CType.classifyInt(ty, zcu)) { + .void => unreachable, // opv + .small => |int_cty| return FormatInt128.format(.{ + .target = zcu.getTarget(), + .int_cty = int_cty, + .val = val_bigint, + .is_global = data.loc == .static_initializer, + .base = data.base, + .case = data.case, + }, w), + .big => |big| { + if (!data.loc.isInitializer()) { + // Use `CType.fmtTypeName` directly to avoid the possibility of `error.OutOfMemory`. + try w.print("({f})", .{data.cty.fmtTypeName(zcu)}); + } + + try w.writeAll("{{"); + + var limb_buf: [std.math.big.int.calcTwosCompLimbCount(65535)]std.math.big.Limb = undefined; + for (0..big.limbs_len) |limb_index| { + if (limb_index != 0) try w.writeAll(", "); + const limb_bit_offset: u16 = switch (target.cpu.arch.endian()) { + .little => @intCast(limb_index * big.limb_size.bits()), + .big => @intCast((big.limbs_len - limb_index - 1) * big.limb_size.bits()), + }; + var limb_bigint: std.math.big.int.Mutable = .{ + .limbs = &limb_buf, + .len = undefined, + .positive = undefined, + }; + limb_bigint.shiftRight(val_bigint, limb_bit_offset); + limb_bigint.truncate(limb_bigint.toConst(), .unsigned, big.limb_size.bits()); + try FormatInt128.format(.{ + .target = zcu.getTarget(), + .int_cty = big.limb_size.unsigned(), + .val = limb_bigint.toConst(), + .is_global = data.loc == .static_initializer, + .base = data.base, + .case = data.case, + }, w); + } + + try w.writeAll("}}"); + }, + } +} +const FormatInt128 = struct { + target: *const std.Target, + int_cty: CType.Int, + val: std.math.big.int.Const, + is_global: bool, + base: u8, + case: std.fmt.Case, + pub fn format(data: FormatInt128, w: *Writer) Writer.Error!void { + const target = data.target; + + const val = data.val; + const is_global = data.is_global; + const base = data.base; + const case = data.case; + + switch (data.int_cty) { + .uint8_t, + .uint16_t, + .uint32_t, + .uint64_t, + .@"unsigned short", + .@"unsigned int", + .@"unsigned long", + .@"unsigned long long", + .uintptr_t, + => |t| try w.print("{f}", .{ + fmtUnsignedIntLiteralSmall(target, t, val.toInt(u64) catch unreachable, is_global, base, case), + }), + + .int8_t, + .int16_t, + .int32_t, + .int64_t, + .char, + .@"signed short", + .@"signed int", + .@"signed long", + .@"signed long long", + .intptr_t, + => |t| try w.print("{f}", .{ + fmtSignedIntLiteralSmall(target, t, val.toInt(i64) catch unreachable, is_global, base, case), + }), + + .zig_u128 => { + const raw = val.toInt(u128) catch unreachable; + const lo: u64 = @truncate(raw); + const hi: u64 = @intCast(raw >> 64); + const macro_name: []const u8 = if (is_global) "zig_init_u128" else "zig_make_u128"; + try w.print("{s}({f}, {f})", .{ + macro_name, + fmtUnsignedIntLiteralSmall(target, .uint64_t, hi, is_global, base, case), + fmtUnsignedIntLiteralSmall(target, .uint64_t, lo, is_global, base, case), + }); }, - .zig_u128, .zig_i128 => .{ - .ctype = .u64, - .count = 2, - .endian = .big, - .homogeneous = false, + + .zig_i128 => { + const raw = val.toInt(i128) catch unreachable; + const lo: u64 = @truncate(@as(u128, @bitCast(raw))); + const hi: i64 = @intCast(raw >> 64); + const macro_name: []const u8 = if (is_global) "zig_init_i128" else "zig_make_i128"; + try w.print("{s}({f}, {f})", .{ + macro_name, + fmtSignedIntLiteralSmall(target, .int64_t, hi, is_global, base, case), + fmtUnsignedIntLiteralSmall(target, .uint64_t, lo, is_global, base, case), + }); }, - }, - .array => |array_info| .{ - .ctype = array_info.elem_ctype, - .count = @intCast(array_info.len), - .endian = target.cpu.arch.endian(), - .homogeneous = true, - }, - else => unreachable, + } + } +}; +fn fmtUnsignedIntLiteralSmall( + target: *const std.Target, + int_cty: CType.Int, + val: u64, + is_global: bool, + base: u8, + case: std.fmt.Case, +) FormatUnsignedIntLiteralSmall { + return .{ + .target = target, + .int_cty = int_cty, + .val = val, + .is_global = is_global, + .base = base, + .case = case, }; - if (c_limb_info.count == 1) { - if (wrap.addWrap(int, one, data.int_info.signedness, c_bits) or - data.int_info.signedness == .signed and wrap.subWrap(int, one, data.int_info.signedness, c_bits)) - return w.print("{s}_{s}", .{ - data.ctype.getStandardDefineAbbrev() orelse return w.print("zig_{s}Int_{c}{d}", .{ - if (int.positive) "max" else "min", signAbbrev(data.int_info.signedness), c_bits, - }), - if (int.positive) "MAX" else "MIN", - }); - - if (!int.positive) try w.writeByte('-'); - try data.ctype.renderLiteralPrefix(w, data.kind, ctype_pool); +} +fn fmtSignedIntLiteralSmall( + target: *const std.Target, + int_cty: CType.Int, + val: i64, + is_global: bool, + base: u8, + case: std.fmt.Case, +) FormatSignedIntLiteralSmall { + return .{ + .target = target, + .int_cty = int_cty, + .val = val, + .is_global = is_global, + .base = base, + .case = case, + }; +} +const FormatSignedIntLiteralSmall = struct { + target: *const std.Target, + int_cty: CType.Int, + val: i64, + is_global: bool, + base: u8, + case: std.fmt.Case, + pub fn format(data: FormatSignedIntLiteralSmall, w: *Writer) Writer.Error!void { + const bits = data.int_cty.bits(data.target); + const max_int: i64 = @bitCast((@as(u64, 1) << @intCast(bits - 1)) - 1); + const min_int: i64 = @bitCast(@as(u64, 1) << @intCast(bits - 1)); + if (data.val == max_int) { + return w.print("{s}_MAX", .{minMaxMacroPrefix(data.int_cty)}); + } else if (data.val == min_int) { + return w.print("{s}_MIN", .{minMaxMacroPrefix(data.int_cty)}); + } + if (data.val < 0) try w.writeByte('-'); + try w.writeAll(intLiteralPrefix(data.int_cty, data.is_global)); switch (data.base) { 2 => try w.writeAll("0b"), 8 => try w.writeByte('0'), @@ -8131,68 +7286,131 @@ fn formatIntLiteral(data: FormatIntLiteralContext, w: *Writer) Writer.Error!void 16 => try w.writeAll("0x"), else => unreachable, } - const string = int.abs().toStringAlloc(allocator, data.base, data.case) catch - return error.WriteFailed; - defer allocator.free(string); - try w.writeAll(string); - } else { - try data.ctype.renderLiteralPrefix(w, data.kind, ctype_pool); - wrap.truncate(int, .unsigned, c_bits); - @memset(wrap.limbs[wrap.len..], 0); - wrap.len = wrap.limbs.len; - const limbs_per_c_limb = @divExact(wrap.len, c_limb_info.count); - - var c_limb_int_info: std.builtin.Type.Int = .{ - .signedness = undefined, - .bits = @intCast(@divExact(c_bits, c_limb_info.count)), - }; - var c_limb_ctype: CType = undefined; - - var limb_offset: usize = 0; - const most_significant_limb_i = wrap.len - limbs_per_c_limb; - while (limb_offset < wrap.len) : (limb_offset += limbs_per_c_limb) { - const limb_i = switch (c_limb_info.endian) { - .little => limb_offset, - .big => most_significant_limb_i - limb_offset, - }; - var c_limb_mut = BigInt.Mutable{ - .limbs = wrap.limbs[limb_i..][0..limbs_per_c_limb], - .len = undefined, - .positive = true, - }; - c_limb_mut.normalize(limbs_per_c_limb); - - if (limb_i == most_significant_limb_i and - !c_limb_info.homogeneous and data.int_info.signedness == .signed) - { - // most significant limb is actually signed - c_limb_int_info.signedness = .signed; - c_limb_ctype = c_limb_info.ctype.toSigned(); - - c_limb_mut.truncate( - c_limb_mut.toConst(), - .signed, - data.int_info.bits - limb_i * @bitSizeOf(BigIntLimb), - ); - } else { - c_limb_int_info.signedness = .unsigned; - c_limb_ctype = c_limb_info.ctype; - } - - if (limb_offset > 0) try w.writeAll(", "); - try formatIntLiteral(.{ - .dg = data.dg, - .int_info = c_limb_int_info, - .kind = data.kind, - .ctype = c_limb_ctype, - .val = pt.intValue_big(.comptime_int, c_limb_mut.toConst()) catch - return error.WriteFailed, - .base = data.base, - .case = data.case, - }, w); + // This `@abs` is safe thanks to the `min_int` case above. + try w.printInt(@abs(data.val), data.base, data.case, .{}); + try w.writeAll(intLiteralSuffix(data.int_cty)); + } +}; +const FormatUnsignedIntLiteralSmall = struct { + target: *const std.Target, + int_cty: CType.Int, + val: u64, + is_global: bool, + base: u8, + case: std.fmt.Case, + pub fn format(data: FormatUnsignedIntLiteralSmall, w: *Writer) Writer.Error!void { + const bits = data.int_cty.bits(data.target); + const max_int: u64 = @as(u64, std.math.maxInt(u64)) >> @intCast(64 - bits); + if (data.val == max_int) { + return w.print("{s}_MAX", .{minMaxMacroPrefix(data.int_cty)}); + } + try w.writeAll(intLiteralPrefix(data.int_cty, data.is_global)); + switch (data.base) { + 2 => try w.writeAll("0b"), + 8 => try w.writeByte('0'), + 10 => {}, + 16 => try w.writeAll("0x"), + else => unreachable, } + try w.printInt(data.val, data.base, data.case, .{}); + try w.writeAll(intLiteralSuffix(data.int_cty)); } - try data.ctype.renderLiteralSuffix(w, ctype_pool); +}; +fn minMaxMacroPrefix(int_cty: CType.Int) []const u8 { + return switch (int_cty) { + // zig fmt: off + .char => "CHAR", + + .@"unsigned short" => "USHRT", + .@"unsigned int" => "UINT", + .@"unsigned long" => "ULONG", + .@"unsigned long long" => "ULLONG", + + .@"signed short" => "SHRT", + .@"signed int" => "INT", + .@"signed long" => "LONG", + .@"signed long long" => "LLONG", + + .uint8_t => "UINT8", + .uint16_t => "UINT16", + .uint32_t => "UINT32", + .uint64_t => "UINT64", + .zig_u128 => unreachable, + + .int8_t => "INT8", + .int16_t => "INT16", + .int32_t => "INT32", + .int64_t => "INT64", + .zig_i128 => unreachable, + + .uintptr_t => "UINTPTR", + .intptr_t => "INTPTR", + // zig fmt: on + }; +} +fn intLiteralPrefix(cty: CType.Int, is_global: bool) []const u8 { + return switch (cty) { + // zig fmt: off + .char => if (is_global) "" else "(char)", + + .@"unsigned short" => if (is_global) "" else "(unsigned short)", + .@"unsigned int" => "", + .@"unsigned long" => "", + .@"unsigned long long" => "", + + .@"signed short" => if (is_global) "" else "(signed short)", + .@"signed int" => "", + .@"signed long" => "", + .@"signed long long" => "", + + .uint8_t => "UINT8_C(", + .uint16_t => "UINT16_C(", + .uint32_t => "UINT32_C(", + .uint64_t => "UINT64_C(", + .zig_u128 => unreachable, + + .int8_t => "INT8_C(", + .int16_t => "INT16_C(", + .int32_t => "INT32_C(", + .int64_t => "INT64_C(", + .zig_i128 => unreachable, + + .uintptr_t => if (is_global) "" else "(uintptr_t)", + .intptr_t => if (is_global) "" else "(intptr_t)", + // zig fmt: on + }; +} +fn intLiteralSuffix(cty: CType.Int) []const u8 { + return switch (cty) { + // zig fmt: off + .char => "", + + .@"unsigned short" => "u", + .@"unsigned int" => "u", + .@"unsigned long" => "ul", + .@"unsigned long long" => "ull", + + .@"signed short" => "", + .@"signed int" => "", + .@"signed long" => "l", + .@"signed long long" => "ll", + + .uint8_t => ")", + .uint16_t => ")", + .uint32_t => ")", + .uint64_t => ")", + .zig_u128 => unreachable, + + .int8_t => ")", + .int16_t => ")", + .int32_t => ")", + .int64_t => ")", + .zig_i128 => unreachable, + + .uintptr_t => "ul", + .intptr_t => "", + // zig fmt: on + }; } const Materialize = struct { @@ -8207,7 +7425,7 @@ const Materialize = struct { } pub fn mat(self: Materialize, f: *Function, w: *Writer) !void { - try f.writeCValue(w, self.local, .Other); + try f.writeCValue(w, self.local, .other); } pub fn end(self: Materialize, f: *Function, inst: Air.Inst.Index) !void { @@ -8215,95 +7433,52 @@ const Materialize = struct { } }; -const Assignment = struct { - ctype: CType, - - pub fn start(f: *Function, w: *Writer, ctype: CType) !Assignment { - const self: Assignment = .{ .ctype = ctype }; - try self.restart(f, w); - return self; - } - - pub fn restart(self: Assignment, f: *Function, w: *Writer) !void { - switch (self.strategy(f)) { - .assign => {}, - .memcpy => try w.writeAll("memcpy("), - } - } - - pub fn assign(self: Assignment, f: *Function, w: *Writer) !void { - switch (self.strategy(f)) { - .assign => try w.writeAll(" = "), - .memcpy => try w.writeAll(", "), - } - } - - pub fn end(self: Assignment, f: *Function, w: *Writer) !void { - switch (self.strategy(f)) { - .assign => {}, - .memcpy => { - try w.writeAll(", sizeof("); - try f.renderCType(w, self.ctype); - try w.writeAll("))"); - }, - } - try w.writeByte(';'); - try f.object.newline(); - } - - fn strategy(self: Assignment, f: *Function) enum { assign, memcpy } { - return switch (self.ctype.info(&f.object.dg.ctype_pool)) { - else => .assign, - .array, .vector => .memcpy, - }; - } -}; - const Vectorize = struct { index: CValue = .none, pub fn start(f: *Function, inst: Air.Inst.Index, w: *Writer, ty: Type) !Vectorize { - const pt = f.object.dg.pt; + const pt = f.dg.pt; const zcu = pt.zcu; - return if (ty.zigTypeTag(zcu) == .vector) index: { - const local = try f.allocLocal(inst, .usize); - - try w.writeAll("for ("); - try f.writeCValue(w, local, .Other); - try w.print(" = {f}; ", .{try f.fmtIntLiteralDec(.zero_usize)}); - try f.writeCValue(w, local, .Other); - try w.print(" < {f}; ", .{try f.fmtIntLiteralDec(try pt.intValue(.usize, ty.vectorLen(zcu)))}); - try f.writeCValue(w, local, .Other); - try w.print(" += {f}) {{\n", .{try f.fmtIntLiteralDec(.one_usize)}); - f.object.indent(); - try f.object.newline(); - - break :index .{ .index = local }; - } else .{}; + switch (ty.zigTypeTag(zcu)) { + else => return .{ .index = .none }, + .vector => { + const local = try f.allocLocal(inst, .usize); + try w.writeAll("for ("); + try f.writeCValue(w, local, .other); + try w.print(" = {f}; ", .{try f.fmtIntLiteralDec(.zero_usize)}); + try f.writeCValue(w, local, .other); + try w.print(" < {f}; ", .{try f.fmtIntLiteralDec(try pt.intValue(.usize, ty.vectorLen(zcu)))}); + try f.writeCValue(w, local, .other); + try w.print(" += {f}) {{", .{try f.fmtIntLiteralDec(.one_usize)}); + f.indent(); + try f.newline(); + return .{ .index = local }; + }, + } } pub fn elem(self: Vectorize, f: *Function, w: *Writer) !void { if (self.index != .none) { - try w.writeByte('['); - try f.writeCValue(w, self.index, .Other); + try w.writeAll(".array["); + try f.writeCValue(w, self.index, .other); try w.writeByte(']'); } } pub fn end(self: Vectorize, f: *Function, inst: Air.Inst.Index, w: *Writer) !void { if (self.index != .none) { - try f.object.outdent(); + try f.outdent(); try w.writeByte('}'); - try f.object.newline(); + try f.newline(); try freeLocal(f, inst, self.index.new_local, null); } } }; -fn lowersToArray(ty: Type, zcu: *Zcu) bool { +fn lowersToBigInt(ty: Type, zcu: *const Zcu) bool { return switch (ty.zigTypeTag(zcu)) { - .array, .vector => return true, - else => return ty.isAbiInt(zcu) and toCIntBits(@as(u32, @intCast(ty.bitSize(zcu)))) == null, + .int, .@"enum", .@"struct", .@"union" => CType.classifyInt(ty, zcu) == .big, + else => false, }; } @@ -8329,8 +7504,8 @@ fn die(f: *Function, inst: Air.Inst.Index, ref: Air.Inst.Ref) !void { } fn freeLocal(f: *Function, inst: ?Air.Inst.Index, local_index: LocalIndex, ref_inst: ?Air.Inst.Index) !void { - const gpa = f.object.dg.gpa; - const local = &f.locals.items[local_index]; + const gpa = f.dg.gpa; + const local = f.locals.items[local_index]; if (inst) |i| { if (ref_inst) |operand| { log.debug("%{d}: freeing t{d} (operand %{d})", .{ @intFromEnum(i), local_index, operand }); @@ -8344,7 +7519,7 @@ fn freeLocal(f: *Function, inst: ?Air.Inst.Index, local_index: LocalIndex, ref_i log.debug("freeing t{d}", .{local_index}); } } - const gop = try f.free_locals_map.getOrPut(gpa, local.getType()); + const gop = try f.free_locals_map.getOrPut(gpa, local); if (!gop.found_existing) gop.value_ptr.* = .{}; if (std.debug.runtime_safety) { // If this trips, an unfreeable allocation was attempted to be freed. @@ -8401,3 +7576,28 @@ fn deinitFreeLocalsMap(gpa: Allocator, map: *LocalsMap) void { } map.deinit(gpa); } + +fn renderErrorName(w: *Writer, err_name: []const u8) Writer.Error!void { + try w.print("zig_error_{f}", .{fmtIdentUnsolo(err_name)}); +} + +fn renderNavName(w: *Writer, nav_index: InternPool.Nav.Index, ip: *const InternPool) !void { + const nav = ip.getNav(nav_index); + if (nav.getExtern(ip)) |@"extern"| { + try w.print("{f}", .{ + fmtIdentSolo(ip.getNav(@"extern".owner_nav).name.toSlice(ip)), + }); + } else { + // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case), + // expand to 3x the length of its input, but let's cut it off at a much shorter limit. + const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip); + try w.print("{f}__{d}", .{ + fmtIdentUnsolo(fqn_slice[0..@min(fqn_slice.len, 100)]), + @intFromEnum(nav_index), + }); + } +} + +fn renderUavName(w: *Writer, uav: Value) !void { + try w.print("__anon_{d}", .{@intFromEnum(uav.toIntern())}); +} diff --git a/src/codegen/c/Type.zig b/src/codegen/c/Type.zig @@ -1,3472 +0,0 @@ -index: CType.Index, - -pub const @"void": CType = .{ .index = .void }; -pub const @"bool": CType = .{ .index = .bool }; -pub const @"i8": CType = .{ .index = .int8_t }; -pub const @"u8": CType = .{ .index = .uint8_t }; -pub const @"i16": CType = .{ .index = .int16_t }; -pub const @"u16": CType = .{ .index = .uint16_t }; -pub const @"i32": CType = .{ .index = .int32_t }; -pub const @"u32": CType = .{ .index = .uint32_t }; -pub const @"i64": CType = .{ .index = .int64_t }; -pub const @"u64": CType = .{ .index = .uint64_t }; -pub const @"i128": CType = .{ .index = .zig_i128 }; -pub const @"u128": CType = .{ .index = .zig_u128 }; -pub const @"isize": CType = .{ .index = .intptr_t }; -pub const @"usize": CType = .{ .index = .uintptr_t }; -pub const @"f16": CType = .{ .index = .zig_f16 }; -pub const @"f32": CType = .{ .index = .zig_f32 }; -pub const @"f64": CType = .{ .index = .zig_f64 }; -pub const @"f80": CType = .{ .index = .zig_f80 }; -pub const @"f128": CType = .{ .index = .zig_f128 }; - -pub fn fromPoolIndex(pool_index: usize) CType { - return .{ .index = @enumFromInt(CType.Index.first_pool_index + pool_index) }; -} - -pub fn toPoolIndex(ctype: CType) ?u32 { - const pool_index, const is_null = - @subWithOverflow(@intFromEnum(ctype.index), CType.Index.first_pool_index); - return switch (is_null) { - 0 => pool_index, - 1 => null, - }; -} - -pub fn eql(lhs: CType, rhs: CType) bool { - return lhs.index == rhs.index; -} - -pub fn isBool(ctype: CType) bool { - return switch (ctype.index) { - ._Bool, .bool => true, - else => false, - }; -} - -pub fn isInteger(ctype: CType) bool { - return switch (ctype.index) { - .char, - .@"signed char", - .short, - .int, - .long, - .@"long long", - .@"unsigned char", - .@"unsigned short", - .@"unsigned int", - .@"unsigned long", - .@"unsigned long long", - .size_t, - .ptrdiff_t, - .uint8_t, - .int8_t, - .uint16_t, - .int16_t, - .uint32_t, - .int32_t, - .uint64_t, - .int64_t, - .uintptr_t, - .intptr_t, - .zig_u128, - .zig_i128, - => true, - else => false, - }; -} - -pub fn signedness(ctype: CType, mod: *Module) std.builtin.Signedness { - return switch (ctype.index) { - .char => mod.resolved_target.result.cCharSignedness(), - .@"signed char", - .short, - .int, - .long, - .@"long long", - .ptrdiff_t, - .int8_t, - .int16_t, - .int32_t, - .int64_t, - .intptr_t, - .zig_i128, - => .signed, - .@"unsigned char", - .@"unsigned short", - .@"unsigned int", - .@"unsigned long", - .@"unsigned long long", - .size_t, - .uint8_t, - .uint16_t, - .uint32_t, - .uint64_t, - .uintptr_t, - .zig_u128, - => .unsigned, - else => unreachable, - }; -} - -pub fn isFloat(ctype: CType) bool { - return switch (ctype.index) { - .float, - .double, - .@"long double", - .zig_f16, - .zig_f32, - .zig_f64, - .zig_f80, - .zig_f128, - .zig_c_longdouble, - => true, - else => false, - }; -} - -pub fn toSigned(ctype: CType) CType { - return switch (ctype.index) { - .char, .@"signed char", .@"unsigned char" => .{ .index = .@"signed char" }, - .short, .@"unsigned short" => .{ .index = .short }, - .int, .@"unsigned int" => .{ .index = .int }, - .long, .@"unsigned long" => .{ .index = .long }, - .@"long long", .@"unsigned long long" => .{ .index = .@"long long" }, - .size_t, .ptrdiff_t => .{ .index = .ptrdiff_t }, - .uint8_t, .int8_t => .{ .index = .int8_t }, - .uint16_t, .int16_t => .{ .index = .int16_t }, - .uint32_t, .int32_t => .{ .index = .int32_t }, - .uint64_t, .int64_t => .{ .index = .int64_t }, - .uintptr_t, .intptr_t => .{ .index = .intptr_t }, - .zig_u128, .zig_i128 => .{ .index = .zig_i128 }, - .float, - .double, - .@"long double", - .zig_f16, - .zig_f32, - .zig_f80, - .zig_f128, - .zig_c_longdouble, - => ctype, - else => unreachable, - }; -} - -pub fn toUnsigned(ctype: CType) CType { - return switch (ctype.index) { - .char, .@"signed char", .@"unsigned char" => .{ .index = .@"unsigned char" }, - .short, .@"unsigned short" => .{ .index = .@"unsigned short" }, - .int, .@"unsigned int" => .{ .index = .@"unsigned int" }, - .long, .@"unsigned long" => .{ .index = .@"unsigned long" }, - .@"long long", .@"unsigned long long" => .{ .index = .@"unsigned long long" }, - .size_t, .ptrdiff_t => .{ .index = .size_t }, - .uint8_t, .int8_t => .{ .index = .uint8_t }, - .uint16_t, .int16_t => .{ .index = .uint16_t }, - .uint32_t, .int32_t => .{ .index = .uint32_t }, - .uint64_t, .int64_t => .{ .index = .uint64_t }, - .uintptr_t, .intptr_t => .{ .index = .uintptr_t }, - .zig_u128, .zig_i128 => .{ .index = .zig_u128 }, - else => unreachable, - }; -} - -pub fn toSignedness(ctype: CType, s: std.builtin.Signedness) CType { - return switch (s) { - .unsigned => ctype.toUnsigned(), - .signed => ctype.toSigned(), - }; -} - -pub fn isAnyChar(ctype: CType) bool { - return switch (ctype.index) { - else => false, - .char, .@"signed char", .@"unsigned char", .uint8_t, .int8_t => true, - }; -} - -pub fn isString(ctype: CType, pool: *const Pool) bool { - return info: switch (ctype.info(pool)) { - .basic, .fwd_decl, .aggregate, .function => false, - .pointer => |pointer_info| pointer_info.elem_ctype.isAnyChar(), - .aligned => |aligned_info| continue :info aligned_info.ctype.info(pool), - .array, .vector => |sequence_info| sequence_info.elem_type.isAnyChar(), - }; -} - -pub fn isNonString(ctype: CType, pool: *const Pool) bool { - var allow_pointer = true; - return info: switch (ctype.info(pool)) { - .basic, .fwd_decl, .aggregate, .function => false, - .pointer => |pointer_info| allow_pointer and pointer_info.nonstring, - .aligned => |aligned_info| continue :info aligned_info.ctype.info(pool), - .array, .vector => |sequence_info| sequence_info.nonstring or { - allow_pointer = false; - continue :info sequence_info.elem_ctype.info(pool); - }, - }; -} - -pub fn getStandardDefineAbbrev(ctype: CType) ?[]const u8 { - return switch (ctype.index) { - .char => "CHAR", - .@"signed char" => "SCHAR", - .short => "SHRT", - .int => "INT", - .long => "LONG", - .@"long long" => "LLONG", - .@"unsigned char" => "UCHAR", - .@"unsigned short" => "USHRT", - .@"unsigned int" => "UINT", - .@"unsigned long" => "ULONG", - .@"unsigned long long" => "ULLONG", - .float => "FLT", - .double => "DBL", - .@"long double" => "LDBL", - .size_t => "SIZE", - .ptrdiff_t => "PTRDIFF", - .uint8_t => "UINT8", - .int8_t => "INT8", - .uint16_t => "UINT16", - .int16_t => "INT16", - .uint32_t => "UINT32", - .int32_t => "INT32", - .uint64_t => "UINT64", - .int64_t => "INT64", - .uintptr_t => "UINTPTR", - .intptr_t => "INTPTR", - else => null, - }; -} - -pub fn renderLiteralPrefix(ctype: CType, w: *Writer, kind: Kind, pool: *const Pool) Writer.Error!void { - switch (ctype.info(pool)) { - .basic => |basic_info| switch (basic_info) { - .void => unreachable, - ._Bool, - .char, - .@"signed char", - .short, - .@"unsigned short", - .bool, - .size_t, - .ptrdiff_t, - .uintptr_t, - .intptr_t, - => switch (kind) { - else => try w.print("({s})", .{@tagName(basic_info)}), - .global => {}, - }, - .int, - .long, - .@"long long", - .@"unsigned char", - .@"unsigned int", - .@"unsigned long", - .@"unsigned long long", - .float, - .double, - .@"long double", - => {}, - .uint8_t, - .int8_t, - .uint16_t, - .int16_t, - .uint32_t, - .int32_t, - .uint64_t, - .int64_t, - => try w.print("{s}_C(", .{ctype.getStandardDefineAbbrev().?}), - .zig_u128, - .zig_i128, - .zig_f16, - .zig_f32, - .zig_f64, - .zig_f80, - .zig_f128, - .zig_c_longdouble, - => try w.print("zig_{s}_{s}(", .{ - switch (kind) { - else => "make", - .global => "init", - }, - @tagName(basic_info)["zig_".len..], - }), - .va_list => unreachable, - _ => unreachable, - }, - .array, .vector => try w.writeByte('{'), - else => unreachable, - } -} - -pub fn renderLiteralSuffix(ctype: CType, w: *Writer, pool: *const Pool) Writer.Error!void { - switch (ctype.info(pool)) { - .basic => |basic_info| switch (basic_info) { - .void => unreachable, - ._Bool => {}, - .char, - .@"signed char", - .short, - .int, - => {}, - .long => try w.writeByte('l'), - .@"long long" => try w.writeAll("ll"), - .@"unsigned char", - .@"unsigned short", - .@"unsigned int", - => try w.writeByte('u'), - .@"unsigned long", - .size_t, - .uintptr_t, - => try w.writeAll("ul"), - .@"unsigned long long" => try w.writeAll("ull"), - .float => try w.writeByte('f'), - .double => {}, - .@"long double" => try w.writeByte('l'), - .bool, - .ptrdiff_t, - .intptr_t, - => {}, - .uint8_t, - .int8_t, - .uint16_t, - .int16_t, - .uint32_t, - .int32_t, - .uint64_t, - .int64_t, - .zig_u128, - .zig_i128, - .zig_f16, - .zig_f32, - .zig_f64, - .zig_f80, - .zig_f128, - .zig_c_longdouble, - => try w.writeByte(')'), - .va_list => unreachable, - _ => unreachable, - }, - .array, .vector => try w.writeByte('}'), - else => unreachable, - } -} - -pub fn floatActiveBits(ctype: CType, mod: *Module) u16 { - const target = &mod.resolved_target.result; - return switch (ctype.index) { - .float => target.cTypeBitSize(.float), - .double => target.cTypeBitSize(.double), - .@"long double", .zig_c_longdouble => target.cTypeBitSize(.longdouble), - .zig_f16 => 16, - .zig_f32 => 32, - .zig_f64 => 64, - .zig_f80 => 80, - .zig_f128 => 128, - else => unreachable, - }; -} - -pub fn byteSize(ctype: CType, pool: *const Pool, mod: *Module) u64 { - const target = &mod.resolved_target.result; - return switch (ctype.info(pool)) { - .basic => |basic_info| switch (basic_info) { - .void => 0, - .char, .@"signed char", ._Bool, .@"unsigned char", .bool, .uint8_t, .int8_t => 1, - .short => target.cTypeByteSize(.short), - .int => target.cTypeByteSize(.int), - .long => target.cTypeByteSize(.long), - .@"long long" => target.cTypeByteSize(.longlong), - .@"unsigned short" => target.cTypeByteSize(.ushort), - .@"unsigned int" => target.cTypeByteSize(.uint), - .@"unsigned long" => target.cTypeByteSize(.ulong), - .@"unsigned long long" => target.cTypeByteSize(.ulonglong), - .float => target.cTypeByteSize(.float), - .double => target.cTypeByteSize(.double), - .@"long double" => target.cTypeByteSize(.longdouble), - .size_t, - .ptrdiff_t, - .uintptr_t, - .intptr_t, - => @divExact(target.ptrBitWidth(), 8), - .uint16_t, .int16_t, .zig_f16 => 2, - .uint32_t, .int32_t, .zig_f32 => 4, - .uint64_t, .int64_t, .zig_f64 => 8, - .zig_u128, .zig_i128, .zig_f128 => 16, - .zig_f80 => if (target.cTypeBitSize(.longdouble) == 80) - target.cTypeByteSize(.longdouble) - else - 16, - .zig_c_longdouble => target.cTypeByteSize(.longdouble), - .va_list => unreachable, - _ => unreachable, - }, - .pointer => @divExact(target.ptrBitWidth(), 8), - .array, .vector => |sequence_info| sequence_info.elem_ctype.byteSize(pool, mod) * sequence_info.len, - else => unreachable, - }; -} - -pub fn info(ctype: CType, pool: *const Pool) Info { - const pool_index = ctype.toPoolIndex() orelse return .{ .basic = ctype.index }; - const item = pool.items.get(pool_index); - switch (item.tag) { - .basic => unreachable, - .pointer => return .{ .pointer = .{ - .elem_ctype = .{ .index = @enumFromInt(item.data) }, - } }, - .pointer_const => return .{ .pointer = .{ - .elem_ctype = .{ .index = @enumFromInt(item.data) }, - .@"const" = true, - } }, - .pointer_volatile => return .{ .pointer = .{ - .elem_ctype = .{ .index = @enumFromInt(item.data) }, - .@"volatile" = true, - } }, - .pointer_const_volatile => return .{ .pointer = .{ - .elem_ctype = .{ .index = @enumFromInt(item.data) }, - .@"const" = true, - .@"volatile" = true, - } }, - .aligned => { - const extra = pool.getExtra(Pool.Aligned, item.data); - return .{ .aligned = .{ - .ctype = .{ .index = extra.ctype }, - .alignas = extra.flags.alignas, - } }; - }, - .array_small => { - const extra = pool.getExtra(Pool.SequenceSmall, item.data); - return .{ .array = .{ - .elem_ctype = .{ .index = extra.elem_ctype }, - .len = extra.len, - } }; - }, - .array_large => { - const extra = pool.getExtra(Pool.SequenceLarge, item.data); - return .{ .array = .{ - .elem_ctype = .{ .index = extra.elem_ctype }, - .len = extra.len(), - } }; - }, - .vector => { - const extra = pool.getExtra(Pool.SequenceSmall, item.data); - return .{ .vector = .{ - .elem_ctype = .{ .index = extra.elem_ctype }, - .len = extra.len, - } }; - }, - .nonstring => { - var child_info = info(.{ .index = @enumFromInt(item.data) }, pool); - switch (child_info) { - else => unreachable, - .pointer => |*pointer_info| pointer_info.nonstring = true, - .array, .vector => |*sequence_info| sequence_info.nonstring = true, - } - return child_info; - }, - .fwd_decl_struct_anon => { - const extra_trail = pool.getExtraTrail(Pool.FwdDeclAnon, item.data); - return .{ .fwd_decl = .{ - .tag = .@"struct", - .name = .{ .anon = .{ - .extra_index = extra_trail.trail.extra_index, - .len = extra_trail.extra.fields_len, - } }, - } }; - }, - .fwd_decl_union_anon => { - const extra_trail = pool.getExtraTrail(Pool.FwdDeclAnon, item.data); - return .{ .fwd_decl = .{ - .tag = .@"union", - .name = .{ .anon = .{ - .extra_index = extra_trail.trail.extra_index, - .len = extra_trail.extra.fields_len, - } }, - } }; - }, - .fwd_decl_struct => return .{ .fwd_decl = .{ - .tag = .@"struct", - .name = .{ .index = @enumFromInt(item.data) }, - } }, - .fwd_decl_union => return .{ .fwd_decl = .{ - .tag = .@"union", - .name = .{ .index = @enumFromInt(item.data) }, - } }, - .aggregate_struct_anon => { - const extra_trail = pool.getExtraTrail(Pool.AggregateAnon, item.data); - return .{ .aggregate = .{ - .tag = .@"struct", - .name = .{ .anon = .{ - .index = extra_trail.extra.index, - .id = extra_trail.extra.id, - } }, - .fields = .{ - .extra_index = extra_trail.trail.extra_index, - .len = extra_trail.extra.fields_len, - }, - } }; - }, - .aggregate_union_anon => { - const extra_trail = pool.getExtraTrail(Pool.AggregateAnon, item.data); - return .{ .aggregate = .{ - .tag = .@"union", - .name = .{ .anon = .{ - .index = extra_trail.extra.index, - .id = extra_trail.extra.id, - } }, - .fields = .{ - .extra_index = extra_trail.trail.extra_index, - .len = extra_trail.extra.fields_len, - }, - } }; - }, - .aggregate_struct_packed_anon => { - const extra_trail = pool.getExtraTrail(Pool.AggregateAnon, item.data); - return .{ .aggregate = .{ - .tag = .@"struct", - .@"packed" = true, - .name = .{ .anon = .{ - .index = extra_trail.extra.index, - .id = extra_trail.extra.id, - } }, - .fields = .{ - .extra_index = extra_trail.trail.extra_index, - .len = extra_trail.extra.fields_len, - }, - } }; - }, - .aggregate_union_packed_anon => { - const extra_trail = pool.getExtraTrail(Pool.AggregateAnon, item.data); - return .{ .aggregate = .{ - .tag = .@"union", - .@"packed" = true, - .name = .{ .anon = .{ - .index = extra_trail.extra.index, - .id = extra_trail.extra.id, - } }, - .fields = .{ - .extra_index = extra_trail.trail.extra_index, - .len = extra_trail.extra.fields_len, - }, - } }; - }, - .aggregate_struct => { - const extra_trail = pool.getExtraTrail(Pool.Aggregate, item.data); - return .{ .aggregate = .{ - .tag = .@"struct", - .name = .{ .fwd_decl = .{ .index = extra_trail.extra.fwd_decl } }, - .fields = .{ - .extra_index = extra_trail.trail.extra_index, - .len = extra_trail.extra.fields_len, - }, - } }; - }, - .aggregate_union => { - const extra_trail = pool.getExtraTrail(Pool.Aggregate, item.data); - return .{ .aggregate = .{ - .tag = .@"union", - .name = .{ .fwd_decl = .{ .index = extra_trail.extra.fwd_decl } }, - .fields = .{ - .extra_index = extra_trail.trail.extra_index, - .len = extra_trail.extra.fields_len, - }, - } }; - }, - .aggregate_struct_packed => { - const extra_trail = pool.getExtraTrail(Pool.Aggregate, item.data); - return .{ .aggregate = .{ - .tag = .@"struct", - .@"packed" = true, - .name = .{ .fwd_decl = .{ .index = extra_trail.extra.fwd_decl } }, - .fields = .{ - .extra_index = extra_trail.trail.extra_index, - .len = extra_trail.extra.fields_len, - }, - } }; - }, - .aggregate_union_packed => { - const extra_trail = pool.getExtraTrail(Pool.Aggregate, item.data); - return .{ .aggregate = .{ - .tag = .@"union", - .@"packed" = true, - .name = .{ .fwd_decl = .{ .index = extra_trail.extra.fwd_decl } }, - .fields = .{ - .extra_index = extra_trail.trail.extra_index, - .len = extra_trail.extra.fields_len, - }, - } }; - }, - .function => { - const extra_trail = pool.getExtraTrail(Pool.Function, item.data); - return .{ .function = .{ - .return_ctype = .{ .index = extra_trail.extra.return_ctype }, - .param_ctypes = .{ - .extra_index = extra_trail.trail.extra_index, - .len = extra_trail.extra.param_ctypes_len, - }, - .varargs = false, - } }; - }, - .function_varargs => { - const extra_trail = pool.getExtraTrail(Pool.Function, item.data); - return .{ .function = .{ - .return_ctype = .{ .index = extra_trail.extra.return_ctype }, - .param_ctypes = .{ - .extra_index = extra_trail.trail.extra_index, - .len = extra_trail.extra.param_ctypes_len, - }, - .varargs = true, - } }; - }, - } -} - -pub fn hash(ctype: CType, pool: *const Pool) Pool.Map.Hash { - return if (ctype.toPoolIndex()) |pool_index| - pool.map.entries.items(.hash)[pool_index] - else - CType.Index.basic_hashes[@intFromEnum(ctype.index)]; -} - -fn toForward(ctype: CType, pool: *Pool, allocator: std.mem.Allocator) !CType { - return switch (ctype.info(pool)) { - .basic, .pointer, .fwd_decl => ctype, - .aligned => |aligned_info| pool.getAligned(allocator, .{ - .ctype = try aligned_info.ctype.toForward(pool, allocator), - .alignas = aligned_info.alignas, - }), - .array => |array_info| pool.getArray(allocator, .{ - .elem_ctype = try array_info.elem_ctype.toForward(pool, allocator), - .len = array_info.len, - .nonstring = array_info.nonstring, - }), - .vector => |vector_info| pool.getVector(allocator, .{ - .elem_ctype = try vector_info.elem_ctype.toForward(pool, allocator), - .len = vector_info.len, - .nonstring = vector_info.nonstring, - }), - .aggregate => |aggregate_info| switch (aggregate_info.name) { - .anon => ctype, - .fwd_decl => |fwd_decl| fwd_decl, - }, - .function => unreachable, - }; -} - -const Index = enum(u32) { - void, - - // C basic types - char, - - @"signed char", - short, - int, - long, - @"long long", - - _Bool, - @"unsigned char", - @"unsigned short", - @"unsigned int", - @"unsigned long", - @"unsigned long long", - - float, - double, - @"long double", - - // C header types - // - stdbool.h - bool, - // - stddef.h - size_t, - ptrdiff_t, - // - stdint.h - uint8_t, - int8_t, - uint16_t, - int16_t, - uint32_t, - int32_t, - uint64_t, - int64_t, - uintptr_t, - intptr_t, - // - stdarg.h - va_list, - - // zig.h types - zig_u128, - zig_i128, - zig_f16, - zig_f32, - zig_f64, - zig_f80, - zig_f128, - zig_c_longdouble, - - _, - - const first_pool_index: u32 = @typeInfo(CType.Index).@"enum".fields.len; - const basic_hashes = init: { - @setEvalBranchQuota(1_600); - var basic_hashes_init: [first_pool_index]Pool.Map.Hash = undefined; - for (&basic_hashes_init, 0..) |*basic_hash, index| { - const ctype_index: CType.Index = @enumFromInt(index); - var hasher = Pool.Hasher.init; - hasher.update(@intFromEnum(ctype_index)); - basic_hash.* = hasher.final(.basic); - } - break :init basic_hashes_init; - }; -}; - -const Slice = struct { - extra_index: Pool.ExtraIndex, - len: u32, - - pub fn at(slice: CType.Slice, index: usize, pool: *const Pool) CType { - var extra: Pool.ExtraTrail = .{ .extra_index = slice.extra_index }; - return .{ .index = extra.next(slice.len, CType.Index, pool)[index] }; - } -}; - -pub const Kind = enum { - forward, - forward_parameter, - complete, - global, - parameter, - - pub fn isForward(kind: Kind) bool { - return switch (kind) { - .forward, .forward_parameter => true, - .complete, .global, .parameter => false, - }; - } - - pub fn isParameter(kind: Kind) bool { - return switch (kind) { - .forward_parameter, .parameter => true, - .forward, .complete, .global => false, - }; - } - - pub fn asParameter(kind: Kind) Kind { - return switch (kind) { - .forward, .forward_parameter => .forward_parameter, - .complete, .parameter, .global => .parameter, - }; - } - - pub fn noParameter(kind: Kind) Kind { - return switch (kind) { - .forward, .forward_parameter => .forward, - .complete, .parameter => .complete, - .global => .global, - }; - } - - pub fn asComplete(kind: Kind) Kind { - return switch (kind) { - .forward, .complete => .complete, - .forward_parameter, .parameter => .parameter, - .global => .global, - }; - } -}; - -pub const Info = union(enum) { - basic: CType.Index, - pointer: Pointer, - aligned: Aligned, - array: Sequence, - vector: Sequence, - fwd_decl: FwdDecl, - aggregate: Aggregate, - function: Function, - - const Tag = @typeInfo(Info).@"union".tag_type.?; - - pub const Pointer = struct { - elem_ctype: CType, - @"const": bool = false, - @"volatile": bool = false, - nonstring: bool = false, - - fn tag(pointer_info: Pointer) Pool.Tag { - return @enumFromInt(@intFromEnum(Pool.Tag.pointer) + - @as(u2, @bitCast(packed struct(u2) { - @"const": bool, - @"volatile": bool, - }{ - .@"const" = pointer_info.@"const", - .@"volatile" = pointer_info.@"volatile", - }))); - } - }; - - pub const Aligned = struct { - ctype: CType, - alignas: AlignAs, - }; - - pub const Sequence = struct { - elem_ctype: CType, - len: u64, - nonstring: bool = false, - }; - - pub const AggregateTag = enum { @"enum", @"struct", @"union" }; - - pub const Field = struct { - name: Pool.String, - ctype: CType, - alignas: AlignAs, - - pub const Slice = struct { - extra_index: Pool.ExtraIndex, - len: u32, - - pub fn at(slice: Field.Slice, index: usize, pool: *const Pool) Field { - assert(index < slice.len); - const extra = pool.getExtra(Pool.Field, @intCast(slice.extra_index + - index * @typeInfo(Pool.Field).@"struct".fields.len)); - return .{ - .name = .{ .index = extra.name }, - .ctype = .{ .index = extra.ctype }, - .alignas = extra.flags.alignas, - }; - } - - fn eqlAdapted( - lhs_slice: Field.Slice, - lhs_pool: *const Pool, - rhs_slice: Field.Slice, - rhs_pool: *const Pool, - pool_adapter: anytype, - ) bool { - if (lhs_slice.len != rhs_slice.len) return false; - for (0..lhs_slice.len) |index| { - if (!lhs_slice.at(index, lhs_pool).eqlAdapted( - lhs_pool, - rhs_slice.at(index, rhs_pool), - rhs_pool, - pool_adapter, - )) return false; - } - return true; - } - }; - - fn eqlAdapted( - lhs_field: Field, - lhs_pool: *const Pool, - rhs_field: Field, - rhs_pool: *const Pool, - pool_adapter: anytype, - ) bool { - if (!std.meta.eql(lhs_field.alignas, rhs_field.alignas)) return false; - if (!pool_adapter.eql(lhs_field.ctype, rhs_field.ctype)) return false; - return if (lhs_field.name.toPoolSlice(lhs_pool)) |lhs_name| - if (rhs_field.name.toPoolSlice(rhs_pool)) |rhs_name| - std.mem.eql(u8, lhs_name, rhs_name) - else - false - else - lhs_field.name.index == rhs_field.name.index; - } - }; - - pub const FwdDecl = struct { - tag: AggregateTag, - name: union(enum) { - anon: Field.Slice, - index: InternPool.Index, - }, - }; - - pub const Aggregate = struct { - tag: AggregateTag, - @"packed": bool = false, - name: union(enum) { - anon: struct { - index: InternPool.Index, - id: u32, - }, - fwd_decl: CType, - }, - fields: Field.Slice, - }; - - pub const Function = struct { - return_ctype: CType, - param_ctypes: CType.Slice, - varargs: bool = false, - }; - - pub fn eqlAdapted( - lhs_info: Info, - lhs_pool: *const Pool, - rhs_ctype: CType, - rhs_pool: *const Pool, - pool_adapter: anytype, - ) bool { - const rhs_info = rhs_ctype.info(rhs_pool); - if (@as(Info.Tag, lhs_info) != @as(Info.Tag, rhs_info)) return false; - return switch (lhs_info) { - .basic => |lhs_basic_info| lhs_basic_info == rhs_info.basic, - .pointer => |lhs_pointer_info| lhs_pointer_info.@"const" == rhs_info.pointer.@"const" and - lhs_pointer_info.@"volatile" == rhs_info.pointer.@"volatile" and - lhs_pointer_info.nonstring == rhs_info.pointer.nonstring and - pool_adapter.eql(lhs_pointer_info.elem_ctype, rhs_info.pointer.elem_ctype), - .aligned => |lhs_aligned_info| std.meta.eql(lhs_aligned_info.alignas, rhs_info.aligned.alignas) and - pool_adapter.eql(lhs_aligned_info.ctype, rhs_info.aligned.ctype), - .array => |lhs_array_info| lhs_array_info.len == rhs_info.array.len and - lhs_array_info.nonstring == rhs_info.array.nonstring and - pool_adapter.eql(lhs_array_info.elem_ctype, rhs_info.array.elem_ctype), - .vector => |lhs_vector_info| lhs_vector_info.len == rhs_info.vector.len and - lhs_vector_info.nonstring == rhs_info.vector.nonstring and - pool_adapter.eql(lhs_vector_info.elem_ctype, rhs_info.vector.elem_ctype), - .fwd_decl => |lhs_fwd_decl_info| lhs_fwd_decl_info.tag == rhs_info.fwd_decl.tag and - switch (lhs_fwd_decl_info.name) { - .anon => |lhs_anon| rhs_info.fwd_decl.name == .anon and lhs_anon.eqlAdapted( - lhs_pool, - rhs_info.fwd_decl.name.anon, - rhs_pool, - pool_adapter, - ), - .index => |lhs_index| rhs_info.fwd_decl.name == .index and - lhs_index == rhs_info.fwd_decl.name.index, - }, - .aggregate => |lhs_aggregate_info| lhs_aggregate_info.tag == rhs_info.aggregate.tag and - lhs_aggregate_info.@"packed" == rhs_info.aggregate.@"packed" and - switch (lhs_aggregate_info.name) { - .anon => |lhs_anon| rhs_info.aggregate.name == .anon and - lhs_anon.index == rhs_info.aggregate.name.anon.index and - lhs_anon.id == rhs_info.aggregate.name.anon.id, - .fwd_decl => |lhs_fwd_decl| rhs_info.aggregate.name == .fwd_decl and - pool_adapter.eql(lhs_fwd_decl, rhs_info.aggregate.name.fwd_decl), - } and lhs_aggregate_info.fields.eqlAdapted( - lhs_pool, - rhs_info.aggregate.fields, - rhs_pool, - pool_adapter, - ), - .function => |lhs_function_info| lhs_function_info.param_ctypes.len == - rhs_info.function.param_ctypes.len and - pool_adapter.eql(lhs_function_info.return_ctype, rhs_info.function.return_ctype) and - for (0..lhs_function_info.param_ctypes.len) |param_index| { - if (!pool_adapter.eql( - lhs_function_info.param_ctypes.at(param_index, lhs_pool), - rhs_info.function.param_ctypes.at(param_index, rhs_pool), - )) break false; - } else true, - }; - } -}; - -pub const Pool = struct { - map: Map, - items: std.MultiArrayList(Item), - extra: std.ArrayList(u32), - - string_map: Map, - string_indices: std.ArrayList(u32), - string_bytes: std.ArrayList(u8), - - const Map = std.AutoArrayHashMapUnmanaged(void, void); - - pub const String = struct { - index: String.Index, - - const FormatData = struct { string: String, pool: *const Pool }; - fn format(data: FormatData, writer: *Writer) Writer.Error!void { - if (data.string.toSlice(data.pool)) |slice| - try writer.writeAll(slice) - else - try writer.print("f{d}", .{@intFromEnum(data.string.index)}); - } - pub fn fmt(str: String, pool: *const Pool) std.fmt.Alt(FormatData, format) { - return .{ .data = .{ .string = str, .pool = pool } }; - } - - fn fromUnnamed(index: u31) String { - return .{ .index = @enumFromInt(index) }; - } - - fn isNamed(str: String) bool { - return @intFromEnum(str.index) >= String.Index.first_named_index; - } - - pub fn toSlice(str: String, pool: *const Pool) ?[]const u8 { - return str.toPoolSlice(pool) orelse if (str.isNamed()) @tagName(str.index) else null; - } - - fn toPoolSlice(str: String, pool: *const Pool) ?[]const u8 { - if (str.toPoolIndex()) |pool_index| { - const start = pool.string_indices.items[pool_index + 0]; - const end = pool.string_indices.items[pool_index + 1]; - return pool.string_bytes.items[start..end]; - } else return null; - } - - fn fromPoolIndex(pool_index: usize) String { - return .{ .index = @enumFromInt(String.Index.first_pool_index + pool_index) }; - } - - fn toPoolIndex(str: String) ?u32 { - const pool_index, const is_null = - @subWithOverflow(@intFromEnum(str.index), String.Index.first_pool_index); - return switch (is_null) { - 0 => pool_index, - 1 => null, - }; - } - - const Index = enum(u32) { - array = first_named_index, - @"error", - is_null, - len, - payload, - ptr, - tag, - _, - - const first_named_index: u32 = 1 << 31; - const first_pool_index: u32 = first_named_index + @typeInfo(String.Index).@"enum".fields.len; - }; - - const Adapter = struct { - pool: *const Pool, - pub fn hash(_: @This(), slice: []const u8) Map.Hash { - return @truncate(Hasher.Impl.hash(1, slice)); - } - pub fn eql(string_adapter: @This(), lhs_slice: []const u8, _: void, rhs_index: usize) bool { - const rhs_string = String.fromPoolIndex(rhs_index); - const rhs_slice = rhs_string.toPoolSlice(string_adapter.pool).?; - return std.mem.eql(u8, lhs_slice, rhs_slice); - } - }; - }; - - pub const empty: Pool = .{ - .map = .{}, - .items = .{}, - .extra = .{}, - - .string_map = .{}, - .string_indices = .{}, - .string_bytes = .{}, - }; - - pub fn init(pool: *Pool, allocator: std.mem.Allocator) !void { - if (pool.string_indices.items.len == 0) - try pool.string_indices.append(allocator, 0); - } - - pub fn deinit(pool: *Pool, allocator: std.mem.Allocator) void { - pool.map.deinit(allocator); - pool.items.deinit(allocator); - pool.extra.deinit(allocator); - - pool.string_map.deinit(allocator); - pool.string_indices.deinit(allocator); - pool.string_bytes.deinit(allocator); - - pool.* = undefined; - } - - pub fn move(pool: *Pool) Pool { - defer pool.* = empty; - return pool.*; - } - - pub fn clearRetainingCapacity(pool: *Pool) void { - pool.map.clearRetainingCapacity(); - pool.items.shrinkRetainingCapacity(0); - pool.extra.clearRetainingCapacity(); - - pool.string_map.clearRetainingCapacity(); - pool.string_indices.shrinkRetainingCapacity(1); - pool.string_bytes.clearRetainingCapacity(); - } - - pub fn freeUnusedCapacity(pool: *Pool, allocator: std.mem.Allocator) void { - pool.map.shrinkAndFree(allocator, pool.map.count()); - pool.items.shrinkAndFree(allocator, pool.items.len); - pool.extra.shrinkAndFree(allocator, pool.extra.items.len); - - pool.string_map.shrinkAndFree(allocator, pool.string_map.count()); - pool.string_indices.shrinkAndFree(allocator, pool.string_indices.items.len); - pool.string_bytes.shrinkAndFree(allocator, pool.string_bytes.items.len); - } - - pub fn getPointer(pool: *Pool, allocator: std.mem.Allocator, pointer_info: Info.Pointer) !CType { - var hasher = Hasher.init; - hasher.update(pointer_info.elem_ctype.hash(pool)); - return pool.getNonString(allocator, try pool.tagData( - allocator, - hasher, - pointer_info.tag(), - @intFromEnum(pointer_info.elem_ctype.index), - ), pointer_info.nonstring); - } - - pub fn getAligned(pool: *Pool, allocator: std.mem.Allocator, aligned_info: Info.Aligned) !CType { - return pool.tagExtra(allocator, .aligned, Aligned, .{ - .ctype = aligned_info.ctype.index, - .flags = .{ .alignas = aligned_info.alignas }, - }); - } - - pub fn getArray(pool: *Pool, allocator: std.mem.Allocator, array_info: Info.Sequence) !CType { - return pool.getNonString(allocator, if (std.math.cast(u32, array_info.len)) |small_len| - try pool.tagExtra(allocator, .array_small, SequenceSmall, .{ - .elem_ctype = array_info.elem_ctype.index, - .len = small_len, - }) - else - try pool.tagExtra(allocator, .array_large, SequenceLarge, .{ - .elem_ctype = array_info.elem_ctype.index, - .len_lo = @truncate(array_info.len >> 0), - .len_hi = @truncate(array_info.len >> 32), - }), array_info.nonstring); - } - - pub fn getVector(pool: *Pool, allocator: std.mem.Allocator, vector_info: Info.Sequence) !CType { - return pool.getNonString(allocator, try pool.tagExtra(allocator, .vector, SequenceSmall, .{ - .elem_ctype = vector_info.elem_ctype.index, - .len = @intCast(vector_info.len), - }), vector_info.nonstring); - } - - pub fn getNonString( - pool: *Pool, - allocator: std.mem.Allocator, - child_ctype: CType, - nonstring: bool, - ) !CType { - if (!nonstring) return child_ctype; - var hasher = Hasher.init; - hasher.update(child_ctype.hash(pool)); - return pool.tagData(allocator, hasher, .nonstring, @intFromEnum(child_ctype.index)); - } - - pub fn getFwdDecl( - pool: *Pool, - allocator: std.mem.Allocator, - fwd_decl_info: struct { - tag: Info.AggregateTag, - name: union(enum) { - anon: []const Info.Field, - index: InternPool.Index, - }, - }, - ) !CType { - var hasher = Hasher.init; - switch (fwd_decl_info.name) { - .anon => |fields| { - const ExpectedContents = [32]CType; - var stack align(@max( - @alignOf(std.heap.StackFallbackAllocator(0)), - @alignOf(ExpectedContents), - )) = std.heap.stackFallback(@sizeOf(ExpectedContents), allocator); - const stack_allocator = stack.get(); - const field_ctypes = try stack_allocator.alloc(CType, fields.len); - defer stack_allocator.free(field_ctypes); - for (field_ctypes, fields) |*field_ctype, field| - field_ctype.* = try field.ctype.toForward(pool, allocator); - const extra: FwdDeclAnon = .{ .fields_len = @intCast(fields.len) }; - const extra_index = try pool.addExtra( - allocator, - FwdDeclAnon, - extra, - fields.len * @typeInfo(Field).@"struct".fields.len, - ); - for (fields, field_ctypes) |field, field_ctype| pool.addHashedExtraAssumeCapacity( - &hasher, - Field, - .{ - .name = field.name.index, - .ctype = field_ctype.index, - .flags = .{ .alignas = field.alignas }, - }, - ); - hasher.updateExtra(FwdDeclAnon, extra, pool); - return pool.tagTrailingExtra(allocator, hasher, switch (fwd_decl_info.tag) { - .@"struct" => .fwd_decl_struct_anon, - .@"union" => .fwd_decl_union_anon, - .@"enum" => unreachable, - }, extra_index); - }, - .index => |index| { - hasher.update(index); - return pool.tagData(allocator, hasher, switch (fwd_decl_info.tag) { - .@"struct" => .fwd_decl_struct, - .@"union" => .fwd_decl_union, - .@"enum" => unreachable, - }, @intFromEnum(index)); - }, - } - } - - pub fn getAggregate( - pool: *Pool, - allocator: std.mem.Allocator, - aggregate_info: struct { - tag: Info.AggregateTag, - @"packed": bool = false, - name: union(enum) { - anon: struct { - index: InternPool.Index, - id: u32, - }, - fwd_decl: CType, - }, - fields: []const Info.Field, - }, - ) !CType { - var hasher = Hasher.init; - switch (aggregate_info.name) { - .anon => |anon| { - const extra: AggregateAnon = .{ - .index = anon.index, - .id = anon.id, - .fields_len = @intCast(aggregate_info.fields.len), - }; - const extra_index = try pool.addExtra( - allocator, - AggregateAnon, - extra, - aggregate_info.fields.len * @typeInfo(Field).@"struct".fields.len, - ); - for (aggregate_info.fields) |field| pool.addHashedExtraAssumeCapacity(&hasher, Field, .{ - .name = field.name.index, - .ctype = field.ctype.index, - .flags = .{ .alignas = field.alignas }, - }); - hasher.updateExtra(AggregateAnon, extra, pool); - return pool.tagTrailingExtra(allocator, hasher, switch (aggregate_info.tag) { - .@"struct" => switch (aggregate_info.@"packed") { - false => .aggregate_struct_anon, - true => .aggregate_struct_packed_anon, - }, - .@"union" => switch (aggregate_info.@"packed") { - false => .aggregate_union_anon, - true => .aggregate_union_packed_anon, - }, - .@"enum" => unreachable, - }, extra_index); - }, - .fwd_decl => |fwd_decl| { - const extra: Aggregate = .{ - .fwd_decl = fwd_decl.index, - .fields_len = @intCast(aggregate_info.fields.len), - }; - const extra_index = try pool.addExtra( - allocator, - Aggregate, - extra, - aggregate_info.fields.len * @typeInfo(Field).@"struct".fields.len, - ); - for (aggregate_info.fields) |field| pool.addHashedExtraAssumeCapacity(&hasher, Field, .{ - .name = field.name.index, - .ctype = field.ctype.index, - .flags = .{ .alignas = field.alignas }, - }); - hasher.updateExtra(Aggregate, extra, pool); - return pool.tagTrailingExtra(allocator, hasher, switch (aggregate_info.tag) { - .@"struct" => switch (aggregate_info.@"packed") { - false => .aggregate_struct, - true => .aggregate_struct_packed, - }, - .@"union" => switch (aggregate_info.@"packed") { - false => .aggregate_union, - true => .aggregate_union_packed, - }, - .@"enum" => unreachable, - }, extra_index); - }, - } - } - - pub fn getFunction( - pool: *Pool, - allocator: std.mem.Allocator, - function_info: struct { - return_ctype: CType, - param_ctypes: []const CType, - varargs: bool = false, - }, - ) !CType { - var hasher = Hasher.init; - const extra: Function = .{ - .return_ctype = function_info.return_ctype.index, - .param_ctypes_len = @intCast(function_info.param_ctypes.len), - }; - const extra_index = try pool.addExtra(allocator, Function, extra, function_info.param_ctypes.len); - for (function_info.param_ctypes) |param_ctype| { - hasher.update(param_ctype.hash(pool)); - pool.extra.appendAssumeCapacity(@intFromEnum(param_ctype.index)); - } - hasher.updateExtra(Function, extra, pool); - return pool.tagTrailingExtra(allocator, hasher, switch (function_info.varargs) { - false => .function, - true => .function_varargs, - }, extra_index); - } - - pub fn fromFields( - pool: *Pool, - allocator: std.mem.Allocator, - tag: Info.AggregateTag, - fields: []Info.Field, - kind: Kind, - ) !CType { - sortFields(fields); - const fwd_decl = try pool.getFwdDecl(allocator, .{ - .tag = tag, - .name = .{ .anon = fields }, - }); - return if (kind.isForward()) fwd_decl else pool.getAggregate(allocator, .{ - .tag = tag, - .name = .{ .fwd_decl = fwd_decl }, - .fields = fields, - }); - } - - pub fn fromIntInfo( - pool: *Pool, - allocator: std.mem.Allocator, - int_info: std.builtin.Type.Int, - mod: *Module, - kind: Kind, - ) !CType { - switch (int_info.bits) { - 0 => return .void, - 1...8 => switch (int_info.signedness) { - .signed => return .i8, - .unsigned => return .u8, - }, - 9...16 => switch (int_info.signedness) { - .signed => return .i16, - .unsigned => return .u16, - }, - 17...32 => switch (int_info.signedness) { - .signed => return .i32, - .unsigned => return .u32, - }, - 33...64 => switch (int_info.signedness) { - .signed => return .i64, - .unsigned => return .u64, - }, - 65...128 => switch (int_info.signedness) { - .signed => return .i128, - .unsigned => return .u128, - }, - else => { - const target = &mod.resolved_target.result; - const abi_align_bytes = std.zig.target.intAlignment(target, int_info.bits); - const limb_ctype = try pool.fromIntInfo(allocator, .{ - .signedness = .unsigned, - .bits = @intCast(abi_align_bytes * 8), - }, mod, kind.noParameter()); - const array_ctype = try pool.getArray(allocator, .{ - .len = @divExact(std.zig.target.intByteSize(target, int_info.bits), abi_align_bytes), - .elem_ctype = limb_ctype, - .nonstring = limb_ctype.isAnyChar(), - }); - if (!kind.isParameter()) return array_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = array_ctype, - .alignas = AlignAs.fromAbiAlignment(.fromByteUnits(abi_align_bytes)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - } - } - - pub fn fromType( - pool: *Pool, - allocator: std.mem.Allocator, - scratch: *std.ArrayList(u32), - ty: Type, - pt: Zcu.PerThread, - mod: *Module, - kind: Kind, - ) !CType { - const ip = &pt.zcu.intern_pool; - const zcu = pt.zcu; - switch (ty.toIntern()) { - .u0_type, - .i0_type, - .anyopaque_type, - .void_type, - .empty_tuple_type, - .type_type, - .comptime_int_type, - .comptime_float_type, - .null_type, - .undefined_type, - .enum_literal_type, - .optional_type_type, - .manyptr_const_type_type, - .slice_const_type_type, - => return .void, - .u1_type, .u8_type => return .u8, - .i8_type => return .i8, - .u16_type => return .u16, - .i16_type => return .i16, - .u29_type, .u32_type => return .u32, - .i32_type => return .i32, - .u64_type => return .u64, - .i64_type => return .i64, - .u80_type, .u128_type => return .u128, - .i128_type => return .i128, - .u256_type => return pool.fromIntInfo(allocator, .{ - .signedness = .unsigned, - .bits = 256, - }, mod, kind), - .usize_type => return .usize, - .isize_type => return .isize, - .c_char_type => return .{ .index = .char }, - .c_short_type => return .{ .index = .short }, - .c_ushort_type => return .{ .index = .@"unsigned short" }, - .c_int_type => return .{ .index = .int }, - .c_uint_type => return .{ .index = .@"unsigned int" }, - .c_long_type => return .{ .index = .long }, - .c_ulong_type => return .{ .index = .@"unsigned long" }, - .c_longlong_type => return .{ .index = .@"long long" }, - .c_ulonglong_type => return .{ .index = .@"unsigned long long" }, - .c_longdouble_type => return .{ .index = .@"long double" }, - .f16_type => return .f16, - .f32_type => return .f32, - .f64_type => return .f64, - .f80_type => return .f80, - .f128_type => return .f128, - .bool_type, .optional_noreturn_type => return .bool, - .noreturn_type, - .anyframe_type, - .generic_poison_type, - => unreachable, - .anyerror_type, - .anyerror_void_error_union_type, - .adhoc_inferred_error_set_type, - => return pool.fromIntInfo(allocator, .{ - .signedness = .unsigned, - .bits = pt.zcu.errorSetBits(), - }, mod, kind), - - .ptr_usize_type => return pool.getPointer(allocator, .{ - .elem_ctype = .usize, - }), - .ptr_const_comptime_int_type => return pool.getPointer(allocator, .{ - .elem_ctype = .void, - .@"const" = true, - }), - .manyptr_u8_type => return pool.getPointer(allocator, .{ - .elem_ctype = .u8, - .nonstring = true, - }), - .manyptr_const_u8_type => return pool.getPointer(allocator, .{ - .elem_ctype = .u8, - .@"const" = true, - .nonstring = true, - }), - .manyptr_const_u8_sentinel_0_type => return pool.getPointer(allocator, .{ - .elem_ctype = .u8, - .@"const" = true, - }), - .slice_const_u8_type => { - const target = &mod.resolved_target.result; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .ptr }, - .ctype = try pool.getPointer(allocator, .{ - .elem_ctype = .u8, - .@"const" = true, - .nonstring = true, - }), - .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)), - }, - .{ - .name = .{ .index = .len }, - .ctype = .usize, - .alignas = AlignAs.fromAbiAlignment( - .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())), - ), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .slice_const_u8_sentinel_0_type => { - const target = &mod.resolved_target.result; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .ptr }, - .ctype = try pool.getPointer(allocator, .{ - .elem_ctype = .u8, - .@"const" = true, - }), - .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)), - }, - .{ - .name = .{ .index = .len }, - .ctype = .usize, - .alignas = AlignAs.fromAbiAlignment( - .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())), - ), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - - .manyptr_const_slice_const_u8_type => { - const target = &mod.resolved_target.result; - var fields: [2]Info.Field = .{ - .{ - .name = .{ .index = .ptr }, - .ctype = try pool.getPointer(allocator, .{ - .elem_ctype = .u8, - .@"const" = true, - .nonstring = true, - }), - .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)), - }, - .{ - .name = .{ .index = .len }, - .ctype = .usize, - .alignas = AlignAs.fromAbiAlignment( - .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())), - ), - }, - }; - const slice_const_u8 = try pool.fromFields(allocator, .@"struct", &fields, kind); - return pool.getPointer(allocator, .{ - .elem_ctype = slice_const_u8, - .@"const" = true, - }); - }, - .slice_const_slice_const_u8_type => { - const target = &mod.resolved_target.result; - var fields: [2]Info.Field = .{ - .{ - .name = .{ .index = .ptr }, - .ctype = try pool.getPointer(allocator, .{ - .elem_ctype = .u8, - .@"const" = true, - .nonstring = true, - }), - .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)), - }, - .{ - .name = .{ .index = .len }, - .ctype = .usize, - .alignas = AlignAs.fromAbiAlignment( - .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())), - ), - }, - }; - const slice_const_u8 = try pool.fromFields(allocator, .@"struct", &fields, .forward); - fields = .{ - .{ - .name = .{ .index = .ptr }, - .ctype = try pool.getPointer(allocator, .{ - .elem_ctype = slice_const_u8, - .@"const" = true, - }), - .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)), - }, - .{ - .name = .{ .index = .len }, - .ctype = .usize, - .alignas = AlignAs.fromAbiAlignment( - .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())), - ), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - - .vector_8_i8_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .i8, - .len = 8, - .nonstring = true, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.i8.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_16_i8_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .i8, - .len = 16, - .nonstring = true, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.i8.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_32_i8_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .i8, - .len = 32, - .nonstring = true, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.i8.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_64_i8_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .i8, - .len = 64, - .nonstring = true, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.i8.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_1_u8_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u8, - .len = 1, - .nonstring = true, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_2_u8_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u8, - .len = 2, - .nonstring = true, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_4_u8_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u8, - .len = 4, - .nonstring = true, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_8_u8_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u8, - .len = 8, - .nonstring = true, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_16_u8_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u8, - .len = 16, - .nonstring = true, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_32_u8_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u8, - .len = 32, - .nonstring = true, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_64_u8_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u8, - .len = 64, - .nonstring = true, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_2_i16_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .i16, - .len = 2, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.i16.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_4_i16_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .i16, - .len = 4, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.i16.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_8_i16_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .i16, - .len = 8, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.i16.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_16_i16_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .i16, - .len = 16, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.i16.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_32_i16_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .i16, - .len = 32, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.i16.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_4_u16_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u16, - .len = 4, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u16.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_8_u16_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u16, - .len = 8, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u16.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_16_u16_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u16, - .len = 16, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u16.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_32_u16_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u16, - .len = 32, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u16.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_2_i32_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .i32, - .len = 2, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.i32.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_4_i32_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .i32, - .len = 4, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.i32.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_8_i32_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .i32, - .len = 8, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.i32.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_16_i32_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .i32, - .len = 16, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.i32.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_4_u32_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u32, - .len = 4, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u32.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_8_u32_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u32, - .len = 8, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u32.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_16_u32_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u32, - .len = 16, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u32.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_2_i64_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .i64, - .len = 2, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.i64.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_4_i64_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .i64, - .len = 4, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.i64.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_8_i64_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .i64, - .len = 8, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.i64.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_2_u64_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u64, - .len = 2, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u64.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_4_u64_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u64, - .len = 4, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u64.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_8_u64_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u64, - .len = 8, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u64.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_1_u128_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u128, - .len = 1, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u128.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_2_u128_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .u128, - .len = 2, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u128.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_1_u256_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = try pool.fromIntInfo(allocator, .{ - .signedness = .unsigned, - .bits = 256, - }, mod, kind), - .len = 1, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.u256.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_4_f16_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .f16, - .len = 4, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.f16.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_8_f16_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .f16, - .len = 8, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.f16.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_16_f16_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .f16, - .len = 16, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.f16.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_32_f16_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .f16, - .len = 32, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.f16.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_2_f32_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .f32, - .len = 2, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.f32.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_4_f32_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .f32, - .len = 4, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.f32.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_8_f32_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .f32, - .len = 8, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.f32.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_16_f32_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .f32, - .len = 16, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.f32.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_2_f64_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .f64, - .len = 2, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.f64.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_4_f64_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .f64, - .len = 4, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.f64.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_8_f64_type => { - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = .f64, - .len = 8, - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(Type.f64.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - - .undef, - .undef_bool, - .undef_usize, - .undef_u1, - .zero, - .zero_usize, - .zero_u1, - .zero_u8, - .one, - .one_usize, - .one_u1, - .one_u8, - .four_u8, - .negative_one, - .void_value, - .unreachable_value, - .null_value, - .bool_true, - .bool_false, - .empty_tuple, - .none, - => unreachable, // values, not types - - _ => |ip_index| switch (ip.indexToKey(ip_index)) { - .int_type => |int_info| return pool.fromIntInfo(allocator, int_info, mod, kind), - .ptr_type => |ptr_info| switch (ptr_info.flags.size) { - .one, .many, .c => { - const elem_ctype = elem_ctype: { - if (ptr_info.packed_offset.host_size > 0 and - ptr_info.flags.vector_index == .none) - break :elem_ctype try pool.fromIntInfo(allocator, .{ - .signedness = .unsigned, - .bits = ptr_info.packed_offset.host_size * 8, - }, mod, .forward); - const elem: Info.Aligned = .{ - .ctype = try pool.fromType( - allocator, - scratch, - Type.fromInterned(ptr_info.child), - pt, - mod, - .forward, - ), - .alignas = AlignAs.fromAlignment(.{ - .@"align" = ptr_info.flags.alignment, - .abi = Type.fromInterned(ptr_info.child).abiAlignment(zcu), - }), - }; - break :elem_ctype if (elem.alignas.abiOrder().compare(.gte)) - elem.ctype - else - try pool.getAligned(allocator, elem); - }; - const elem_tag: Info.Tag = switch (elem_ctype.info(pool)) { - .aligned => |aligned_info| aligned_info.ctype.info(pool), - else => |elem_tag| elem_tag, - }; - return pool.getPointer(allocator, .{ - .elem_ctype = elem_ctype, - .@"const" = switch (elem_tag) { - .basic, - .pointer, - .aligned, - .array, - .vector, - .fwd_decl, - .aggregate, - => ptr_info.flags.is_const, - .function => false, - }, - .@"volatile" = ptr_info.flags.is_volatile, - .nonstring = elem_ctype.isAnyChar() and switch (ptr_info.sentinel) { - .none => true, - .zero_u8 => false, - else => |sentinel| !Value.fromInterned(sentinel).compareAllWithZero(.eq, zcu), - }, - }); - }, - .slice => { - const target = &mod.resolved_target.result; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .ptr }, - .ctype = try pool.fromType( - allocator, - scratch, - Type.fromInterned(ip.slicePtrType(ip_index)), - pt, - mod, - kind, - ), - .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)), - }, - .{ - .name = .{ .index = .len }, - .ctype = .usize, - .alignas = AlignAs.fromAbiAlignment( - .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())), - ), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - }, - .array_type => |array_info| { - const len = array_info.lenIncludingSentinel(); - if (len == 0) return .void; - const elem_type = Type.fromInterned(array_info.child); - const elem_ctype = try pool.fromType( - allocator, - scratch, - elem_type, - pt, - mod, - kind.noParameter().asComplete(), - ); - if (elem_ctype.index == .void) return .void; - const array_ctype = try pool.getArray(allocator, .{ - .elem_ctype = elem_ctype, - .len = len, - .nonstring = elem_ctype.isAnyChar() and switch (array_info.sentinel) { - .none => true, - .zero_u8 => false, - else => |sentinel| !Value.fromInterned(sentinel).compareAllWithZero(.eq, zcu), - }, - }); - if (!kind.isParameter()) return array_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = array_ctype, - .alignas = AlignAs.fromAbiAlignment(elem_type.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .vector_type => |vector_info| { - if (vector_info.len == 0) return .void; - const elem_type = Type.fromInterned(vector_info.child); - const elem_ctype = try pool.fromType( - allocator, - scratch, - elem_type, - pt, - mod, - kind.noParameter().asComplete(), - ); - if (elem_ctype.index == .void) return .void; - const vector_ctype = try pool.getVector(allocator, .{ - .elem_ctype = elem_ctype, - .len = vector_info.len, - .nonstring = elem_ctype.isAnyChar(), - }); - if (!kind.isParameter()) return vector_ctype; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .array }, - .ctype = vector_ctype, - .alignas = AlignAs.fromAbiAlignment(elem_type.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .opt_type => |payload_type| { - if (ip.isNoReturn(payload_type)) return .void; - const payload_ctype = try pool.fromType( - allocator, - scratch, - Type.fromInterned(payload_type), - pt, - mod, - kind.noParameter(), - ); - if (payload_ctype.index == .void) return .bool; - switch (payload_type) { - .anyerror_type => return payload_ctype, - else => switch (ip.indexToKey(payload_type)) { - .ptr_type => |payload_ptr_info| if (payload_ptr_info.flags.size != .c and - !payload_ptr_info.flags.is_allowzero) return payload_ctype, - .error_set_type, .inferred_error_set_type => return payload_ctype, - else => {}, - }, - } - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .is_null }, - .ctype = .bool, - .alignas = AlignAs.fromAbiAlignment(.@"1"), - }, - .{ - .name = .{ .index = .payload }, - .ctype = payload_ctype, - .alignas = AlignAs.fromAbiAlignment( - Type.fromInterned(payload_type).abiAlignment(zcu), - ), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .anyframe_type => unreachable, - .error_union_type => |error_union_info| { - const error_set_bits = pt.zcu.errorSetBits(); - const error_set_ctype = try pool.fromIntInfo(allocator, .{ - .signedness = .unsigned, - .bits = error_set_bits, - }, mod, kind); - if (ip.isNoReturn(error_union_info.payload_type)) return error_set_ctype; - const payload_type = Type.fromInterned(error_union_info.payload_type); - const payload_ctype = try pool.fromType( - allocator, - scratch, - payload_type, - pt, - mod, - kind.noParameter(), - ); - if (payload_ctype.index == .void) return error_set_ctype; - const target = &mod.resolved_target.result; - var fields = [_]Info.Field{ - .{ - .name = .{ .index = .@"error" }, - .ctype = error_set_ctype, - .alignas = AlignAs.fromAbiAlignment( - .fromByteUnits(std.zig.target.intAlignment(target, error_set_bits)), - ), - }, - .{ - .name = .{ .index = .payload }, - .ctype = payload_ctype, - .alignas = AlignAs.fromAbiAlignment(payload_type.abiAlignment(zcu)), - }, - }; - return pool.fromFields(allocator, .@"struct", &fields, kind); - }, - .simple_type => unreachable, - .struct_type => { - const loaded_struct = ip.loadStructType(ip_index); - switch (loaded_struct.layout) { - .auto, .@"extern" => { - const fwd_decl = try pool.getFwdDecl(allocator, .{ - .tag = .@"struct", - .name = .{ .index = ip_index }, - }); - if (kind.isForward()) return if (ty.hasRuntimeBitsIgnoreComptime(zcu)) - fwd_decl - else - .void; - const scratch_top = scratch.items.len; - defer scratch.shrinkRetainingCapacity(scratch_top); - try scratch.ensureUnusedCapacity( - allocator, - loaded_struct.field_types.len * @typeInfo(Field).@"struct".fields.len, - ); - var hasher = Hasher.init; - var tag: Pool.Tag = .aggregate_struct; - var field_it = loaded_struct.iterateRuntimeOrder(ip); - while (field_it.next()) |field_index| { - const field_type = Type.fromInterned( - loaded_struct.field_types.get(ip)[field_index], - ); - const field_ctype = try pool.fromType( - allocator, - scratch, - field_type, - pt, - mod, - kind.noParameter(), - ); - if (field_ctype.index == .void) continue; - const field_name = try pool.string(allocator, loaded_struct.fieldName(ip, field_index).toSlice(ip)); - const field_alignas = AlignAs.fromAlignment(.{ - .@"align" = loaded_struct.fieldAlign(ip, field_index), - .abi = field_type.abiAlignment(zcu), - }); - pool.addHashedExtraAssumeCapacityTo(scratch, &hasher, Field, .{ - .name = field_name.index, - .ctype = field_ctype.index, - .flags = .{ .alignas = field_alignas }, - }); - if (field_alignas.abiOrder().compare(.lt)) - tag = .aggregate_struct_packed; - } - const fields_len: u32 = @intCast(@divExact( - scratch.items.len - scratch_top, - @typeInfo(Field).@"struct".fields.len, - )); - if (fields_len == 0) return .void; - try pool.ensureUnusedCapacity(allocator, 1); - const extra_index = try pool.addHashedExtra(allocator, &hasher, Aggregate, .{ - .fwd_decl = fwd_decl.index, - .fields_len = fields_len, - }, fields_len * @typeInfo(Field).@"struct".fields.len); - pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]); - return pool.tagTrailingExtraAssumeCapacity(hasher, tag, extra_index); - }, - .@"packed" => return pool.fromType( - allocator, - scratch, - Type.fromInterned(loaded_struct.backingIntTypeUnordered(ip)), - pt, - mod, - kind, - ), - } - }, - .tuple_type => |tuple_info| { - const scratch_top = scratch.items.len; - defer scratch.shrinkRetainingCapacity(scratch_top); - try scratch.ensureUnusedCapacity(allocator, tuple_info.types.len * - @typeInfo(Field).@"struct".fields.len); - var hasher = Hasher.init; - for (0..tuple_info.types.len) |field_index| { - if (tuple_info.values.get(ip)[field_index] != .none) continue; - const field_type = Type.fromInterned( - tuple_info.types.get(ip)[field_index], - ); - const field_ctype = try pool.fromType( - allocator, - scratch, - field_type, - pt, - mod, - kind.noParameter(), - ); - if (field_ctype.index == .void) continue; - const field_name = try pool.fmt(allocator, "f{d}", .{field_index}); - pool.addHashedExtraAssumeCapacityTo(scratch, &hasher, Field, .{ - .name = field_name.index, - .ctype = field_ctype.index, - .flags = .{ .alignas = AlignAs.fromAbiAlignment( - field_type.abiAlignment(zcu), - ) }, - }); - } - const fields_len: u32 = @intCast(@divExact( - scratch.items.len - scratch_top, - @typeInfo(Field).@"struct".fields.len, - )); - if (fields_len == 0) return .void; - if (kind.isForward()) { - try pool.ensureUnusedCapacity(allocator, 1); - const extra_index = try pool.addHashedExtra( - allocator, - &hasher, - FwdDeclAnon, - .{ .fields_len = fields_len }, - fields_len * @typeInfo(Field).@"struct".fields.len, - ); - pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]); - return pool.tagTrailingExtra( - allocator, - hasher, - .fwd_decl_struct_anon, - extra_index, - ); - } - const fwd_decl = try pool.fromType(allocator, scratch, ty, pt, mod, .forward); - try pool.ensureUnusedCapacity(allocator, 1); - const extra_index = try pool.addHashedExtra(allocator, &hasher, Aggregate, .{ - .fwd_decl = fwd_decl.index, - .fields_len = fields_len, - }, fields_len * @typeInfo(Field).@"struct".fields.len); - pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]); - return pool.tagTrailingExtraAssumeCapacity(hasher, .aggregate_struct, extra_index); - }, - .union_type => { - const loaded_union = ip.loadUnionType(ip_index); - switch (loaded_union.flagsUnordered(ip).layout) { - .auto, .@"extern" => { - const has_tag = loaded_union.hasTag(ip); - const fwd_decl = try pool.getFwdDecl(allocator, .{ - .tag = if (has_tag) .@"struct" else .@"union", - .name = .{ .index = ip_index }, - }); - if (kind.isForward()) return if (ty.hasRuntimeBitsIgnoreComptime(zcu)) - fwd_decl - else - .void; - const loaded_tag = loaded_union.loadTagType(ip); - const scratch_top = scratch.items.len; - defer scratch.shrinkRetainingCapacity(scratch_top); - try scratch.ensureUnusedCapacity( - allocator, - loaded_union.field_types.len * @typeInfo(Field).@"struct".fields.len, - ); - var hasher = Hasher.init; - var tag: Pool.Tag = .aggregate_union; - var payload_align: InternPool.Alignment = .@"1"; - for (0..loaded_union.field_types.len) |field_index| { - const field_type = Type.fromInterned( - loaded_union.field_types.get(ip)[field_index], - ); - if (ip.isNoReturn(field_type.toIntern())) continue; - const field_ctype = try pool.fromType( - allocator, - scratch, - field_type, - pt, - mod, - kind.noParameter(), - ); - if (field_ctype.index == .void) continue; - const field_name = try pool.string( - allocator, - loaded_tag.names.get(ip)[field_index].toSlice(ip), - ); - const field_alignas = AlignAs.fromAlignment(.{ - .@"align" = loaded_union.fieldAlign(ip, field_index), - .abi = field_type.abiAlignment(zcu), - }); - pool.addHashedExtraAssumeCapacityTo(scratch, &hasher, Field, .{ - .name = field_name.index, - .ctype = field_ctype.index, - .flags = .{ .alignas = field_alignas }, - }); - if (field_alignas.abiOrder().compare(.lt)) - tag = .aggregate_union_packed; - payload_align = payload_align.maxStrict(field_alignas.@"align"); - } - const fields_len: u32 = @intCast(@divExact( - scratch.items.len - scratch_top, - @typeInfo(Field).@"struct".fields.len, - )); - if (!has_tag) { - if (fields_len == 0) return .void; - try pool.ensureUnusedCapacity(allocator, 1); - const extra_index = try pool.addHashedExtra( - allocator, - &hasher, - Aggregate, - .{ .fwd_decl = fwd_decl.index, .fields_len = fields_len }, - fields_len * @typeInfo(Field).@"struct".fields.len, - ); - pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]); - return pool.tagTrailingExtraAssumeCapacity(hasher, tag, extra_index); - } - try pool.ensureUnusedCapacity(allocator, 2); - var struct_fields: [2]Info.Field = undefined; - var struct_fields_len: usize = 0; - if (loaded_tag.tag_ty != .comptime_int_type) { - const tag_type = Type.fromInterned(loaded_tag.tag_ty); - const tag_ctype: CType = try pool.fromType( - allocator, - scratch, - tag_type, - pt, - mod, - kind.noParameter(), - ); - if (tag_ctype.index != .void) { - struct_fields[struct_fields_len] = .{ - .name = .{ .index = .tag }, - .ctype = tag_ctype, - .alignas = AlignAs.fromAbiAlignment(tag_type.abiAlignment(zcu)), - }; - struct_fields_len += 1; - } - } - if (fields_len > 0) { - const payload_ctype = payload_ctype: { - const extra_index = try pool.addHashedExtra( - allocator, - &hasher, - AggregateAnon, - .{ - .index = ip_index, - .id = 0, - .fields_len = fields_len, - }, - fields_len * @typeInfo(Field).@"struct".fields.len, - ); - pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]); - break :payload_ctype pool.tagTrailingExtraAssumeCapacity( - hasher, - switch (tag) { - .aggregate_union => .aggregate_union_anon, - .aggregate_union_packed => .aggregate_union_packed_anon, - else => unreachable, - }, - extra_index, - ); - }; - if (payload_ctype.index != .void) { - struct_fields[struct_fields_len] = .{ - .name = .{ .index = .payload }, - .ctype = payload_ctype, - .alignas = AlignAs.fromAbiAlignment(payload_align), - }; - struct_fields_len += 1; - } - } - if (struct_fields_len == 0) return .void; - sortFields(struct_fields[0..struct_fields_len]); - return pool.getAggregate(allocator, .{ - .tag = .@"struct", - .name = .{ .fwd_decl = fwd_decl }, - .fields = struct_fields[0..struct_fields_len], - }); - }, - .@"packed" => return pool.fromIntInfo(allocator, .{ - .signedness = .unsigned, - .bits = @intCast(ty.bitSize(zcu)), - }, mod, kind), - } - }, - .opaque_type => return .void, - .enum_type => return pool.fromType( - allocator, - scratch, - Type.fromInterned(ip.loadEnumType(ip_index).tag_ty), - pt, - mod, - kind, - ), - .func_type => |func_info| if (func_info.is_generic) return .void else { - const scratch_top = scratch.items.len; - defer scratch.shrinkRetainingCapacity(scratch_top); - try scratch.ensureUnusedCapacity(allocator, func_info.param_types.len); - var hasher = Hasher.init; - const return_type = Type.fromInterned(func_info.return_type); - const return_ctype: CType = - if (!ip.isNoReturn(func_info.return_type)) try pool.fromType( - allocator, - scratch, - return_type, - pt, - mod, - kind.asParameter(), - ) else .void; - for (0..func_info.param_types.len) |param_index| { - const param_type = Type.fromInterned( - func_info.param_types.get(ip)[param_index], - ); - const param_ctype = try pool.fromType( - allocator, - scratch, - param_type, - pt, - mod, - kind.asParameter(), - ); - if (param_ctype.index == .void) continue; - hasher.update(param_ctype.hash(pool)); - scratch.appendAssumeCapacity(@intFromEnum(param_ctype.index)); - } - const param_ctypes_len: u32 = @intCast(scratch.items.len - scratch_top); - try pool.ensureUnusedCapacity(allocator, 1); - const extra_index = try pool.addHashedExtra(allocator, &hasher, Function, .{ - .return_ctype = return_ctype.index, - .param_ctypes_len = param_ctypes_len, - }, param_ctypes_len); - pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]); - return pool.tagTrailingExtraAssumeCapacity(hasher, switch (func_info.is_var_args) { - false => .function, - true => .function_varargs, - }, extra_index); - }, - .error_set_type, - .inferred_error_set_type, - => return pool.fromIntInfo(allocator, .{ - .signedness = .unsigned, - .bits = pt.zcu.errorSetBits(), - }, mod, kind), - - .undef, - .simple_value, - .variable, - .@"extern", - .func, - .int, - .err, - .error_union, - .enum_literal, - .enum_tag, - .empty_enum_value, - .float, - .ptr, - .slice, - .opt, - .aggregate, - .un, - .memoized_call, - => unreachable, // values, not types - }, - } - } - - pub fn getOrPutAdapted( - pool: *Pool, - allocator: std.mem.Allocator, - source_pool: *const Pool, - source_ctype: CType, - pool_adapter: anytype, - ) !struct { CType, bool } { - const tag = source_pool.items.items(.tag)[ - source_ctype.toPoolIndex() orelse return .{ source_ctype, true } - ]; - try pool.ensureUnusedCapacity(allocator, 1); - const CTypeAdapter = struct { - pool: *const Pool, - source_pool: *const Pool, - source_info: Info, - pool_adapter: @TypeOf(pool_adapter), - pub fn hash(map_adapter: @This(), key_ctype: CType) Map.Hash { - return key_ctype.hash(map_adapter.source_pool); - } - pub fn eql(map_adapter: @This(), _: CType, _: void, pool_index: usize) bool { - return map_adapter.source_info.eqlAdapted( - map_adapter.source_pool, - .fromPoolIndex(pool_index), - map_adapter.pool, - map_adapter.pool_adapter, - ); - } - }; - const source_info = source_ctype.info(source_pool); - const gop = pool.map.getOrPutAssumeCapacityAdapted(source_ctype, CTypeAdapter{ - .pool = pool, - .source_pool = source_pool, - .source_info = source_info, - .pool_adapter = pool_adapter, - }); - errdefer _ = pool.map.pop(); - const ctype: CType = .fromPoolIndex(gop.index); - if (!gop.found_existing) switch (source_info) { - .basic => unreachable, - .pointer => |pointer_info| pool.items.appendAssumeCapacity(switch (pointer_info.nonstring) { - false => .{ - .tag = tag, - .data = @intFromEnum(pool_adapter.copy(pointer_info.elem_ctype).index), - }, - true => .{ - .tag = .nonstring, - .data = @intFromEnum(pool_adapter.copy(.{ .index = @enumFromInt( - source_pool.items.items(.data)[source_ctype.toPoolIndex().?], - ) }).index), - }, - }), - .aligned => |aligned_info| pool.items.appendAssumeCapacity(.{ - .tag = tag, - .data = try pool.addExtra(allocator, Aligned, .{ - .ctype = pool_adapter.copy(aligned_info.ctype).index, - .flags = .{ .alignas = aligned_info.alignas }, - }, 0), - }), - .array, .vector => |sequence_info| pool.items.appendAssumeCapacity(switch (sequence_info.nonstring) { - false => .{ - .tag = tag, - .data = switch (tag) { - .array_small, .vector => try pool.addExtra(allocator, SequenceSmall, .{ - .elem_ctype = pool_adapter.copy(sequence_info.elem_ctype).index, - .len = @intCast(sequence_info.len), - }, 0), - .array_large => try pool.addExtra(allocator, SequenceLarge, .{ - .elem_ctype = pool_adapter.copy(sequence_info.elem_ctype).index, - .len_lo = @truncate(sequence_info.len >> 0), - .len_hi = @truncate(sequence_info.len >> 32), - }, 0), - else => unreachable, - }, - }, - true => .{ - .tag = .nonstring, - .data = @intFromEnum(pool_adapter.copy(.{ .index = @enumFromInt( - source_pool.items.items(.data)[source_ctype.toPoolIndex().?], - ) }).index), - }, - }), - .fwd_decl => |fwd_decl_info| switch (fwd_decl_info.name) { - .anon => |fields| { - pool.items.appendAssumeCapacity(.{ - .tag = tag, - .data = try pool.addExtra(allocator, FwdDeclAnon, .{ - .fields_len = fields.len, - }, fields.len * @typeInfo(Field).@"struct".fields.len), - }); - for (0..fields.len) |field_index| { - const field = fields.at(field_index, source_pool); - const field_name = if (field.name.toPoolSlice(source_pool)) |slice| - try pool.string(allocator, slice) - else - field.name; - pool.addExtraAssumeCapacity(Field, .{ - .name = field_name.index, - .ctype = pool_adapter.copy(field.ctype).index, - .flags = .{ .alignas = field.alignas }, - }); - } - }, - .index => |index| pool.items.appendAssumeCapacity(.{ - .tag = tag, - .data = @intFromEnum(index), - }), - }, - .aggregate => |aggregate_info| { - pool.items.appendAssumeCapacity(.{ - .tag = tag, - .data = switch (aggregate_info.name) { - .anon => |anon| try pool.addExtra(allocator, AggregateAnon, .{ - .index = anon.index, - .id = anon.id, - .fields_len = aggregate_info.fields.len, - }, aggregate_info.fields.len * @typeInfo(Field).@"struct".fields.len), - .fwd_decl => |fwd_decl| try pool.addExtra(allocator, Aggregate, .{ - .fwd_decl = pool_adapter.copy(fwd_decl).index, - .fields_len = aggregate_info.fields.len, - }, aggregate_info.fields.len * @typeInfo(Field).@"struct".fields.len), - }, - }); - for (0..aggregate_info.fields.len) |field_index| { - const field = aggregate_info.fields.at(field_index, source_pool); - const field_name = if (field.name.toPoolSlice(source_pool)) |slice| - try pool.string(allocator, slice) - else - field.name; - pool.addExtraAssumeCapacity(Field, .{ - .name = field_name.index, - .ctype = pool_adapter.copy(field.ctype).index, - .flags = .{ .alignas = field.alignas }, - }); - } - }, - .function => |function_info| { - pool.items.appendAssumeCapacity(.{ - .tag = tag, - .data = try pool.addExtra(allocator, Function, .{ - .return_ctype = pool_adapter.copy(function_info.return_ctype).index, - .param_ctypes_len = function_info.param_ctypes.len, - }, function_info.param_ctypes.len), - }); - for (0..function_info.param_ctypes.len) |param_index| pool.extra.appendAssumeCapacity( - @intFromEnum(pool_adapter.copy( - function_info.param_ctypes.at(param_index, source_pool), - ).index), - ); - }, - }; - assert(source_info.eqlAdapted(source_pool, ctype, pool, pool_adapter)); - assert(source_ctype.hash(source_pool) == ctype.hash(pool)); - return .{ ctype, gop.found_existing }; - } - - pub fn string(pool: *Pool, allocator: std.mem.Allocator, slice: []const u8) !String { - try pool.string_bytes.appendSlice(allocator, slice); - return pool.trailingString(allocator); - } - - pub fn fmt( - pool: *Pool, - allocator: std.mem.Allocator, - comptime fmt_str: []const u8, - fmt_args: anytype, - ) !String { - try pool.string_bytes.print(allocator, fmt_str, fmt_args); - return pool.trailingString(allocator); - } - - fn ensureUnusedCapacity(pool: *Pool, allocator: std.mem.Allocator, len: u32) !void { - try pool.map.ensureUnusedCapacity(allocator, len); - try pool.items.ensureUnusedCapacity(allocator, len); - } - - const Hasher = struct { - const Impl = std.hash.Wyhash; - impl: Impl, - - const init: Hasher = .{ .impl = Impl.init(0) }; - - fn updateExtra(hasher: *Hasher, comptime Extra: type, extra: Extra, pool: *const Pool) void { - inline for (@typeInfo(Extra).@"struct".fields) |field| { - const value = @field(extra, field.name); - switch (field.type) { - Pool.Tag, String, CType => unreachable, - CType.Index => hasher.update((CType{ .index = value }).hash(pool)), - String.Index => if ((String{ .index = value }).toPoolSlice(pool)) |slice| - hasher.update(slice) - else - hasher.update(@intFromEnum(value)), - else => hasher.update(value), - } - } - } - fn update(hasher: *Hasher, data: anytype) void { - switch (@TypeOf(data)) { - Pool.Tag => @compileError("pass tag to final"), - CType, CType.Index => @compileError("hash ctype.hash(pool) instead"), - String, String.Index => @compileError("hash string.slice(pool) instead"), - u32, InternPool.Index, Aligned.Flags => hasher.impl.update(std.mem.asBytes(&data)), - []const u8 => hasher.impl.update(data), - else => @compileError("unhandled type: " ++ @typeName(@TypeOf(data))), - } - } - - fn final(hasher: Hasher, tag: Pool.Tag) Map.Hash { - var impl = hasher.impl; - impl.update(std.mem.asBytes(&tag)); - return @truncate(impl.final()); - } - }; - - fn tagData( - pool: *Pool, - allocator: std.mem.Allocator, - hasher: Hasher, - tag: Pool.Tag, - data: u32, - ) !CType { - try pool.ensureUnusedCapacity(allocator, 1); - const Key = struct { hash: Map.Hash, tag: Pool.Tag, data: u32 }; - const CTypeAdapter = struct { - pool: *const Pool, - pub fn hash(_: @This(), key: Key) Map.Hash { - return key.hash; - } - pub fn eql(ctype_adapter: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { - const rhs_item = ctype_adapter.pool.items.get(rhs_index); - return lhs_key.tag == rhs_item.tag and lhs_key.data == rhs_item.data; - } - }; - const gop = pool.map.getOrPutAssumeCapacityAdapted( - Key{ .hash = hasher.final(tag), .tag = tag, .data = data }, - CTypeAdapter{ .pool = pool }, - ); - if (!gop.found_existing) pool.items.appendAssumeCapacity(.{ .tag = tag, .data = data }); - return .fromPoolIndex(gop.index); - } - - fn tagExtra( - pool: *Pool, - allocator: std.mem.Allocator, - tag: Pool.Tag, - comptime Extra: type, - extra: Extra, - ) !CType { - var hasher = Hasher.init; - hasher.updateExtra(Extra, extra, pool); - return pool.tagTrailingExtra( - allocator, - hasher, - tag, - try pool.addExtra(allocator, Extra, extra, 0), - ); - } - - fn tagTrailingExtra( - pool: *Pool, - allocator: std.mem.Allocator, - hasher: Hasher, - tag: Pool.Tag, - extra_index: ExtraIndex, - ) !CType { - try pool.ensureUnusedCapacity(allocator, 1); - return pool.tagTrailingExtraAssumeCapacity(hasher, tag, extra_index); - } - - fn tagTrailingExtraAssumeCapacity( - pool: *Pool, - hasher: Hasher, - tag: Pool.Tag, - extra_index: ExtraIndex, - ) CType { - const Key = struct { hash: Map.Hash, tag: Pool.Tag, extra: []const u32 }; - const CTypeAdapter = struct { - pool: *const Pool, - pub fn hash(_: @This(), key: Key) Map.Hash { - return key.hash; - } - pub fn eql(ctype_adapter: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { - const rhs_item = ctype_adapter.pool.items.get(rhs_index); - if (lhs_key.tag != rhs_item.tag) return false; - const rhs_extra = ctype_adapter.pool.extra.items[rhs_item.data..]; - return std.mem.startsWith(u32, rhs_extra, lhs_key.extra); - } - }; - const gop = pool.map.getOrPutAssumeCapacityAdapted( - Key{ .hash = hasher.final(tag), .tag = tag, .extra = pool.extra.items[extra_index..] }, - CTypeAdapter{ .pool = pool }, - ); - if (gop.found_existing) - pool.extra.shrinkRetainingCapacity(extra_index) - else - pool.items.appendAssumeCapacity(.{ .tag = tag, .data = extra_index }); - return .fromPoolIndex(gop.index); - } - - fn sortFields(fields: []Info.Field) void { - std.mem.sort(Info.Field, fields, {}, struct { - fn before(_: void, lhs_field: Info.Field, rhs_field: Info.Field) bool { - return lhs_field.alignas.order(rhs_field.alignas).compare(.gt); - } - }.before); - } - - fn trailingString(pool: *Pool, allocator: std.mem.Allocator) !String { - const start = pool.string_indices.getLast(); - const slice: []const u8 = pool.string_bytes.items[start..]; - if (slice.len >= 2 and slice[0] == 'f' and switch (slice[1]) { - '0' => slice.len == 2, - '1'...'9' => true, - else => false, - }) if (std.fmt.parseInt(u31, slice[1..], 10)) |unnamed| { - pool.string_bytes.shrinkRetainingCapacity(start); - return String.fromUnnamed(unnamed); - } else |_| {}; - if (std.meta.stringToEnum(String.Index, slice)) |index| { - pool.string_bytes.shrinkRetainingCapacity(start); - return .{ .index = index }; - } - - try pool.string_map.ensureUnusedCapacity(allocator, 1); - try pool.string_indices.ensureUnusedCapacity(allocator, 1); - - const gop = pool.string_map.getOrPutAssumeCapacityAdapted(slice, String.Adapter{ .pool = pool }); - if (gop.found_existing) - pool.string_bytes.shrinkRetainingCapacity(start) - else - pool.string_indices.appendAssumeCapacity(@intCast(pool.string_bytes.items.len)); - return String.fromPoolIndex(gop.index); - } - - const Item = struct { - tag: Pool.Tag, - data: u32, - }; - - const ExtraIndex = u32; - - const Tag = enum(u8) { - basic, - pointer, - pointer_const, - pointer_volatile, - pointer_const_volatile, - aligned, - array_small, - array_large, - vector, - nonstring, - fwd_decl_struct_anon, - fwd_decl_union_anon, - fwd_decl_struct, - fwd_decl_union, - aggregate_struct_anon, - aggregate_struct_packed_anon, - aggregate_union_anon, - aggregate_union_packed_anon, - aggregate_struct, - aggregate_struct_packed, - aggregate_union, - aggregate_union_packed, - function, - function_varargs, - }; - - const Aligned = struct { - ctype: CType.Index, - flags: Flags, - - const Flags = packed struct(u32) { - alignas: AlignAs, - _: u20 = 0, - }; - }; - - const SequenceSmall = struct { - elem_ctype: CType.Index, - len: u32, - }; - - const SequenceLarge = struct { - elem_ctype: CType.Index, - len_lo: u32, - len_hi: u32, - - fn len(extra: SequenceLarge) u64 { - return @as(u64, extra.len_lo) << 0 | - @as(u64, extra.len_hi) << 32; - } - }; - - const Field = struct { - name: String.Index, - ctype: CType.Index, - flags: Flags, - - const Flags = Aligned.Flags; - }; - - const FwdDeclAnon = struct { - fields_len: u32, - }; - - const AggregateAnon = struct { - index: InternPool.Index, - id: u32, - fields_len: u32, - }; - - const Aggregate = struct { - fwd_decl: CType.Index, - fields_len: u32, - }; - - const Function = struct { - return_ctype: CType.Index, - param_ctypes_len: u32, - }; - - fn addExtra( - pool: *Pool, - allocator: std.mem.Allocator, - comptime Extra: type, - extra: Extra, - trailing_len: usize, - ) !ExtraIndex { - try pool.extra.ensureUnusedCapacity( - allocator, - @typeInfo(Extra).@"struct".fields.len + trailing_len, - ); - defer pool.addExtraAssumeCapacity(Extra, extra); - return @intCast(pool.extra.items.len); - } - fn addExtraAssumeCapacity(pool: *Pool, comptime Extra: type, extra: Extra) void { - addExtraAssumeCapacityTo(&pool.extra, Extra, extra); - } - fn addExtraAssumeCapacityTo( - array: *std.ArrayList(u32), - comptime Extra: type, - extra: Extra, - ) void { - inline for (@typeInfo(Extra).@"struct".fields) |field| { - const value = @field(extra, field.name); - array.appendAssumeCapacity(switch (field.type) { - u32 => value, - CType.Index, String.Index, InternPool.Index => @intFromEnum(value), - Aligned.Flags => @bitCast(value), - else => @compileError("bad field type: " ++ field.name ++ ": " ++ - @typeName(field.type)), - }); - } - } - - fn addHashedExtra( - pool: *Pool, - allocator: std.mem.Allocator, - hasher: *Hasher, - comptime Extra: type, - extra: Extra, - trailing_len: usize, - ) !ExtraIndex { - hasher.updateExtra(Extra, extra, pool); - return pool.addExtra(allocator, Extra, extra, trailing_len); - } - fn addHashedExtraAssumeCapacity( - pool: *Pool, - hasher: *Hasher, - comptime Extra: type, - extra: Extra, - ) void { - hasher.updateExtra(Extra, extra, pool); - pool.addExtraAssumeCapacity(Extra, extra); - } - fn addHashedExtraAssumeCapacityTo( - pool: *Pool, - array: *std.ArrayList(u32), - hasher: *Hasher, - comptime Extra: type, - extra: Extra, - ) void { - hasher.updateExtra(Extra, extra, pool); - addExtraAssumeCapacityTo(array, Extra, extra); - } - - const ExtraTrail = struct { - extra_index: ExtraIndex, - - fn next( - extra_trail: *ExtraTrail, - len: u32, - comptime Extra: type, - pool: *const Pool, - ) []const Extra { - defer extra_trail.extra_index += @intCast(len); - return @ptrCast(pool.extra.items[extra_trail.extra_index..][0..len]); - } - }; - - fn getExtraTrail( - pool: *const Pool, - comptime Extra: type, - extra_index: ExtraIndex, - ) struct { extra: Extra, trail: ExtraTrail } { - var extra: Extra = undefined; - const fields = @typeInfo(Extra).@"struct".fields; - inline for (fields, pool.extra.items[extra_index..][0..fields.len]) |field, value| - @field(extra, field.name) = switch (field.type) { - u32 => value, - CType.Index, String.Index, InternPool.Index => @enumFromInt(value), - Aligned.Flags => @bitCast(value), - else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), - }; - return .{ - .extra = extra, - .trail = .{ .extra_index = extra_index + @as(ExtraIndex, @intCast(fields.len)) }, - }; - } - - fn getExtra(pool: *const Pool, comptime Extra: type, extra_index: ExtraIndex) Extra { - return pool.getExtraTrail(Extra, extra_index).extra; - } -}; - -pub const AlignAs = packed struct { - @"align": InternPool.Alignment, - abi: InternPool.Alignment, - - pub fn fromAlignment(alignas: AlignAs) AlignAs { - assert(alignas.abi != .none); - return .{ - .@"align" = if (alignas.@"align" != .none) alignas.@"align" else alignas.abi, - .abi = alignas.abi, - }; - } - pub fn fromAbiAlignment(abi: InternPool.Alignment) AlignAs { - assert(abi != .none); - return .{ .@"align" = abi, .abi = abi }; - } - pub fn fromByteUnits(@"align": u64, abi: u64) AlignAs { - return fromAlignment(.{ - .@"align" = InternPool.Alignment.fromByteUnits(@"align"), - .abi = InternPool.Alignment.fromNonzeroByteUnits(abi), - }); - } - - pub fn order(lhs: AlignAs, rhs: AlignAs) std.math.Order { - return lhs.@"align".order(rhs.@"align"); - } - pub fn abiOrder(alignas: AlignAs) std.math.Order { - return alignas.@"align".order(alignas.abi); - } - pub fn toByteUnits(alignas: AlignAs) u64 { - return alignas.@"align".toByteUnits().?; - } -}; - -const std = @import("std"); -const assert = std.debug.assert; -const Writer = std.Io.Writer; - -const CType = @This(); -const InternPool = @import("../../InternPool.zig"); -const Module = @import("../../Package/Module.zig"); -const Type = @import("../../Type.zig"); -const Value = @import("../../Value.zig"); -const Zcu = @import("../../Zcu.zig"); diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig @@ -0,0 +1,1023 @@ +pub const CType = union(enum) { + pub const render_defs = @import("type/render_defs.zig"); + + // The first nodes are primitive types (or standard typedefs). + + void, + bool, + int: Int, + float: Float, + + // These next nodes are all typedefs, structs, or unions. + + @"fn": Type, + @"enum": Type, + bitpack: Type, + @"struct": Type, + union_auto: Type, + union_extern: Type, + slice: Type, + opt: Type, + arr: Type, + vec: Type, + errunion: struct { payload_ty: Type }, + aligned: struct { + ty: Type, + alignment: InternPool.Alignment, + }, + bigint: BigInt, + + // The remaining nodes have children. + + pointer: struct { + @"const": bool, + @"volatile": bool, + elem_ty: *const CType, + nonstring: bool, + }, + array: struct { + len: u64, + elem_ty: *const CType, + nonstring: bool, + }, + function: struct { + param_tys: []const CType, + ret_ty: *const CType, + varargs: bool, + }, + + /// Returns `true` if this node has a postfix operator, meaning an `[...]` or `(...)` appears + /// after the identifier in a declarator with this type. In this case, if this node is wrapped + /// in a pointer type, we will need to add parentheses due to operator precedence. + /// + /// For instance, when lowering a Zig declaration `foo: *const fn (c_int) void`, it would be a + /// bug to write the C declarator as `void *foo(int)`, because the `(int)` suffix declaring the + /// function type has higher precedence than the `*` prefix declaring the pointer type. Instead, + /// this type must be lowered as `void (*foo)(int)`. + fn kind(cty: *const CType) enum { + /// `cty` is just a C type specifier, i.e. a typedef or a named struct/union type. + specifier, + /// `cty` is a C function or array type. It will have a postfix "operator" in its suffix to + /// declare the type, either `(...)` (for a function type) or `[...]` (for an array type). + postfix_op, + /// `cty` is a C pointer type. Its prefix will end with "*". + pointer, + } { + return switch (cty.*) { + .void, + .bool, + .int, + .float, + .@"fn", + .@"enum", + .bitpack, + .@"struct", + .union_auto, + .union_extern, + .slice, + .opt, + .arr, + .vec, + .errunion, + .aligned, + .bigint, + => .specifier, + + .array, + .function, + => .postfix_op, + + .pointer => .pointer, + }; + } + + pub const Int = enum { + char, + + @"unsigned short", + @"unsigned int", + @"unsigned long", + @"unsigned long long", + + @"signed short", + @"signed int", + @"signed long", + @"signed long long", + + uint8_t, + uint16_t, + uint32_t, + uint64_t, + zig_u128, + + int8_t, + int16_t, + int32_t, + int64_t, + zig_i128, + + uintptr_t, + intptr_t, + + pub fn bits(int: Int, target: *const std.Target) u16 { + return switch (int) { + // zig fmt: off + .char => target.cTypeBitSize(.char), + + .@"unsigned short" => target.cTypeBitSize(.ushort), + .@"unsigned int" => target.cTypeBitSize(.uint), + .@"unsigned long" => target.cTypeBitSize(.ulong), + .@"unsigned long long" => target.cTypeBitSize(.ulonglong), + + .@"signed short" => target.cTypeBitSize(.short), + .@"signed int" => target.cTypeBitSize(.int), + .@"signed long" => target.cTypeBitSize(.long), + .@"signed long long" => target.cTypeBitSize(.longlong), + + .uintptr_t, .intptr_t => target.ptrBitWidth(), + + .uint8_t, .int8_t => 8, + .uint16_t, .int16_t => 16, + .uint32_t, .int32_t => 32, + .uint64_t, .int64_t => 64, + .zig_u128, .zig_i128 => 128, + // zig fmt: on + }; + } + }; + + pub const BigInt = struct { + limb_size: LimbSize, + /// Always greater than 1. + limbs_len: u16, + + pub const LimbSize = enum { + @"8", + @"16", + @"32", + @"64", + @"128", + pub fn bits(s: LimbSize) u8 { + return switch (s) { + .@"8" => 8, + .@"16" => 16, + .@"32" => 32, + .@"64" => 64, + .@"128" => 128, + }; + } + pub fn unsigned(s: LimbSize) Int { + return switch (s) { + .@"8" => .uint8_t, + .@"16" => .uint16_t, + .@"32" => .uint32_t, + .@"64" => .uint64_t, + .@"128" => .zig_u128, + }; + } + pub fn signed(s: LimbSize) Int { + return switch (s) { + .@"8" => .int8_t, + .@"16" => .int16_t, + .@"32" => .int32_t, + .@"64" => .int64_t, + .@"128" => .zig_i128, + }; + } + }; + }; + + pub const Float = enum { + @"long double", + zig_f16, + zig_f32, + zig_f64, + zig_f80, + zig_f128, + zig_u128, + zig_i128, + }; + + pub fn isStringElem(cty: CType) bool { + return switch (cty) { + .int => |int| switch (int) { + .char, .int8_t, .uint8_t => true, + else => false, + }, + else => false, + }; + } + + pub fn lower( + ty: Type, + deps: *Dependencies, + arena: Allocator, + zcu: *const Zcu, + ) Allocator.Error!CType { + return lowerInner(ty, false, deps, arena, zcu); + } + fn lowerInner( + start_ty: Type, + allow_incomplete: bool, + deps: *Dependencies, + arena: Allocator, + zcu: *const Zcu, + ) Allocator.Error!CType { + const gpa = zcu.comp.gpa; + const ip = &zcu.intern_pool; + var cur_ty = start_ty; + while (true) { + switch (cur_ty.zigTypeTag(zcu)) { + .type, + .comptime_int, + .comptime_float, + .undefined, + .null, + .enum_literal, + .@"opaque", + .noreturn, + .void, + => return .void, + + .bool => return .bool, + + .int, .error_set => switch (classifyInt(cur_ty, zcu)) { + .void => return .void, + .small => |s| return .{ .int = s }, + .big => |big| { + try deps.bigint.put(gpa, big, {}); + return .{ .bigint = big }; + }, + }, + + .float => return .{ .float = switch (cur_ty.toIntern()) { + .c_longdouble_type => .@"long double", + .f16_type => .zig_f16, + .f32_type => .zig_f32, + .f64_type => .zig_f64, + .f80_type => .zig_f80, + .f128_type => .zig_f128, + else => unreachable, + } }, + .vector => { + try deps.addType(gpa, cur_ty, allow_incomplete); + return .{ .vec = cur_ty }; + }, + .array => { + try deps.addType(gpa, cur_ty, allow_incomplete); + return .{ .arr = cur_ty }; + }, + + .pointer => { + const ptr = cur_ty.ptrInfo(zcu); + switch (ptr.flags.size) { + .slice => { + try deps.addType(gpa, cur_ty, allow_incomplete); + return .{ .slice = cur_ty }; + }, + .one, .many, .c => { + const elem_ty: Type = .fromInterned(ptr.child); + const is_fn_ptr = elem_ty.zigTypeTag(zcu) == .@"fn"; + const elem_cty: CType = elem_cty: { + if (ptr.packed_offset.host_size > 0 and ptr.flags.vector_index == .none) { + switch (classifyBitInt(.unsigned, ptr.packed_offset.host_size * 8, zcu)) { + .void => break :elem_cty .void, + .small => |s| break :elem_cty .{ .int = s }, + .big => |big| { + try deps.bigint.put(gpa, big, {}); + break :elem_cty .{ .bigint = big }; + }, + } + } + if (ptr.flags.alignment != .none and !is_fn_ptr) { + // The pointer has an explicit alignment---if it's an underalignment + // then we need to use an "aligned" typedef. + const ptr_align = ptr.flags.alignment; + if (!alwaysHasLayout(elem_ty, ip) or + ptr_align.compareStrict(.lt, elem_ty.abiAlignment(zcu))) + { + const gop = try deps.aligned_type_fwd.getOrPut(gpa, elem_ty.toIntern()); + if (!gop.found_existing) gop.value_ptr.* = 0; + gop.value_ptr.* |= @as(u64, 1) << ptr_align.toLog2Units(); + break :elem_cty .{ .aligned = .{ + .ty = elem_ty, + .alignment = ptr_align, + } }; + } + } + break :elem_cty try .lowerInner(elem_ty, true, deps, arena, zcu); + }; + const elem_cty_buf = try arena.create(CType); + elem_cty_buf.* = elem_cty; + return .{ .pointer = .{ + .@"const" = ptr.flags.is_const and !is_fn_ptr, + .@"volatile" = ptr.flags.is_volatile and !is_fn_ptr, + .elem_ty = elem_cty_buf, + .nonstring = nonstring: { + if (!elem_cty.isStringElem()) break :nonstring false; + if (ptr.sentinel == .none) break :nonstring true; + break :nonstring Value.compareHetero( + .fromInterned(ptr.sentinel), + .neq, + .zero_comptime_int, + zcu, + ); + }, + } }; + }, + } + }, + + .@"fn" => { + const func_type = ip.indexToKey(cur_ty.toIntern()).func_type; + direct: { + const ret_ty: Type = .fromInterned(func_type.return_type); + if (!alwaysHasLayout(ret_ty, ip)) break :direct; + var params_len: usize = 0; // only counts parameter types with runtime bits + for (func_type.param_types.get(ip)) |param_ty_ip| { + const param_ty: Type = .fromInterned(param_ty_ip); + if (!alwaysHasLayout(param_ty, ip)) break :direct; + if (param_ty.hasRuntimeBits(zcu)) params_len += 1; + } + // We can actually write this function type directly! + if (!cur_ty.fnHasRuntimeBits(zcu)) return .void; + const ret_cty_buf = try arena.create(CType); + if (!ret_ty.hasRuntimeBits(zcu)) { + // Incomplete function return types must always be `void`. + ret_cty_buf.* = .void; + } else { + ret_cty_buf.* = try .lowerInner(ret_ty, allow_incomplete, deps, arena, zcu); + } + const param_cty_buf = try arena.alloc(CType, params_len); + var param_index: usize = 0; + for (func_type.param_types.get(ip)) |param_ty_ip| { + const param_ty: Type = .fromInterned(param_ty_ip); + if (!param_ty.hasRuntimeBits(zcu)) continue; + param_cty_buf[param_index] = try .lowerInner(param_ty, allow_incomplete, deps, arena, zcu); + param_index += 1; + } + assert(param_index == params_len); + return .{ .function = .{ + .ret_ty = ret_cty_buf, + .param_tys = param_cty_buf, + .varargs = func_type.is_var_args, + } }; + } + try deps.addType(gpa, cur_ty, allow_incomplete); + return .{ .@"fn" = cur_ty }; + }, + + .@"struct" => { + try deps.addType(gpa, cur_ty, allow_incomplete); + switch (cur_ty.containerLayout(zcu)) { + .auto, .@"extern" => return .{ .@"struct" = cur_ty }, + .@"packed" => return .{ .bitpack = cur_ty }, + } + }, + .@"union" => { + try deps.addType(gpa, cur_ty, allow_incomplete); + switch (cur_ty.containerLayout(zcu)) { + .auto => return .{ .union_auto = cur_ty }, + .@"extern" => return .{ .union_extern = cur_ty }, + .@"packed" => return .{ .bitpack = cur_ty }, + } + }, + .@"enum" => { + try deps.addType(gpa, cur_ty, allow_incomplete); + return .{ .@"enum" = cur_ty }; + }, + + .optional => { + // This query does not require any type resolution. + if (cur_ty.optionalReprIsPayload(zcu)) { + // Either a pointer-like optional, or an optional error set. Just lower the payload. + cur_ty = cur_ty.optionalChild(zcu); + continue; + } + if (alwaysHasLayout(cur_ty, ip)) switch (classifyOptional(cur_ty, zcu)) { + .error_set, .ptr_like, .slice_like => unreachable, // handled above + .npv_payload => return .void, + .opv_payload, .@"struct" => {}, + }; + try deps.addType(gpa, cur_ty, allow_incomplete); + return .{ .opt = cur_ty }; + }, + + .error_union => { + const payload_ty = cur_ty.errorUnionPayload(zcu); + if (allow_incomplete) { + try deps.errunion_type_fwd.put(gpa, payload_ty.toIntern(), {}); + } else { + try deps.errunion_type.put(gpa, payload_ty.toIntern(), {}); + } + return .{ .errunion = .{ + .payload_ty = payload_ty, + } }; + }, + + .frame, + .@"anyframe", + => unreachable, + } + comptime unreachable; + } + } + + pub fn classifyOptional(opt_ty: Type, zcu: *const Zcu) enum { + /// The optional is something like `?noreturn`; it lowers to `void`. + npv_payload, + /// The payload type is an error set; the representation matches that of the error set, with + /// the value 0 representing `null`. + error_set, + /// The payload type is a non-optional pointer; the NULL pointer is used for `null`. + ptr_like, + /// The payload type is a non-optional slice; a NULL pointer field is used for `null`. + slice_like, + /// The optional is something like `?void`; it lowers to a struct, but one containing only + /// one field `is_null` (the payload is omitted). + opv_payload, + /// The optional uses the "default" lowering of a struct with two fields, like this: + /// struct optional_1234 { payload_ty payload; bool is_null; } + @"struct", + } { + const payload_ty = opt_ty.optionalChild(zcu); + if (opt_ty.optionalReprIsPayload(zcu)) { + return switch (payload_ty.zigTypeTag(zcu)) { + .error_set => .error_set, + .pointer => if (payload_ty.isSlice(zcu)) .slice_like else .ptr_like, + else => unreachable, + }; + } else { + return switch (payload_ty.classify(zcu)) { + .no_possible_value => .npv_payload, + .one_possible_value => .opv_payload, + else => .@"struct", + }; + } + } + + pub const IntClass = union(enum) { + /// The integer type is zero-bit, so lowers to `void`. + void, + /// The integer is under 128 bits long, so lowers to this C integer type. + small: Int, + /// The integer is over 128 bits long, so lowers to an array of limbs. + big: BigInt, + }; + + /// Asserts that `ty` is an integer, enum, bitpack, or error set. + pub fn classifyInt(ty: Type, zcu: *const Zcu) IntClass { + const int_ty: Type = switch (ty.zigTypeTag(zcu)) { + .error_set => return classifyBitInt(.unsigned, zcu.errorSetBits(), zcu), + .@"enum" => ty.intTagType(zcu), + .@"struct", .@"union" => ty.bitpackBackingInt(zcu), + .int => ty, + else => unreachable, + }; + switch (int_ty.toIntern()) { + // zig fmt: off + .usize_type => return .{ .small = .uintptr_t }, + .isize_type => return .{ .small = .intptr_t }, + + .c_char_type => return .{ .small = .char }, + + .c_short_type => return .{ .small = .@"signed short" }, + .c_int_type => return .{ .small = .@"signed int" }, + .c_long_type => return .{ .small = .@"signed long" }, + .c_longlong_type => return .{ .small = .@"signed long long" }, + + .c_ushort_type => return .{ .small = .@"unsigned short" }, + .c_uint_type => return .{ .small = .@"unsigned int" }, + .c_ulong_type => return .{ .small = .@"unsigned long" }, + .c_ulonglong_type => return .{ .small = .@"unsigned long long" }, + // zig fmt: on + + else => { + const int = ty.intInfo(zcu); + return classifyBitInt(int.signedness, int.bits, zcu); + }, + } + } + fn classifyBitInt(signedness: std.builtin.Signedness, bits: u16, zcu: *const Zcu) IntClass { + return switch (bits) { + 0 => .void, + 1...8 => switch (signedness) { + .unsigned => .{ .small = .uint8_t }, + .signed => .{ .small = .int8_t }, + }, + 9...16 => switch (signedness) { + .unsigned => .{ .small = .uint16_t }, + .signed => .{ .small = .int16_t }, + }, + 17...32 => switch (signedness) { + .unsigned => .{ .small = .uint32_t }, + .signed => .{ .small = .int32_t }, + }, + 33...64 => switch (signedness) { + .unsigned => .{ .small = .uint64_t }, + .signed => .{ .small = .int64_t }, + }, + 65...128 => switch (signedness) { + .unsigned => .{ .small = .zig_u128 }, + .signed => .{ .small = .zig_i128 }, + }, + else => { + @branchHint(.unlikely); + const target = zcu.getTarget(); + const limb_bytes = std.zig.target.intAlignment(target, bits); + return .{ .big = .{ + .limb_size = switch (limb_bytes) { + 1 => .@"8", + 2 => .@"16", + 4 => .@"32", + 8 => .@"64", + 16 => .@"128", + else => unreachable, + }, + .limbs_len = @divExact( + std.zig.target.intByteSize(target, bits), + limb_bytes, + ), + } }; + }, + }; + } + + /// Describes a set of types which must be declared or completed in the C source file before + /// some string of rendered C code (such as a function), due to said C code using these types. + pub const Dependencies = struct { + /// Key is any Zig type which corresponds to a C `struct`, `union`, or `typedef`. That C + /// type must be declared and complete. + type: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), + + /// Key is a Zig type which is the *payload* of an error union. The C `struct` type + /// corresponding to such an error union must be declared and complete. + /// + /// These are separate from `type` to avoid redundant types for every different error set + /// used with the same payload type---for instance a different C type for every `E!void`. + errunion_type: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), + + /// Like `type`, but the type does not necessarily need to be completed yet: a forward + /// declaration is sufficient. + type_fwd: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), + + /// Like `errunion_type`, but the type does not necessarily need to be completed yet: a + /// forward declaration is sufficient. + errunion_type_fwd: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), + + /// Key is a Zig type; value is a bitmask of alignments. For every bit which is set, an + /// aligned typedef is required. For instance, if bit 3 is set, the C type 'aligned__8_foo' + /// must be declared through `typedef` (but not necessarily completed yet). + aligned_type_fwd: std.AutoArrayHashMapUnmanaged(InternPool.Index, u64), + + /// Key specifies a big-int type whose C `struct` must be declared and complete. + bigint: std.AutoArrayHashMapUnmanaged(BigInt, void), + + pub const empty: Dependencies = .{ + .type = .empty, + .errunion_type = .empty, + .type_fwd = .empty, + .errunion_type_fwd = .empty, + .aligned_type_fwd = .empty, + .bigint = .empty, + }; + + pub fn deinit(deps: *Dependencies, gpa: Allocator) void { + deps.type.deinit(gpa); + deps.errunion_type.deinit(gpa); + deps.type_fwd.deinit(gpa); + deps.errunion_type_fwd.deinit(gpa); + deps.aligned_type_fwd.deinit(gpa); + deps.bigint.deinit(gpa); + } + + pub fn clearRetainingCapacity(deps: *Dependencies) void { + deps.type.clearRetainingCapacity(); + deps.errunion_type.clearRetainingCapacity(); + deps.type_fwd.clearRetainingCapacity(); + deps.errunion_type_fwd.clearRetainingCapacity(); + deps.aligned_type_fwd.clearRetainingCapacity(); + deps.bigint.clearRetainingCapacity(); + } + + pub fn move(deps: *Dependencies) Dependencies { + const moved = deps.*; + deps.* = .empty; + return moved; + } + + fn addType(deps: *Dependencies, gpa: Allocator, ty: Type, allow_incomplete: bool) Allocator.Error!void { + if (allow_incomplete) { + try deps.type_fwd.put(gpa, ty.toIntern(), {}); + } else { + try deps.type.put(gpa, ty.toIntern(), {}); + } + } + }; + + /// Formats the bytes which appear *before* the identifier in a declarator. This includes the + /// type specifier and all "prefix type operators" in the declarator. e.g: + /// * for the declarator "int foo", writes "int " + /// * for the declarator "struct thing *foo", writes "struct thing *" + /// * for the declarator "void *(*foo)(int)", writes "void *(*" + pub fn fmtDeclaratorPrefix(cty: CType, zcu: *const Zcu) Formatter { + return .{ + .cty = cty, + .zcu = zcu, + .kind = .declarator_prefix, + }; + } + /// Formats the bytes which appear *before* the identifier in a declarator. This includes the + /// type specifier and all "prefix type operators" in the declarator. e.g: + /// * for the declarator "int foo", writes "" + /// * for the declarator "struct thing *foo", writes "" + /// * for the declarator "void *(*foo)(int)", writes ")(int)" + pub fn fmtDeclaratorSuffix(cty: CType, zcu: *const Zcu) Formatter { + return .{ + .cty = cty, + .zcu = zcu, + .kind = .declarator_suffix, + }; + } + /// Like `fmtDeclaratorSuffix`, except never emits a `zig_nonstring` annotation. + pub fn fmtDeclaratorSuffixIgnoreNonstring(cty: CType, zcu: *const Zcu) Formatter { + return .{ + .cty = cty, + .zcu = zcu, + .kind = .declarator_suffix_ignore_nonstring, + }; + } + /// Formats a type's full name, e.g. "int", "struct foo *", "void *(uint32_t)". + /// + /// This is almost identical to `fmtDeclaratorPrefix` followed by `fmtDeclaratorSuffix`, but + /// that sequence of calls may emit trailing whitespace where this one does not---for instance, + /// those calls would write the type "void" as "void ". + pub fn fmtTypeName(cty: CType, zcu: *const Zcu) Formatter { + return .{ + .cty = cty, + .zcu = zcu, + .kind = .type_name, + }; + } + + const Formatter = struct { + cty: CType, + zcu: *const Zcu, + kind: enum { type_name, declarator_prefix, declarator_suffix, declarator_suffix_ignore_nonstring }, + + pub fn format(ctx: Formatter, w: *Writer) Writer.Error!void { + switch (ctx.kind) { + .type_name => { + try ctx.cty.writeTypePrefix(w, ctx.zcu); + try ctx.cty.writeTypeSuffix(w, ctx.zcu); + }, + .declarator_prefix => { + try ctx.cty.writeTypePrefix(w, ctx.zcu); + switch (ctx.cty.kind()) { + .specifier => try w.writeByte(' '), // write "int " rather than "int" + .pointer => {}, // we already have something like "foo *" + .postfix_op => {}, // we already have something like "ret_ty " + } + }, + .declarator_suffix => { + try ctx.cty.writeTypeSuffix(w, ctx.zcu); + const nonstring = switch (ctx.cty) { + .array => |arr| arr.nonstring, + .pointer => |ptr| ptr.nonstring, + else => false, + }; + if (nonstring) try w.writeAll(" zig_nonstring"); + }, + .declarator_suffix_ignore_nonstring => { + try ctx.cty.writeTypeSuffix(w, ctx.zcu); + }, + } + } + }; + + fn writeTypePrefix(cty: CType, w: *Writer, zcu: *const Zcu) Writer.Error!void { + switch (cty) { + .void => try w.writeAll("void"), + .bool => try w.writeAll("bool"), + .int => |int| try w.writeAll(@tagName(int)), + .float => |float| try w.writeAll(@tagName(float)), + .@"fn" => |ty| try w.print("{f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }), + .@"enum" => |ty| try w.print("enum__{f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }), + .bitpack => |ty| try w.print("bitpack__{f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }), + .@"struct" => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }), + .union_auto => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }), + .union_extern => |ty| try w.print("union {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }), + .slice => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }), + .opt => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }), + .arr => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }), + .vec => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }), + .errunion => |eu| try w.print("struct errunion_{f}_{d}", .{ + fmtZigType(eu.payload_ty, zcu), + eu.payload_ty.toIntern(), + }), + .aligned => |aligned| try w.print("aligned__{d}_{f}_{d}", .{ + aligned.alignment.toByteUnits().?, + fmtZigType(aligned.ty, zcu), + aligned.ty.toIntern(), + }), + .bigint => |bigint| try w.print("struct int_{d}x{d}", .{ + bigint.limb_size.bits(), + bigint.limbs_len, + }), + + .pointer => |ptr| { + try ptr.elem_ty.writeTypePrefix(w, zcu); + switch (ptr.elem_ty.kind()) { + .pointer, .postfix_op => {}, + .specifier => { + // We want "foo *" or "foo const *" rather than "foo*" or "fooconst *". + try w.writeByte(' '); + }, + } + if (ptr.@"const") try w.writeAll("const "); + if (ptr.@"volatile") try w.writeAll("volatile "); + switch (ptr.elem_ty.kind()) { + .specifier, .pointer => {}, + .postfix_op => { + // Prefix "*" is lower precedence than postfix "(x)" or "[x]" so use parens + // to disambiguate; e.g. "void (*foo)(int)" instead of "void *foo(int)". + try w.writeByte('('); + }, + } + try w.writeByte('*'); + }, + + .array => |array| { + try array.elem_ty.writeTypePrefix(w, zcu); + switch (array.elem_ty.kind()) { + .pointer, .postfix_op => {}, + .specifier => { + // We want e.g. "struct foo [5]" rather than "struct foo[5]". + try w.writeByte(' '); + }, + } + }, + + .function => |function| { + try function.ret_ty.writeTypePrefix(w, zcu); + switch (function.ret_ty.kind()) { + .pointer, .postfix_op => {}, + .specifier => { + // We want e.g. "struct foo (void)" rather than "struct foo(void)". + try w.writeByte(' '); + }, + } + }, + } + } + fn writeTypeSuffix(cty: CType, w: *Writer, zcu: *const Zcu) Writer.Error!void { + switch (cty) { + // simple type specifiers + .void, + .bool, + .int, + .float, + .@"fn", + .@"enum", + .bitpack, + .@"struct", + .union_auto, + .union_extern, + .slice, + .opt, + .arr, + .vec, + .errunion, + .aligned, + .bigint, + => {}, + + .pointer => |ptr| { + // Match opening paren "(" write `writeTypePrefix`. + switch (ptr.elem_ty.kind()) { + .specifier, .pointer => {}, + .postfix_op => try w.writeByte(')'), + } + try ptr.elem_ty.writeTypeSuffix(w, zcu); + }, + + .array => |array| { + try w.print("[{d}]", .{array.len}); + try array.elem_ty.writeTypeSuffix(w, zcu); + }, + + .function => |function| { + if (function.param_tys.len == 0 and !function.varargs) { + try w.writeAll("(void)"); + } else { + try w.writeByte('('); + for (function.param_tys, 0..) |param_ty, param_index| { + if (param_index > 0) try w.writeAll(", "); + try param_ty.writeTypePrefix(w, zcu); + try param_ty.writeTypeSuffix(w, zcu); + } + if (function.varargs) { + if (function.param_tys.len > 0) try w.writeAll(", "); + try w.writeAll("..."); + } + try w.writeByte(')'); + } + try function.ret_ty.writeTypeSuffix(w, zcu); + }, + } + } + + /// Renders Zig types using only bytes allowed in C identifiers in a somewhat-understandable + /// way. The output is *not* guaranteed to be unique. + fn fmtZigType(ty: Type, zcu: *const Zcu) FormatZigType { + return .{ .ty = ty, .zcu = zcu }; + } + const FormatZigType = struct { + ty: Type, + zcu: *const Zcu, + pub fn format(ctx: FormatZigType, w: *Writer) Writer.Error!void { + const ty = ctx.ty; + const zcu = ctx.zcu; + const ip = &zcu.intern_pool; + switch (ty.zigTypeTag(zcu)) { + .frame => unreachable, + .@"anyframe" => unreachable, + + .type => try w.writeAll("type"), + .void => try w.writeAll("void"), + .bool => try w.writeAll("bool"), + .noreturn => try w.writeAll("noreturn"), + .comptime_int => try w.writeAll("comptime_int"), + .comptime_float => try w.writeAll("comptime_float"), + .enum_literal => try w.writeAll("enum_literal"), + .undefined => try w.writeAll("undefined"), + .null => try w.writeAll("null"), + + .int => switch (ty.toIntern()) { + .usize_type => try w.writeAll("usize"), + .isize_type => try w.writeAll("isize"), + .c_char_type => try w.writeAll("c_char"), + .c_short_type => try w.writeAll("c_short"), + .c_ushort_type => try w.writeAll("c_ushort"), + .c_int_type => try w.writeAll("c_int"), + .c_uint_type => try w.writeAll("c_uint"), + .c_long_type => try w.writeAll("c_long"), + .c_ulong_type => try w.writeAll("c_ulong"), + .c_longlong_type => try w.writeAll("c_longlong"), + .c_ulonglong_type => try w.writeAll("c_ulonglong"), + else => { + const info = ty.intInfo(zcu); + switch (info.signedness) { + .unsigned => try w.print("u{d}", .{info.bits}), + .signed => try w.print("i{d}", .{info.bits}), + } + }, + }, + .float => switch (ty.toIntern()) { + .c_longdouble_type => try w.writeAll("c_longdouble"), + .f16_type => try w.writeAll("f16"), + .f32_type => try w.writeAll("f32"), + .f64_type => try w.writeAll("f64"), + .f80_type => try w.writeAll("f80"), + .f128_type => try w.writeAll("f128"), + else => unreachable, + }, + .error_set => switch (ty.toIntern()) { + .anyerror_type => try w.writeAll("anyerror"), + else => try w.print("error_{d}", .{@intFromEnum(ty.toIntern())}), + }, + .optional => try w.print("opt_{f}", .{fmtZigType(ty.optionalChild(zcu), zcu)}), + .error_union => try w.print("errunion_{f}", .{fmtZigType(ty.errorUnionPayload(zcu), zcu)}), + + .pointer => switch (ty.ptrSize(zcu)) { + .one, .many, .c => try w.print("ptr_{f}", .{fmtZigType(ty.childType(zcu), zcu)}), + .slice => try w.print("slice_{f}", .{fmtZigType(ty.childType(zcu), zcu)}), + }, + .@"fn" => { + const func_type = ip.indexToKey(ty.toIntern()).func_type; + try w.writeAll("fn_"); // intentional double underscore to start + for (func_type.param_types.get(ip)) |param_ty_ip| { + const param_ty: Type = .fromInterned(param_ty_ip); + if (param_ty.isGenericPoison()) { + try w.writeAll("_Pgeneric"); + } else { + try w.print("_P{f}", .{fmtZigType(param_ty, zcu)}); + } + } + if (func_type.is_var_args) { + try w.writeAll("_VA"); + } + const ret_ty: Type = .fromInterned(func_type.return_type); + if (ret_ty.isGenericPoison()) { + try w.writeAll("_Rgeneric"); + } else if (ret_ty.zigTypeTag(zcu) == .error_union and ret_ty.errorUnionPayload(zcu).isGenericPoison()) { + try w.writeAll("_Rgeneric_ies"); + } else { + try w.print("_R{f}", .{fmtZigType(ret_ty, zcu)}); + } + }, + + .vector => try w.print("vec_{d}_{f}", .{ + ty.arrayLen(zcu), + fmtZigType(ty.childType(zcu), zcu), + }), + + .array => if (ty.sentinel(zcu)) |s| try w.print("arr_{d}s{d}_{f}", .{ + ty.arrayLen(zcu), + @intFromEnum(s.toIntern()), + fmtZigType(ty.childType(zcu), zcu), + }) else try w.print("arr_{d}_{f}", .{ + ty.arrayLen(zcu), + fmtZigType(ty.childType(zcu), zcu), + }), + + .@"struct" => if (ty.isTuple(zcu)) { + const len = ty.structFieldCount(zcu); + try w.print("tuple_{d}", .{len}); + for (0..len) |field_index| { + const field_ty = ty.fieldType(field_index, zcu); + try w.print("_{f}", .{fmtZigType(field_ty, zcu)}); + } + } else { + const name = ty.containerTypeName(ip).toSlice(ip); + try w.print("{f}", .{@import("../c.zig").fmtIdentUnsolo(name)}); + }, + .@"opaque" => if (ty.toIntern() == .anyopaque_type) { + try w.writeAll("anyopaque"); + } else { + const name = ty.containerTypeName(ip).toSlice(ip); + try w.print("{f}", .{@import("../c.zig").fmtIdentUnsolo(name)}); + }, + .@"union", .@"enum" => { + const name = ty.containerTypeName(ip).toSlice(ip); + try w.print("{f}", .{@import("../c.zig").fmtIdentUnsolo(name)}); + }, + } + } + }; + + /// Returns `true` if the layout of `ty` is known without any type resolution required. This + /// allows some types to be lowered directly where 'typedef' would otherwise be necessary. + fn alwaysHasLayout(ty: Type, ip: *const InternPool) bool { + return switch (ip.indexToKey(ty.toIntern())) { + .int_type, + .ptr_type, + .anyframe_type, + .simple_type, + .opaque_type, + .error_set_type, + .inferred_error_set_type, + => true, + + .struct_type, + .union_type, + .enum_type, + => false, + + .array_type => |arr| alwaysHasLayout(.fromInterned(arr.child), ip), + .vector_type => |vec| alwaysHasLayout(.fromInterned(vec.child), ip), + .opt_type => |child| alwaysHasLayout(.fromInterned(child), ip), + .error_union_type => |eu| alwaysHasLayout(.fromInterned(eu.payload_type), ip), + + .tuple_type => |tuple| for (tuple.types.get(ip)) |field_ty| { + if (!alwaysHasLayout(.fromInterned(field_ty), ip)) break false; + } else true, + + .func_type => |f| for (f.param_types.get(ip)) |param_ty| { + if (!alwaysHasLayout(.fromInterned(param_ty), ip)) break false; + } else alwaysHasLayout(.fromInterned(f.return_type), ip), + + // values, not types + .undef, + .simple_value, + .variable, + .@"extern", + .func, + .int, + .err, + .error_union, + .enum_literal, + .enum_tag, + .float, + .ptr, + .slice, + .opt, + .aggregate, + .un, + .bitpack, + // memoization, not types + .memoized_call, + => unreachable, + }; + } +}; + +const Zcu = @import("../../Zcu.zig"); +const Type = @import("../../Type.zig"); +const Value = @import("../../Value.zig"); +const InternPool = @import("../../InternPool.zig"); + +const std = @import("std"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const Writer = std.Io.Writer; diff --git a/src/codegen/c/type/render_defs.zig b/src/codegen/c/type/render_defs.zig @@ -0,0 +1,710 @@ +/// Renders the `typedef` for an aligned type. +pub fn defineAligned( + ty: Type, + alignment: Alignment, + complete: bool, + deps: *CType.Dependencies, + arena: Allocator, + w: *Writer, + pt: Zcu.PerThread, +) (Allocator.Error || Writer.Error)!void { + const zcu = pt.zcu; + + const name_cty: CType = .{ .aligned = .{ + .ty = ty, + .alignment = alignment, + } }; + + const cty: CType = try .lower(ty, deps, arena, zcu); + + try w.writeAll("typedef "); + if (complete and alignment.compareStrict(.lt, ty.abiAlignment(zcu))) { + try w.print("zig_under_align({d}) ", .{alignment.toByteUnits().?}); + } + try w.print("{f}{f}{f}; /* align({d}) {f} */\n", .{ + cty.fmtDeclaratorPrefix(zcu), + name_cty.fmtTypeName(zcu), + cty.fmtDeclaratorSuffix(zcu), + alignment.toByteUnits().?, + ty.fmt(pt), + }); +} +/// Renders the definition of a big-int `struct`. +pub fn defineBigInt(big: CType.BigInt, w: *Writer, zcu: *const Zcu) Writer.Error!void { + const name_cty: CType = .{ .bigint = .{ + .limb_size = big.limb_size, + .limbs_len = big.limbs_len, + } }; + const limb_cty: CType = .{ .int = big.limb_size.unsigned() }; + const array_cty: CType = .{ .array = .{ + .len = big.limbs_len, + .elem_ty = &limb_cty, + .nonstring = limb_cty.isStringElem(), + } }; + try w.print("{f} {{ {f}limbs{f}; }}; /* {d} bits */\n", .{ + name_cty.fmtTypeName(zcu), + array_cty.fmtDeclaratorPrefix(zcu), + array_cty.fmtDeclaratorSuffix(zcu), + big.limb_size.bits() * @as(u17, big.limbs_len), + }); +} + +/// Renders a forward declaration of the `struct` which represents an error union whose payload type +/// is `payload_ty` (the error set type is unspecified). +pub fn errunionFwdDecl(payload_ty: Type, w: *Writer, zcu: *const Zcu) Writer.Error!void { + const name_cty: CType = .{ .errunion = .{ + .payload_ty = payload_ty, + } }; + try w.print("{f};\n", .{name_cty.fmtTypeName(zcu)}); +} +/// Renders the definition of the `struct` which represents an error union whose payload type is +/// `payload_ty` (the error set type is unspecified). +/// +/// Asserts that the layout of `payload_ty` is resolved. +pub fn errunionDefineComplete( + payload_ty: Type, + deps: *CType.Dependencies, + arena: Allocator, + w: *Writer, + pt: Zcu.PerThread, +) (Allocator.Error || Writer.Error)!void { + const zcu = pt.zcu; + + payload_ty.assertHasLayout(zcu); + + const name_cty: CType = .{ .errunion = .{ + .payload_ty = payload_ty, + } }; + + const error_cty: CType = try .lower(.anyerror, deps, arena, zcu); + + if (payload_ty.hasRuntimeBits(zcu)) { + const payload_cty: CType = try .lower(payload_ty, deps, arena, zcu); + try w.print( + \\{f} {{ /* anyerror!{f} */ + \\ {f}payload{f}; + \\ {f}error{f}; + \\}}; + \\ + , .{ + name_cty.fmtTypeName(zcu), + payload_ty.fmt(pt), + payload_cty.fmtDeclaratorPrefix(zcu), + payload_cty.fmtDeclaratorSuffix(zcu), + error_cty.fmtDeclaratorPrefix(zcu), + error_cty.fmtDeclaratorSuffix(zcu), + }); + } else { + try w.print("{f} {{ {f}error{f}; }}; /* anyerror!{f} */\n", .{ + name_cty.fmtTypeName(zcu), + error_cty.fmtDeclaratorPrefix(zcu), + error_cty.fmtDeclaratorSuffix(zcu), + payload_ty.fmt(pt), + }); + } +} + +/// If the Zig type `ty` lowers to a `struct` or `union` type, renders a forward declaration of that +/// type. Does not write anything for error union types, because their forward declarations are +/// instead rendered by `errunionFwdDecl`. +pub fn fwdDecl(ty: Type, w: *Writer, zcu: *const Zcu) Writer.Error!void { + const name_cty: CType = switch (ty.zigTypeTag(zcu)) { + .@"struct" => switch (ty.containerLayout(zcu)) { + .auto, .@"extern" => .{ .@"struct" = ty }, + .@"packed" => return, + }, + .@"union" => switch (ty.containerLayout(zcu)) { + .auto => .{ .union_auto = ty }, + .@"extern" => .{ .union_extern = ty }, + .@"packed" => return, + }, + .pointer => if (ty.isSlice(zcu)) .{ .slice = ty } else return, + .optional => .{ .opt = ty }, + .array => .{ .arr = ty }, + .vector => .{ .vec = ty }, + else => return, + }; + try w.print("{f};\n", .{name_cty.fmtTypeName(zcu)}); +} + +/// If the Zig type `ty` lowers to a `typedef`, renders a typedef of that type to `void`, because +/// the type's layout is not resolved. This is only necessary for `typedef`s because a `struct` or +/// `union` which is never defined is already an incomplete type, just like `void`. +pub fn defineIncomplete(ty: Type, w: *Writer, pt: Zcu.PerThread) Writer.Error!void { + const zcu = pt.zcu; + const name_cty: CType = switch (ty.zigTypeTag(zcu)) { + .@"fn" => .{ .@"fn" = ty }, + .@"enum" => .{ .@"enum" = ty }, + .@"struct", .@"union" => switch (ty.containerLayout(zcu)) { + .auto, .@"extern" => return, + .@"packed" => .{ .bitpack = ty }, + }, + else => return, + }; + try w.print("typedef void {f}; /* {f} */\n", .{ + name_cty.fmtTypeName(zcu), + ty.fmt(pt), + }); +} + +/// If the Zig type `ty` lowers to a `struct` or `union` type, or to a `typedef`, renders the +/// definition of that type. Does not write anything for error union types, because their +/// definitions are instead rendered by `errunionDefine`. +/// +/// Asserts that the layout of `ty` is resolved. +pub fn defineComplete( + ty: Type, + deps: *CType.Dependencies, + arena: Allocator, + w: *Writer, + pt: Zcu.PerThread, +) (Allocator.Error || Writer.Error)!void { + const zcu = pt.zcu; + + ty.assertHasLayout(zcu); + + switch (ty.zigTypeTag(zcu)) { + .@"fn" => if (!ty.fnHasRuntimeBits(zcu)) { + const name_cty: CType = .{ .@"fn" = ty }; + try w.print("typedef void {f}; /* {f} */\n", .{ + name_cty.fmtTypeName(zcu), + ty.fmt(pt), + }); + } else { + const ip = &zcu.intern_pool; + const func_type = ip.indexToKey(ty.toIntern()).func_type; + + // While incomplete types are usually an acceptable substitute for "void", this is not + // true in function return types, where "void" is the only incomplete type permitted. + const actual_ret_ty: Type = .fromInterned(func_type.return_type); + const effective_ret_ty: Type = switch (actual_ret_ty.classify(zcu)) { + .no_possible_value => .noreturn, + .one_possible_value, .fully_comptime => .void, // no runtime bits + .partially_comptime, .runtime => actual_ret_ty, // yes runtime bits + }; + + const name_cty: CType = .{ .@"fn" = ty }; + const ret_cty: CType = try .lower(effective_ret_ty, deps, arena, zcu); + + try w.print("typedef {f}{f}(", .{ + ret_cty.fmtDeclaratorPrefix(zcu), + name_cty.fmtTypeName(zcu), + }); + var any_params = false; + for (func_type.param_types.get(ip)) |param_ty_ip| { + const param_ty: Type = .fromInterned(param_ty_ip); + if (!param_ty.hasRuntimeBits(zcu)) continue; + if (any_params) try w.writeAll(", "); + any_params = true; + const param_cty: CType = try .lower(param_ty, deps, arena, zcu); + try w.print("{f}", .{param_cty.fmtTypeName(zcu)}); + } + if (func_type.is_var_args) { + if (any_params) try w.writeAll(", "); + try w.writeAll("..."); + } else if (!any_params) { + try w.writeAll("void"); + } + try w.print("){f}; /* {f} */\n", .{ + ret_cty.fmtDeclaratorSuffixIgnoreNonstring(zcu), + ty.fmt(pt), + }); + }, + .@"enum" => { + const name_cty: CType = .{ .@"enum" = ty }; + const cty: CType = try .lower(ty.intTagType(zcu), deps, arena, zcu); + try w.print("typedef {f}{f}{f}; /* {f} */\n", .{ + cty.fmtDeclaratorPrefix(zcu), + name_cty.fmtTypeName(zcu), + cty.fmtDeclaratorSuffix(zcu), + ty.fmt(pt), + }); + }, + .@"struct" => if (ty.isTuple(zcu)) { + try defineTuple(ty, deps, arena, w, pt); + } else switch (ty.containerLayout(zcu)) { + .auto, .@"extern" => try defineStruct(ty, deps, arena, w, pt), + .@"packed" => try defineBitpack(ty, deps, arena, w, pt), + }, + .@"union" => switch (ty.containerLayout(zcu)) { + .auto => try defineUnionAuto(ty, deps, arena, w, pt), + .@"extern" => try defineUnionExtern(ty, deps, arena, w, pt), + .@"packed" => try defineBitpack(ty, deps, arena, w, pt), + }, + .pointer => if (ty.isSlice(zcu)) { + const name_cty: CType = .{ .slice = ty }; + const ptr_cty: CType = try .lower(ty.slicePtrFieldType(zcu), deps, arena, zcu); + try w.print( + \\{f} {{ /* {f} */ + \\ {f}ptr{f}; + \\ size_t len; + \\}}; + \\ + , .{ + name_cty.fmtTypeName(zcu), + ty.fmt(pt), + ptr_cty.fmtDeclaratorPrefix(zcu), + ptr_cty.fmtDeclaratorSuffix(zcu), + }); + // Don't bother with `writeStaticAssertLayout`---there's not really any way we could mess + // slices up, and they're all obviously the same layout. + }, + .optional => switch (CType.classifyOptional(ty, zcu)) { + .error_set, + .ptr_like, + .slice_like, + .npv_payload, + => {}, + + .opv_payload => { + const name_cty: CType = .{ .opt = ty }; + try w.print("{f} {{ bool is_null; }}; /* {f} */\n", .{ + name_cty.fmtTypeName(zcu), + ty.fmt(pt), + }); + try writeStaticAssertLayout(ty, name_cty, w, zcu); + }, + + .@"struct" => { + const name_cty: CType = .{ .opt = ty }; + const payload_cty: CType = try .lower(ty.optionalChild(zcu), deps, arena, zcu); + try w.print( + \\{f} {{ /* {f} */ + \\ {f}payload{f}; + \\ bool is_null; + \\}}; + \\ + , .{ + name_cty.fmtTypeName(zcu), + ty.fmt(pt), + payload_cty.fmtDeclaratorPrefix(zcu), + payload_cty.fmtDeclaratorSuffix(zcu), + }); + try writeStaticAssertLayout(ty, name_cty, w, zcu); + }, + }, + .array => if (ty.hasRuntimeBits(zcu)) { + const name_cty: CType = .{ .arr = ty }; + const elem_cty: CType = try .lower(ty.childType(zcu), deps, arena, zcu); + const array_cty: CType = .{ .array = .{ + .len = ty.arrayLenIncludingSentinel(zcu), + .elem_ty = &elem_cty, + .nonstring = nonstring: { + if (!elem_cty.isStringElem()) break :nonstring false; + const s = ty.sentinel(zcu) orelse break :nonstring true; + break :nonstring Value.compareHetero(s, .neq, .zero_comptime_int, zcu); + }, + } }; + try w.print("{f} {{ {f}array{f}; }}; /* {f} */\n", .{ + name_cty.fmtTypeName(zcu), + array_cty.fmtDeclaratorPrefix(zcu), + array_cty.fmtDeclaratorSuffix(zcu), + ty.fmt(pt), + }); + try writeStaticAssertLayout(ty, name_cty, w, zcu); + }, + .vector => if (ty.hasRuntimeBits(zcu)) { + const name_cty: CType = .{ .vec = ty }; + const elem_cty: CType = try .lower(ty.childType(zcu), deps, arena, zcu); + const array_cty: CType = .{ .array = .{ + .len = ty.arrayLenIncludingSentinel(zcu), + .elem_ty = &elem_cty, + .nonstring = elem_cty.isStringElem(), + } }; + try w.print("{f} {{ {f}array{f}; }}; /* {f} */\n", .{ + name_cty.fmtTypeName(zcu), + array_cty.fmtDeclaratorPrefix(zcu), + array_cty.fmtDeclaratorSuffix(zcu), + ty.fmt(pt), + }); + try writeStaticAssertLayout(ty, name_cty, w, zcu); + }, + else => {}, + } +} +fn defineBitpack( + ty: Type, + deps: *CType.Dependencies, + arena: Allocator, + w: *Writer, + pt: Zcu.PerThread, +) (Allocator.Error || Writer.Error)!void { + const zcu = pt.zcu; + const name_cty: CType = .{ .bitpack = ty }; + const cty: CType = try .lower(ty.bitpackBackingInt(zcu), deps, arena, zcu); + try w.print("typedef {f}{f}{f}; /* {f} */\n", .{ + cty.fmtDeclaratorPrefix(zcu), + name_cty.fmtTypeName(zcu), + cty.fmtDeclaratorSuffix(zcu), + ty.fmt(pt), + }); +} +fn defineTuple( + ty: Type, + deps: *CType.Dependencies, + arena: Allocator, + w: *Writer, + pt: Zcu.PerThread, +) (Allocator.Error || Writer.Error)!void { + const zcu = pt.zcu; + if (!ty.hasRuntimeBits(zcu)) return; + const ip = &zcu.intern_pool; + const tuple = ip.indexToKey(ty.toIntern()).tuple_type; + + // Fields cannot be underaligned, because tuple fields cannot have specified alignments. + // However, overaligned fields are possible thanks to intermediate zero-bit fields. + + const tuple_align = ty.abiAlignment(zcu); + + // If the alignment of other fields would not give the tuple sufficient alignment, we + // need to align the first field (which does not affect its offset, because 0 is always + // well-aligned) to indirectly specify the tuple alignment. + const overalign: bool = for (tuple.types.get(ip)) |field_ty_ip| { + const field_ty: Type = .fromInterned(field_ty_ip); + if (!field_ty.hasRuntimeBits(zcu)) continue; + const natural_align = field_ty.defaultStructFieldAlignment(.auto, zcu); + if (natural_align.compareStrict(.gte, tuple_align)) break false; + } else true; + + const name_cty: CType = .{ .@"struct" = ty }; + try w.print("{f} {{ /* {f} */\n", .{ + name_cty.fmtTypeName(zcu), + ty.fmt(pt), + }); + var zig_offset: u64 = 0; + var c_offset: u64 = 0; + for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty_ip, field_val_ip, field_index| { + if (field_val_ip != .none) continue; // `comptime` field + const field_ty: Type = .fromInterned(field_ty_ip); + const field_align = field_ty.abiAlignment(zcu); + zig_offset = field_align.forward(zig_offset); + if (!field_ty.hasRuntimeBits(zcu)) continue; + c_offset = field_align.forward(c_offset); + try w.writeByte(' '); + if (zig_offset == 0 and overalign) { + // This is the first field; specify its alignment to align the tuple. + try writeFieldAlign(field_ty, tuple_align, w, zcu); + } else if (zig_offset > c_offset) { + // This field needs to be overaligned compared to what its offset would otherwise be. + const need_align: Alignment = .minStrict( + tuple_align, // don't make the struct more aligned than it should be + .fromLog2Units(@ctz(zig_offset)), + ); + try writeFieldAlign(field_ty, need_align, w, zcu); + c_offset = need_align.forward(c_offset); + } + const field_cty: CType = try .lower(field_ty, deps, arena, zcu); + try w.print("{f}f{d}{f};\n", .{ + field_cty.fmtDeclaratorPrefix(zcu), + field_index, + field_cty.fmtDeclaratorSuffix(zcu), + }); + const field_size = field_ty.abiSize(zcu); + zig_offset += field_size; + c_offset += field_size; + } + try w.writeAll("};\n"); + + try writeStaticAssertLayout(ty, name_cty, w, zcu); +} +fn defineStruct( + ty: Type, + deps: *CType.Dependencies, + arena: Allocator, + w: *Writer, + pt: Zcu.PerThread, +) (Allocator.Error || Writer.Error)!void { + const zcu = pt.zcu; + if (!ty.hasRuntimeBits(zcu)) return; + const ip = &zcu.intern_pool; + + const struct_type = ip.loadStructType(ty.toIntern()); + + // If there are any underaligned fields, we need to byte-pack the struct. + const pack: bool = pack: { + var it = struct_type.iterateRuntimeOrder(ip); + var offset: u64 = 0; + while (it.next()) |field_index| { + const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBits(zcu)) continue; + const natural_align = field_ty.defaultStructFieldAlignment(struct_type.layout, zcu); + const natural_offset = natural_align.forward(offset); + const actual_offset = struct_type.field_offsets.get(ip)[field_index]; + if (actual_offset < natural_offset) break :pack true; + // Also pack if any field is more aligned than the struct should be. + if (natural_align.compareStrict(.gt, struct_type.alignment)) break :pack true; + offset = actual_offset + field_ty.abiSize(zcu); + } + break :pack false; + }; + + // If the alignment of other fields would not give the struct sufficient alignment, we + // need to align the first field (which does not affect its offset, because 0 is always + // well-aligned) to indirectly specify the struct alignment. + const overalign: bool = switch (pack) { + true => struct_type.alignment.compareStrict(.gt, .@"1"), + false => overalign: { + var it = struct_type.iterateRuntimeOrder(ip); + while (it.next()) |field_index| { + const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBits(zcu)) continue; + const natural_align = field_ty.defaultStructFieldAlignment(struct_type.layout, zcu); + if (natural_align.compareStrict(.gte, struct_type.alignment)) break :overalign false; + } + break :overalign true; + }, + }; + + if (pack) try w.writeAll("zig_packed("); + const name_cty: CType = .{ .@"struct" = ty }; + try w.print("{f} {{ /* {f} */\n", .{ + name_cty.fmtTypeName(zcu), + ty.fmt(pt), + }); + var it = struct_type.iterateRuntimeOrder(ip); + var offset: u64 = 0; + while (it.next()) |field_index| { + const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBits(zcu)) continue; + const natural_align = field_ty.defaultStructFieldAlignment(struct_type.layout, zcu); + const natural_offset = switch (pack) { + true => offset, + false => natural_align.forward(offset), + }; + const actual_offset = struct_type.field_offsets.get(ip)[field_index]; + try w.writeByte(' '); + if (actual_offset == 0 and overalign) { + // This is the first field; specify its alignment to align the struct. + try writeFieldAlign(field_ty, struct_type.alignment, w, zcu); + } else if (actual_offset > natural_offset) { + // This field needs to be underaligned or overaligned compared to what its + // offset would otherwise be. + const need_align: Alignment = .minStrict( + struct_type.alignment, // don't make the struct more aligned than it should be + .fromLog2Units(@ctz(actual_offset)), + ); + try writeFieldAlign(field_ty, need_align, w, zcu); + } + const field_cty: CType = try .lower(field_ty, deps, arena, zcu); + const field_name = struct_type.field_names.get(ip)[field_index].toSlice(ip); + try w.print("{f}{f}{f};\n", .{ + field_cty.fmtDeclaratorPrefix(zcu), + fmtIdentSolo(field_name), + field_cty.fmtDeclaratorSuffix(zcu), + }); + offset = actual_offset + field_ty.abiSize(zcu); + } + assert(struct_type.alignment.forward(offset) == struct_type.size); + try w.writeByte('}'); + if (pack) try w.writeByte(')'); + try w.writeAll(";\n"); + + try writeStaticAssertLayout(ty, name_cty, w, zcu); +} +fn defineUnionAuto( + ty: Type, + deps: *CType.Dependencies, + arena: Allocator, + w: *Writer, + pt: Zcu.PerThread, +) (Allocator.Error || Writer.Error)!void { + const zcu = pt.zcu; + if (!ty.hasRuntimeBits(zcu)) return; + const ip = &zcu.intern_pool; + + const union_type = ip.loadUnionType(ty.toIntern()); + const enum_tag_ty: Type = .fromInterned(union_type.enum_tag_type); + + const layout = Type.getUnionLayout(union_type, zcu); + + // If there are any underaligned fields, we need to byte-pack the union. + const pack: bool = for (union_type.field_types.get(ip)) |field_ty_ip| { + const field_ty: Type = .fromInterned(field_ty_ip); + if (!field_ty.hasRuntimeBits(zcu)) continue; + const natural_align = field_ty.abiAlignment(zcu); + if (natural_align.compareStrict(.gt, union_type.alignment)) break true; + // The tag will immediately follow the payload. This layout may put the tag in what would + // otherwise be padding on the payload union, because if the most-aligned union field is not + // the largest one, a larger field may make the payload "underaligned" overall. As such, we + // need to check whether this field is okay with the payload size, and if not then we must + // byte-pack. + if (!natural_align.check(layout.payload_size)) break true; + } else false; + + // If the alignment of other fields would not give the union sufficient alignment, we + // need to align the first field (which does not affect its offset, because 0 is always + // well-aligned) to indirectly specify the union alignment. + const overalign: bool = switch (pack) { + true => union_type.alignment.compareStrict(.gt, .@"1"), + false => for (union_type.field_types.get(ip)) |field_ty_ip| { + const field_ty: Type = .fromInterned(field_ty_ip); + if (!field_ty.hasRuntimeBits(zcu)) continue; + const natural_align = field_ty.abiAlignment(zcu); + if (natural_align.compareStrict(.gte, union_type.alignment)) break false; + } else overalign: { + if (union_type.has_runtime_tag) { + const tag_align = enum_tag_ty.abiAlignment(zcu); + if (tag_align.compareStrict(.gte, union_type.alignment)) break :overalign false; + } + break :overalign true; + }, + }; + + const payload_has_bits = !union_type.has_runtime_tag or union_type.size > enum_tag_ty.abiSize(zcu); + + const name_cty: CType = .{ .union_auto = ty }; + try w.print("{f} {{ /* {f} */\n", .{ + name_cty.fmtTypeName(zcu), + ty.fmt(pt), + }); + if (payload_has_bits) { + try w.writeByte(' '); + if (overalign) { + // Specify the alignment of `union { ... } payload;` to align the union's `struct`. + try w.print("zig_align({d}) ", .{union_type.alignment.toByteUnits().?}); + } + if (pack) try w.writeAll("zig_packed("); + try w.writeAll("union {\n"); + for (0..enum_tag_ty.enumFieldCount(zcu)) |field_index| { + const field_ty = ty.fieldType(field_index, zcu); + if (!field_ty.hasRuntimeBits(zcu)) continue; + const field_name = enum_tag_ty.enumFieldName(field_index, zcu).toSlice(ip); + const field_cty: CType = try .lower(field_ty, deps, arena, zcu); + try w.print(" {f}{f}{f};\n", .{ + field_cty.fmtDeclaratorPrefix(zcu), + fmtIdentSolo(field_name), + field_cty.fmtDeclaratorSuffix(zcu), + }); + } + try w.writeAll(" }"); + if (pack) try w.writeByte(')'); + try w.writeAll(" payload;\n"); + } + if (union_type.has_runtime_tag) { + const tag_cty: CType = try .lower(enum_tag_ty, deps, arena, zcu); + try w.print(" {f}tag{f};\n", .{ + tag_cty.fmtDeclaratorPrefix(zcu), + tag_cty.fmtDeclaratorSuffix(zcu), + }); + } + try w.writeAll("};\n"); + + try writeStaticAssertLayout(ty, name_cty, w, zcu); +} +fn defineUnionExtern( + ty: Type, + deps: *CType.Dependencies, + arena: Allocator, + w: *Writer, + pt: Zcu.PerThread, +) (Allocator.Error || Writer.Error)!void { + const zcu = pt.zcu; + if (!ty.hasRuntimeBits(zcu)) return; + const ip = &zcu.intern_pool; + + const union_type = ip.loadUnionType(ty.toIntern()); + assert(!union_type.has_runtime_tag); + const enum_tag_ty: Type = .fromInterned(union_type.enum_tag_type); + + // If there are any underaligned fields, we need to byte-pack the union. + const pack: bool = for (union_type.field_types.get(ip)) |field_ty_ip| { + const field_ty: Type = .fromInterned(field_ty_ip); + if (!field_ty.hasRuntimeBits(zcu)) continue; + const natural_align = field_ty.abiAlignment(zcu); + if (natural_align.compareStrict(.gt, union_type.alignment)) break true; + } else false; + + // If the alignment of other fields would not give the union sufficient alignment, we + // need to align the first field (which does not affect its offset, because 0 is always + // well-aligned) to indirectly specify the union alignment. + const overalign: bool = switch (pack) { + true => union_type.alignment.compareStrict(.gt, .@"1"), + false => for (union_type.field_types.get(ip)) |field_ty_ip| { + const field_ty: Type = .fromInterned(field_ty_ip); + if (!field_ty.hasRuntimeBits(zcu)) continue; + const natural_align = field_ty.abiAlignment(zcu); + if (natural_align.compareStrict(.gte, union_type.alignment)) break false; + } else overalign: { + if (union_type.has_runtime_tag) { + const tag_align = enum_tag_ty.abiAlignment(zcu); + if (tag_align.compareStrict(.gte, union_type.alignment)) break :overalign false; + } + break :overalign true; + }, + }; + + if (pack) try w.writeAll("zig_packed("); + + const name_cty: CType = .{ .union_extern = ty }; + try w.print("{f} {{ /* {f} */\n", .{ + name_cty.fmtTypeName(zcu), + ty.fmt(pt), + }); + + for (0..enum_tag_ty.enumFieldCount(zcu)) |field_index| { + const field_ty = ty.fieldType(field_index, zcu); + if (!field_ty.hasRuntimeBits(zcu)) continue; + const field_name = enum_tag_ty.enumFieldName(field_index, zcu).toSlice(ip); + const field_cty: CType = try .lower(field_ty, deps, arena, zcu); + try w.writeByte(' '); + if (overalign and field_index == 0) { + // This is the first field; specify its alignment to align the union. + try writeFieldAlign(field_ty, union_type.alignment, w, zcu); + } + try w.print("{f}{f}{f};\n", .{ + field_cty.fmtDeclaratorPrefix(zcu), + fmtIdentSolo(field_name), + field_cty.fmtDeclaratorSuffix(zcu), + }); + } + try w.writeByte('}'); + if (pack) try w.writeByte(')'); + try w.writeAll(";\n"); + + try writeStaticAssertLayout(ty, name_cty, w, zcu); +} + +/// Writes an annotation which, placed before a struct/union field declaration with field type `ty`, +/// will specify that field as having the given alignment. +fn writeFieldAlign( + ty: Type, + alignment: Alignment, + w: *Writer, + zcu: *const Zcu, +) Writer.Error!void { + if (alignment.compareStrict(.lt, ty.abiAlignment(zcu))) { + try w.print("zig_under_align({d}) ", .{alignment.toByteUnits().?}); + } else { + try w.print("zig_align({d}) ", .{alignment.toByteUnits().?}); + } +} + +/// Emits static assertions that the size and alignment of `cty` match those of the Zig type `ty`. +fn writeStaticAssertLayout( + ty: Type, + cty: CType, + w: *Writer, + zcu: *const Zcu, +) Writer.Error!void { + try w.print( + \\zig_static_assert(sizeof ({f}) == {d}, "incorrect size"); + \\zig_static_assert(_Alignof ({f}) == {d}, "incorrect alignment"); + \\ + , .{ + cty.fmtTypeName(zcu), ty.abiSize(zcu), + cty.fmtTypeName(zcu), ty.abiAlignment(zcu).toByteUnits().?, + }); +} + +const std = @import("std"); +const assert = std.debug.assert; +const Writer = std.Io.Writer; +const Allocator = std.mem.Allocator; + +const Zcu = @import("../../../Zcu.zig"); +const Type = @import("../../../Type.zig"); +const Value = @import("../../../Value.zig"); +const CType = @import("../type.zig").CType; +const Alignment = @import("../../../InternPool.zig").Alignment; + +const fmtIdentSolo = @import("../../c.zig").fmtIdentSolo; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig @@ -520,6 +520,21 @@ pub const Object = struct { gpa: Allocator, builder: Builder, + /// This pool contains only types (and not `@as(type, undefined)`). It has two purposes: + /// + /// * Lazily tracking ABI alignment of types, so that `@"align"` attributes can be set to a + /// type's ABI alignment before that type is fully resolved. Each type in the pool has a + /// corresponding entry in `lazy_abi_aligns`. + /// + /// * If `!Object.builder.strip`, lazily tracking debug information types, so that debug + /// information can handle indirect self-reference (and so that debug information works + /// correctly across incremental updates). Each type has a corresponding entry in + /// `debug_types`, provided that `Object.builder.strip` is `false`. + type_pool: link.ConstPool, + + /// Keyed on `link.ConstPool.Index`. + lazy_abi_aligns: std.ArrayList(Builder.Alignment.Lazy), + debug_compile_unit: Builder.Metadata.Optional, debug_enums_fwd_ref: Builder.Metadata.Optional, @@ -529,9 +544,13 @@ pub const Object = struct { debug_globals: std.ArrayList(Builder.Metadata), debug_file_map: std.AutoHashMapUnmanaged(Zcu.File.Index, Builder.Metadata), - debug_type_map: std.AutoHashMapUnmanaged(InternPool.Index, Builder.Metadata), - debug_unresolved_namespace_scopes: std.AutoArrayHashMapUnmanaged(InternPool.NamespaceIndex, Builder.Metadata), + /// Keyed on `link.ConstPool.Index`. + debug_types: std.ArrayList(Builder.Metadata), + /// Initially `.none`, set if the type `anyerror` is lowered to a debug type. The type will not + /// actually be created until `emit`, which must resolve this reference with an appropriate enum + /// type from the global error set. + debug_anyerror_fwd_ref: Builder.Metadata.Optional, target: *const std.Target, /// Ideally we would use `llvm_module.getNamedFunction` to go from *Decl to LLVM function, @@ -654,35 +673,38 @@ pub const Object = struct { obj.* = .{ .gpa = gpa, .builder = builder, + .type_pool = .empty, + .lazy_abi_aligns = .empty, .debug_compile_unit = debug_compile_unit, .debug_enums_fwd_ref = debug_enums_fwd_ref, .debug_globals_fwd_ref = debug_globals_fwd_ref, - .debug_enums = .{}, - .debug_globals = .{}, - .debug_file_map = .{}, - .debug_type_map = .{}, - .debug_unresolved_namespace_scopes = .{}, + .debug_enums = .empty, + .debug_globals = .empty, + .debug_file_map = .empty, + .debug_types = .empty, + .debug_anyerror_fwd_ref = .none, .target = target, - .nav_map = .{}, - .uav_map = .{}, - .enum_tag_name_map = .{}, - .named_enum_map = .{}, - .type_map = .{}, + .nav_map = .empty, + .uav_map = .empty, + .enum_tag_name_map = .empty, + .named_enum_map = .empty, + .type_map = .empty, .error_name_table = .none, .null_opt_usize = .no_init, - .struct_field_map = .{}, - .used = .{}, + .struct_field_map = .empty, + .used = .empty, }; return obj; } pub fn deinit(self: *Object) void { const gpa = self.gpa; + self.type_pool.deinit(gpa); + self.lazy_abi_aligns.deinit(gpa); self.debug_enums.deinit(gpa); self.debug_globals.deinit(gpa); self.debug_file_map.deinit(gpa); - self.debug_type_map.deinit(gpa); - self.debug_unresolved_namespace_scopes.deinit(gpa); + self.debug_types.deinit(gpa); self.nav_map.deinit(gpa); self.uav_map.deinit(gpa); self.enum_tag_name_map.deinit(gpa); @@ -824,19 +846,13 @@ pub const Object = struct { } if (!o.builder.strip) { - { - var i: usize = 0; - while (i < o.debug_unresolved_namespace_scopes.count()) : (i += 1) { - const namespace_index = o.debug_unresolved_namespace_scopes.keys()[i]; - const fwd_ref = o.debug_unresolved_namespace_scopes.values()[i]; - - const namespace = zcu.namespacePtr(namespace_index); - const debug_type = try o.lowerDebugType(pt, Type.fromInterned(namespace.owner_type)); - - o.builder.resolveDebugForwardReference(fwd_ref, debug_type); - } + if (o.debug_anyerror_fwd_ref.unwrap()) |fwd_ref| { + const debug_anyerror_type = try o.lowerDebugAnyerrorType(pt); + o.builder.resolveDebugForwardReference(fwd_ref, debug_anyerror_type); } + try o.flushTypePool(pt); + o.builder.resolveDebugForwardReference( o.debug_enums_fwd_ref.unwrap().?, try o.builder.metadataTuple(o.debug_enums.items), @@ -1395,10 +1411,10 @@ pub const Object = struct { if (ptr_info.flags.is_const) { try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder); } - const elem_align = (if (ptr_info.flags.alignment != .none) - @as(InternPool.Alignment, ptr_info.flags.alignment) - else - Type.fromInterned(ptr_info.child).abiAlignment(zcu).max(.@"1")).toLlvm(); + const elem_align: Builder.Alignment.Lazy = switch (ptr_info.flags.alignment) { + else => |a| .wrap(a.toLlvm()), + .none => try o.lazyAbiAlignment(pt, .fromInterned(ptr_info.child)), + }; try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = elem_align }, &o.builder); const ptr_param = wip.arg(llvm_arg_i); llvm_arg_i += 1; @@ -1472,7 +1488,7 @@ pub const Object = struct { const line_number = zcu.navSrcLine(func.owner_nav) + 1; const is_internal_linkage = ip.indexToKey(nav.status.fully_resolved.val) != .@"extern"; - const debug_decl_type = try o.lowerDebugType(pt, fn_ty); + const debug_decl_type = try o.getDebugType(pt, fn_ty); const subprogram = try o.builder.debugSubprogram( file, @@ -1522,7 +1538,7 @@ pub const Object = struct { break :f .{ .counters_variable = counters_variable, - .pcs = .{}, + .pcs = .empty, }; }; @@ -1538,10 +1554,10 @@ pub const Object = struct { .args = args.items, .arg_index = 0, .arg_inline_index = 0, - .func_inst_table = .{}, - .blocks = .{}, - .loops = .{}, - .switch_dispatch_info = .{}, + .func_inst_table = .empty, + .blocks = .empty, + .loops = .empty, + .switch_dispatch_info = .empty, .sync_scope = if (owner_mod.single_threaded) .singlethread else .system, .file = file, .scope = subprogram, @@ -1599,6 +1615,7 @@ pub const Object = struct { } try fg.wip.finish(); + try o.flushTypePool(pt); } pub fn updateNav(self: *Object, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { @@ -1615,6 +1632,11 @@ pub const Object = struct { }, else => |e| return e, }; + try self.flushTypePool(pt); + } + + fn flushTypePool(o: *Object, pt: Zcu.PerThread) Allocator.Error!void { + try o.type_pool.flushPending(pt, .{ .llvm = o }); } pub fn updateExports( @@ -1810,6 +1832,84 @@ pub const Object = struct { } } + pub fn updateContainerType(o: *Object, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) Allocator.Error!void { + try o.type_pool.updateContainerType(pt, .{ .llvm = o }, ty, success); + } + + /// Should only be called by the `link.ConstPool` implementation. + /// + /// `val` is always a type because `o.type_pool` only contains types. + pub fn addConst(o: *Object, pt: Zcu.PerThread, index: link.ConstPool.Index, val: InternPool.Index) Allocator.Error!void { + const zcu = pt.zcu; + const gpa = zcu.comp.gpa; + assert(zcu.intern_pool.typeOf(val) == .type_type); + + { + assert(@intFromEnum(index) == o.lazy_abi_aligns.items.len); + try o.lazy_abi_aligns.ensureUnusedCapacity(gpa, 1); + const fwd_ref = try o.builder.alignmentForwardReference(); + o.lazy_abi_aligns.appendAssumeCapacity(fwd_ref); + } + + if (!o.builder.strip) { + assert(@intFromEnum(index) == o.debug_types.items.len); + try o.debug_types.ensureUnusedCapacity(gpa, 1); + const fwd_ref = try o.builder.debugForwardReference(); + o.debug_types.appendAssumeCapacity(fwd_ref); + if (val == .anyerror_type) { + assert(o.debug_anyerror_fwd_ref.is_none); + o.debug_anyerror_fwd_ref = fwd_ref.toOptional(); + } + } + } + /// Should only be called by the `link.ConstPool` implementation. + /// + /// `val` is always a type because `o.type_pool` only contains types. + pub fn updateConstIncomplete(o: *Object, pt: Zcu.PerThread, index: link.ConstPool.Index, val: InternPool.Index) Allocator.Error!void { + const zcu = pt.zcu; + assert(zcu.intern_pool.typeOf(val) == .type_type); + + const ty: Type = .fromInterned(val); + + { + const fwd_ref = o.lazy_abi_aligns.items[@intFromEnum(index)]; + o.builder.resolveAlignmentForwardReference(fwd_ref, .fromByteUnits(1)); + } + + if (!o.builder.strip) { + assert(val != .anyerror_type); + const fwd_ref = o.debug_types.items[@intFromEnum(index)]; + const name_str = try o.builder.metadataStringFmt("{f}", .{ty.fmt(pt)}); + const debug_incomplete_type = try o.builder.debugSignedType(name_str, 0); + o.builder.resolveDebugForwardReference(fwd_ref, debug_incomplete_type); + } + } + /// Should only be called by the `link.ConstPool` implementation. + /// + /// `val` is always a type because `o.type_pool` only contains types. + pub fn updateConst(o: *Object, pt: Zcu.PerThread, index: link.ConstPool.Index, val: InternPool.Index) Allocator.Error!void { + const zcu = pt.zcu; + assert(zcu.intern_pool.typeOf(val) == .type_type); + + const ty: Type = .fromInterned(val); + + { + const fwd_ref = o.lazy_abi_aligns.items[@intFromEnum(index)]; + o.builder.resolveAlignmentForwardReference(fwd_ref, ty.abiAlignment(zcu).toLlvm()); + } + + if (!o.builder.strip) { + const fwd_ref = o.debug_types.items[@intFromEnum(index)]; + if (val == .anyerror_type) { + // Don't lower this now; it will be populated in `emit` instead. + assert(o.debug_anyerror_fwd_ref == fwd_ref.toOptional()); + } else { + const debug_type = try o.lowerDebugType(pt, ty, fwd_ref); + o.builder.resolveDebugForwardReference(fwd_ref, debug_type); + } + } + } + fn getDebugFile(o: *Object, pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.Error!Builder.Metadata { const gpa = o.gpa; const gop = try o.debug_file_map.getOrPut(gpa, file_index); @@ -1826,10 +1926,19 @@ pub const Object = struct { return gop.value_ptr.*; } - pub fn lowerDebugType( + fn getDebugType(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error!Builder.Metadata { + assert(!o.builder.strip); + const index = try o.type_pool.get(pt, .{ .llvm = o }, ty.toIntern()); + return o.debug_types.items[@intFromEnum(index)]; + } + + /// In codegen logic, instead of calling this directly, use `getDebugType` to get a forward + /// reference which will be populated only when all necessary type resolution is complete. + fn lowerDebugType( o: *Object, pt: Zcu.PerThread, ty: Type, + ty_fwd_ref: Builder.Metadata, ) Allocator.Error!Builder.Metadata { assert(!o.builder.strip); @@ -1838,312 +1947,137 @@ pub const Object = struct { const zcu = pt.zcu; const ip = &zcu.intern_pool; - if (o.debug_type_map.get(ty.toIntern())) |debug_type| return debug_type; + const name = try o.builder.metadataStringFmt("{f}", .{ty.fmt(pt)}); + + // lldb cannot handle non-byte-sized types, so in the logic below, bit sizes are padded up. + // For instance, `bool` is considered to be 8 bits, and `u60` is considered to be 64 bits. + + // I tried using variants (DW_TAG_variant_part + DW_TAG_variant) to encode error unions, + // tagged unions, etc; this would have told debuggers which field was active, which could + // improve UX significantly. GDB handles this perfectly fine, but unfortunately, LLDB has no + // handling for variants at all, and will never print fields in them, so I opted not to use + // them for now. switch (ty.zigTypeTag(zcu)) { .void, .noreturn, - => { - const debug_void_type = try o.builder.debugSignedType( - try o.builder.metadataString("void"), - 0, - ); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_void_type); - return debug_void_type; - }, + .comptime_int, + .comptime_float, + .type, + .undefined, + .null, + .enum_literal, + => return o.builder.debugSignedType(name, 0), + + .float => return o.builder.debugFloatType(name, ty.floatBits(target)), + + .bool => return o.builder.debugBoolType(name, 8), + .int => { const info = ty.intInfo(zcu); - assert(info.bits != 0); - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - const builder_name = try o.builder.metadataString(name); - const debug_bits = ty.abiSize(zcu) * 8; // lldb cannot handle non-byte sized types - const debug_int_type = switch (info.signedness) { - .signed => try o.builder.debugSignedType(builder_name, debug_bits), - .unsigned => try o.builder.debugUnsignedType(builder_name, debug_bits), + const bits = ty.abiSize(zcu) * 8; + return switch (info.signedness) { + .signed => try o.builder.debugSignedType(name, bits), + .unsigned => try o.builder.debugUnsignedType(name, bits), }; - try o.debug_type_map.put(gpa, ty.toIntern(), debug_int_type); - return debug_int_type; }, - .@"enum" => { - if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) { - const debug_enum_type = try o.makeEmptyNamespaceDebugType(pt, ty); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_enum_type); - return debug_enum_type; - } - - const enum_type = ip.loadEnumType(ty.toIntern()); - const enumerators = try gpa.alloc(Builder.Metadata, enum_type.names.len); - defer gpa.free(enumerators); - - const int_ty = Type.fromInterned(enum_type.tag_ty); - const int_info = ty.intInfo(zcu); - assert(int_info.bits != 0); - - for (enum_type.names.get(ip), 0..) |field_name_ip, i| { - var bigint_space: Value.BigIntSpace = undefined; - const bigint = if (enum_type.values.len != 0) - Value.fromInterned(enum_type.values.get(ip)[i]).toBigInt(&bigint_space, zcu) - else - std.math.big.int.Mutable.init(&bigint_space.limbs, i).toConst(); - - enumerators[i] = try o.builder.debugEnumerator( - try o.builder.metadataString(field_name_ip.toSlice(ip)), - int_info.signedness == .unsigned, - int_info.bits, - bigint, - ); - } - - const file = try o.getDebugFile(pt, ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip)); - const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| - try o.namespaceToDebugScope(pt, parent_namespace) - else - file; - - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - - const debug_enum_type = try o.builder.debugEnumerationType( - try o.builder.metadataString(name), - file, - scope, - ty.typeDeclSrcLine(zcu).? + 1, // Line - try o.lowerDebugType(pt, int_ty), - ty.abiSize(zcu) * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.metadataTuple(enumerators), - ); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_enum_type); - try o.debug_enums.append(gpa, debug_enum_type); - return debug_enum_type; - }, - .float => { - const bits = ty.floatBits(target); - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - const debug_float_type = try o.builder.debugFloatType( - try o.builder.metadataString(name), - bits, - ); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_float_type); - return debug_float_type; - }, - .bool => { - const debug_bool_type = try o.builder.debugBoolType( - try o.builder.metadataString("bool"), - 8, // lldb cannot handle non-byte sized types - ); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_bool_type); - return debug_bool_type; - }, .pointer => { - // Normalize everything that the debug info does not represent. - const ptr_info = ty.ptrInfo(zcu); - - if (ptr_info.sentinel != .none or - ptr_info.flags.address_space != .generic or - ptr_info.packed_offset.bit_offset != 0 or - ptr_info.packed_offset.host_size != 0 or - ptr_info.flags.vector_index != .none or - ptr_info.flags.is_allowzero or - ptr_info.flags.is_const or - ptr_info.flags.is_volatile or - ptr_info.flags.size == .many or ptr_info.flags.size == .c or - !Type.fromInterned(ptr_info.child).hasRuntimeBitsIgnoreComptime(zcu)) - { - const bland_ptr_ty = try pt.ptrType(.{ - .child = if (!Type.fromInterned(ptr_info.child).hasRuntimeBitsIgnoreComptime(zcu)) - .anyopaque_type - else - ptr_info.child, - .flags = .{ - .alignment = ptr_info.flags.alignment, - .size = switch (ptr_info.flags.size) { - .many, .c, .one => .one, - .slice => .slice, - }, - }, - }); - const debug_ptr_type = try o.lowerDebugType(pt, bland_ptr_ty); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_ptr_type); - return debug_ptr_type; - } - - const debug_fwd_ref = try o.builder.debugForwardReference(); - - // Set as forward reference while the type is lowered in case it references itself - try o.debug_type_map.put(gpa, ty.toIntern(), debug_fwd_ref); + const ptr_size = Type.ptrAbiSize(zcu.getTarget()); + const ptr_align = Type.ptrAbiAlignment(zcu.getTarget()); if (ty.isSlice(zcu)) { - const ptr_ty = ty.slicePtrFieldType(zcu); - const len_ty = Type.usize; - - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - const line = 0; - - const ptr_size = ptr_ty.abiSize(zcu); - const ptr_align = ptr_ty.abiAlignment(zcu); - const len_size = len_ty.abiSize(zcu); - const len_align = len_ty.abiAlignment(zcu); - - const len_offset = len_align.forward(ptr_size); - const debug_ptr_type = try o.builder.debugMemberType( try o.builder.metadataString("ptr"), - null, // File - debug_fwd_ref, - 0, // Line - try o.lowerDebugType(pt, ptr_ty), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, ty.slicePtrFieldType(zcu)), ptr_size * 8, - (ptr_align.toByteUnits() orelse 0) * 8, - 0, // Offset + ptr_align.toByteUnits().? * 8, + 0, // offset ); const debug_len_type = try o.builder.debugMemberType( try o.builder.metadataString("len"), - null, // File - debug_fwd_ref, - 0, // Line - try o.lowerDebugType(pt, len_ty), - len_size * 8, - (len_align.toByteUnits() orelse 0) * 8, - len_offset * 8, + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, .usize), + ptr_size * 8, + ptr_align.toByteUnits().? * 8, + ptr_size * 8, ); - const debug_slice_type = try o.builder.debugStructType( - try o.builder.metadataString(name), - null, // File - o.debug_compile_unit.unwrap().?, // Scope - line, - null, // Underlying type - ty.abiSize(zcu) * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, + return o.builder.debugStructType( + name, + null, // file + o.debug_compile_unit.unwrap().?, // scope + 0, // line + null, // underlying type + ptr_size * 2 * 8, + ptr_align.toByteUnits().? * 8, try o.builder.metadataTuple(&.{ debug_ptr_type, debug_len_type, }), ); - - o.builder.resolveDebugForwardReference(debug_fwd_ref, debug_slice_type); - - // Set to real type now that it has been lowered fully - const map_ptr = o.debug_type_map.getPtr(ty.toIntern()) orelse unreachable; - map_ptr.* = debug_slice_type; - - return debug_slice_type; } - const debug_elem_ty = try o.lowerDebugType(pt, Type.fromInterned(ptr_info.child)); - - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - - const debug_ptr_type = try o.builder.debugPointerType( - try o.builder.metadataString(name), - null, // File - null, // Scope - 0, // Line - debug_elem_ty, - target.ptrBitWidth(), - (ty.ptrAlignment(zcu).toByteUnits() orelse 0) * 8, - 0, // Offset + return o.builder.debugPointerType( + name, + null, // file + o.debug_compile_unit.unwrap().?, // scope + 0, // line + try o.getDebugType(pt, ty.childType(zcu)), + ptr_size * 8, + ptr_align.toByteUnits().? * 8, + 0, // offset ); - - o.builder.resolveDebugForwardReference(debug_fwd_ref, debug_ptr_type); - - // Set to real type now that it has been lowered fully - const map_ptr = o.debug_type_map.getPtr(ty.toIntern()) orelse unreachable; - map_ptr.* = debug_ptr_type; - - return debug_ptr_type; - }, - .@"opaque" => { - if (ty.toIntern() == .anyopaque_type) { - const debug_opaque_type = try o.builder.debugSignedType( - try o.builder.metadataString("anyopaque"), - 0, - ); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_opaque_type); - return debug_opaque_type; - } - - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - - const file = try o.getDebugFile(pt, ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip)); - const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| - try o.namespaceToDebugScope(pt, parent_namespace) - else - file; - - const debug_opaque_type = try o.builder.debugStructType( - try o.builder.metadataString(name), - file, - scope, - ty.typeDeclSrcLine(zcu).? + 1, // Line - null, // Underlying type - 0, // Size - 0, // Align - null, // Fields - ); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_opaque_type); - return debug_opaque_type; - }, - .array => { - const debug_array_type = try o.builder.debugArrayType( - null, // Name - null, // File - null, // Scope - 0, // Line - try o.lowerDebugType(pt, ty.childType(zcu)), - ty.abiSize(zcu) * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.metadataTuple(&.{ - try o.builder.debugSubrange( - try o.builder.metadataConstant(try o.builder.intConst(.i64, 0)), - try o.builder.metadataConstant(try o.builder.intConst(.i64, ty.arrayLen(zcu))), - ), - }), - ); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_array_type); - return debug_array_type; }, + .array => return o.builder.debugArrayType( + name, + null, // file + o.debug_compile_unit.unwrap().?, // scope + 0, // line + try o.getDebugType(pt, ty.childType(zcu)), + ty.abiSize(zcu) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, + try o.builder.metadataTuple(&.{ + try o.builder.debugSubrange( + try o.builder.metadataConstant(try o.builder.intConst(.i64, 0)), + try o.builder.metadataConstant(try o.builder.intConst(.i64, ty.arrayLen(zcu))), + ), + }), + ), .vector => { - const elem_ty = ty.elemType2(zcu); + const elem_ty = ty.childType(zcu); // Vector elements cannot be padded since that would make - // @bitSizOf(elem) * len > @bitSizOf(vec). + // @bitSizeOf(elem) * len > @bitSizOf(vec). // Neither gdb nor lldb seem to be able to display non-byte sized // vectors properly. const debug_elem_type = switch (elem_ty.zigTypeTag(zcu)) { .int => blk: { const info = elem_ty.intInfo(zcu); - assert(info.bits != 0); - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - const builder_name = try o.builder.metadataString(name); break :blk switch (info.signedness) { - .signed => try o.builder.debugSignedType(builder_name, info.bits), - .unsigned => try o.builder.debugUnsignedType(builder_name, info.bits), + .signed => try o.builder.debugSignedType(name, info.bits), + .unsigned => try o.builder.debugUnsignedType(name, info.bits), }; }, - .bool => try o.builder.debugBoolType( - try o.builder.metadataString("bool"), - 1, - ), - else => try o.lowerDebugType(pt, ty.childType(zcu)), + .bool => try o.builder.debugBoolType(try o.builder.metadataString("bool"), 1), + // We don't pad pointers or floats, so we can lower those normally. + .pointer, .optional, .float => try o.getDebugType(pt, elem_ty), + else => unreachable, }; - const debug_vector_type = try o.builder.debugVectorType( - null, // Name - null, // File - null, // Scope - 0, // Line + return o.builder.debugVectorType( + name, + null, // file + o.debug_compile_unit.unwrap().?, // scope + 0, // line debug_elem_type, ty.abiSize(zcu) * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, try o.builder.metadataTuple(&.{ try o.builder.debugSubrange( try o.builder.metadataConstant(try o.builder.intConst(.i64, 0)), @@ -2151,566 +2085,574 @@ pub const Object = struct { ), }), ); - - try o.debug_type_map.put(gpa, ty.toIntern(), debug_vector_type); - return debug_vector_type; }, .optional => { - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - const child_ty = ty.optionalChild(zcu); - if (!child_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - const debug_bool_type = try o.builder.debugBoolType( - try o.builder.metadataString(name), - 8, + const payload_ty = ty.optionalChild(zcu); + if (ty.optionalReprIsPayload(zcu)) { + return o.builder.debugTypedefType( + name, + null, // file + o.debug_compile_unit.unwrap().?, // scope + 0, // line + try o.getDebugType(pt, payload_ty), + ty.abiSize(zcu) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, + 0, // offset ); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_bool_type); - return debug_bool_type; } - const debug_fwd_ref = try o.builder.debugForwardReference(); - - // Set as forward reference while the type is lowered in case it references itself - try o.debug_type_map.put(gpa, ty.toIntern(), debug_fwd_ref); - - if (ty.optionalReprIsPayload(zcu)) { - const debug_optional_type = try o.lowerDebugType(pt, child_ty); - - o.builder.resolveDebugForwardReference(debug_fwd_ref, debug_optional_type); - - // Set to real type now that it has been lowered fully - const map_ptr = o.debug_type_map.getPtr(ty.toIntern()) orelse unreachable; - map_ptr.* = debug_optional_type; - - return debug_optional_type; - } + const payload_size = payload_ty.abiSize(zcu); const non_null_ty = Type.u8; - const payload_size = child_ty.abiSize(zcu); - const payload_align = child_ty.abiAlignment(zcu); const non_null_size = non_null_ty.abiSize(zcu); const non_null_align = non_null_ty.abiAlignment(zcu); const non_null_offset = non_null_align.forward(payload_size); - const debug_data_type = try o.builder.debugMemberType( - try o.builder.metadataString("data"), - null, // File - debug_fwd_ref, - 0, // Line - try o.lowerDebugType(pt, child_ty), + const debug_payload_type = try o.builder.debugMemberType( + try o.builder.metadataString("payload"), + null, // file + ty_fwd_ref, // scope + 0, // line + try o.getDebugType(pt, payload_ty), payload_size * 8, - (payload_align.toByteUnits() orelse 0) * 8, - 0, // Offset + payload_ty.abiAlignment(zcu).toByteUnits().? * 8, + 0, // offset ); const debug_some_type = try o.builder.debugMemberType( try o.builder.metadataString("some"), null, - debug_fwd_ref, + ty_fwd_ref, 0, - try o.lowerDebugType(pt, non_null_ty), + try o.getDebugType(pt, non_null_ty), non_null_size * 8, - (non_null_align.toByteUnits() orelse 0) * 8, + non_null_align.toByteUnits().? * 8, non_null_offset * 8, ); - const debug_optional_type = try o.builder.debugStructType( - try o.builder.metadataString(name), - null, // File - o.debug_compile_unit.unwrap().?, // Scope - 0, // Line - null, // Underlying type + return o.builder.debugStructType( + name, + null, // file + o.debug_compile_unit.unwrap().?, // scope + 0, // line + null, // underlying type ty.abiSize(zcu) * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, try o.builder.metadataTuple(&.{ - debug_data_type, + debug_payload_type, debug_some_type, }), ); - - o.builder.resolveDebugForwardReference(debug_fwd_ref, debug_optional_type); - - // Set to real type now that it has been lowered fully - const map_ptr = o.debug_type_map.getPtr(ty.toIntern()) orelse unreachable; - map_ptr.* = debug_optional_type; - - return debug_optional_type; }, .error_union => { + const error_ty = ty.errorUnionSet(zcu); const payload_ty = ty.errorUnionPayload(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - // TODO: Maybe remove? - const debug_error_union_type = try o.lowerDebugType(pt, Type.anyerror); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_error_union_type); - return debug_error_union_type; - } - - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - const error_size = Type.anyerror.abiSize(zcu); - const error_align = Type.anyerror.abiAlignment(zcu); + const error_size = error_ty.abiSize(zcu); + const error_align = error_ty.abiAlignment(zcu); const payload_size = payload_ty.abiSize(zcu); const payload_align = payload_ty.abiAlignment(zcu); - var error_index: u32 = undefined; - var payload_index: u32 = undefined; - var error_offset: u64 = undefined; - var payload_offset: u64 = undefined; - if (error_align.compare(.gt, payload_align)) { - error_index = 0; - payload_index = 1; - error_offset = 0; - payload_offset = payload_align.forward(error_size); - } else { - payload_index = 0; - error_index = 1; - payload_offset = 0; - error_offset = error_align.forward(payload_size); - } - - const debug_fwd_ref = try o.builder.debugForwardReference(); + const error_offset: u64, const payload_offset: u64 = offsets: { + if (error_align.compare(.gt, payload_align)) { + break :offsets .{ 0, payload_align.forward(error_size) }; + } else { + break :offsets .{ error_align.forward(payload_size), 0 }; + } + }; - var fields: [2]Builder.Metadata = undefined; - fields[error_index] = try o.builder.debugMemberType( - try o.builder.metadataString("tag"), - null, // File - debug_fwd_ref, - 0, // Line - try o.lowerDebugType(pt, Type.anyerror), + const error_field = try o.builder.debugMemberType( + try o.builder.metadataString("error"), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, error_ty), error_size * 8, - (error_align.toByteUnits() orelse 0) * 8, + error_align.toByteUnits().? * 8, error_offset * 8, ); - fields[payload_index] = try o.builder.debugMemberType( - try o.builder.metadataString("value"), - null, // File - debug_fwd_ref, - 0, // Line - try o.lowerDebugType(pt, payload_ty), + const payload_field = try o.builder.debugMemberType( + try o.builder.metadataString("payload"), + null, // file + ty_fwd_ref, // scope + 0, // line + try o.getDebugType(pt, payload_ty), payload_size * 8, - (payload_align.toByteUnits() orelse 0) * 8, + payload_align.toByteUnits().? * 8, payload_offset * 8, ); - const debug_error_union_type = try o.builder.debugStructType( - try o.builder.metadataString(name), + return try o.builder.debugStructType( + name, null, // File - o.debug_compile_unit.unwrap().?, // Sope + o.debug_compile_unit.unwrap().?, // Scope 0, // Line null, // Underlying type ty.abiSize(zcu) * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.metadataTuple(&fields), + ty.abiAlignment(zcu).toByteUnits().? * 8, + try o.builder.metadataTuple(&.{ error_field, payload_field }), ); - - o.builder.resolveDebugForwardReference(debug_fwd_ref, debug_error_union_type); - - try o.debug_type_map.put(gpa, ty.toIntern(), debug_error_union_type); - return debug_error_union_type; }, .error_set => { - const debug_error_set = try o.builder.debugUnsignedType( - try o.builder.metadataString("anyerror"), - 16, + assert(ty.toIntern() != .anyerror_type); // handled specially in `updateConst`; will be populated by `emit` instead + // Error sets are just named wrappers around `anyerror`. + return o.builder.debugTypedefType( + name, + null, // file + o.debug_compile_unit.unwrap().?, // scope + 0, // line + try o.getDebugType(pt, .anyerror), + ty.abiSize(zcu) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, + 0, // offset ); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_error_set); - return debug_error_set; }, - .@"struct" => { - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - - if (zcu.typeToPackedStruct(ty)) |struct_type| { - const backing_int_ty = struct_type.backingIntTypeUnordered(ip); - if (backing_int_ty != .none) { - const info = Type.fromInterned(backing_int_ty).intInfo(zcu); - const builder_name = try o.builder.metadataString(name); - const debug_int_type = switch (info.signedness) { - .signed => try o.builder.debugSignedType(builder_name, ty.abiSize(zcu) * 8), - .unsigned => try o.builder.debugUnsignedType(builder_name, ty.abiSize(zcu) * 8), - }; - try o.debug_type_map.put(gpa, ty.toIntern(), debug_int_type); - return debug_int_type; - } + .@"fn" => { + if (!ty.fnHasRuntimeBits(zcu)) { + return o.builder.debugSignedType(name, 0); } - switch (ip.indexToKey(ty.toIntern())) { - .tuple_type => |tuple| { - var fields: std.ArrayList(Builder.Metadata) = .empty; - defer fields.deinit(gpa); - - try fields.ensureUnusedCapacity(gpa, tuple.types.len); + const fn_info = zcu.typeToFunc(ty).?; - comptime assert(struct_layout_version == 2); - var offset: u64 = 0; + var debug_param_types: std.ArrayList(Builder.Metadata) = try .initCapacity(gpa, 3 + fn_info.param_types.len); + defer debug_param_types.deinit(gpa); - const debug_fwd_ref = try o.builder.debugForwardReference(); + // Return type goes first. + const sret = firstParamSRet(fn_info, zcu, target); + const ret_ty: Type = if (sret) .void else .fromInterned(fn_info.return_type); + debug_param_types.appendAssumeCapacity(try o.getDebugType(pt, ret_ty)); - for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, i| { - if (field_val != .none or !Type.fromInterned(field_ty).hasRuntimeBits(zcu)) continue; + if (sret) { + const ptr_ty = try pt.singleMutPtrType(Type.fromInterned(fn_info.return_type)); + debug_param_types.appendAssumeCapacity(try o.getDebugType(pt, ptr_ty)); + } - const field_size = Type.fromInterned(field_ty).abiSize(zcu); - const field_align = Type.fromInterned(field_ty).abiAlignment(zcu); - const field_offset = field_align.forward(offset); - offset = field_offset + field_size; + if (fn_info.cc == .auto and zcu.comp.config.any_error_tracing) { + // Stack trace pointer. + debug_param_types.appendAssumeCapacity(try o.getDebugType(pt, .ptr_usize)); + } - var name_buf: [32]u8 = undefined; - const field_name = std.fmt.bufPrint(&name_buf, "{d}", .{i}) catch unreachable; + for (fn_info.param_types.get(ip)) |param_ty_ip| { + const param_ty: Type = .fromInterned(param_ty_ip); + if (!param_ty.hasRuntimeBits(zcu)) continue; + if (isByRef(param_ty, zcu)) { + const ptr_ty = try pt.singleConstPtrType(param_ty); + debug_param_types.appendAssumeCapacity(try o.getDebugType(pt, ptr_ty)); + } else { + debug_param_types.appendAssumeCapacity(try o.getDebugType(pt, param_ty)); + } + } - fields.appendAssumeCapacity(try o.builder.debugMemberType( - try o.builder.metadataString(field_name), - null, // File - debug_fwd_ref, - 0, - try o.lowerDebugType(pt, Type.fromInterned(field_ty)), - field_size * 8, - (field_align.toByteUnits() orelse 0) * 8, - field_offset * 8, - )); - } + return o.builder.debugSubroutineType( + try o.builder.metadataTuple(debug_param_types.items), + ); + }, + .@"struct" => { + if (ty.isTuple(zcu)) { + const tuple = ip.indexToKey(ty.toIntern()).tuple_type; + var fields: std.ArrayList(Builder.Metadata) = .empty; + defer fields.deinit(gpa); - const debug_struct_type = try o.builder.debugStructType( - try o.builder.metadataString(name), - null, // File - o.debug_compile_unit.unwrap().?, // Scope - 0, // Line - null, // Underlying type - ty.abiSize(zcu) * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.metadataTuple(fields.items), - ); + try fields.ensureUnusedCapacity(gpa, tuple.types.len); - o.builder.resolveDebugForwardReference(debug_fwd_ref, debug_struct_type); + comptime assert(struct_layout_version == 2); + var offset: u64 = 0; - try o.debug_type_map.put(gpa, ty.toIntern(), debug_struct_type); - return debug_struct_type; - }, - .struct_type => { - if (!ip.loadStructType(ty.toIntern()).haveFieldTypes(ip)) { - // This can happen if a struct type makes it all the way to - // flush() without ever being instantiated or referenced (even - // via pointer). The only reason we are hearing about it now is - // that it is being used as a namespace to put other debug types - // into. Therefore we can satisfy this by making an empty namespace, - // rather than changing the frontend to unnecessarily resolve the - // struct field types. - const debug_struct_type = try o.makeEmptyNamespaceDebugType(pt, ty); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_struct_type); - return debug_struct_type; - } - }, - else => {}, - } + for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty_ip, field_val, i| { + const field_ty: Type = .fromInterned(field_ty_ip); + if (field_val != .none or !field_ty.hasRuntimeBits(zcu)) continue; + + const field_size = field_ty.abiSize(zcu); + const field_align = field_ty.abiAlignment(zcu); + const field_offset = field_align.forward(offset); + offset = field_offset + field_size; + + fields.appendAssumeCapacity(try o.builder.debugMemberType( + try o.builder.metadataStringFmt("{d}", .{i}), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, field_ty), + field_size * 8, + field_align.toByteUnits().? * 8, + field_offset * 8, + )); + } - if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) { - const debug_struct_type = try o.makeEmptyNamespaceDebugType(pt, ty); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_struct_type); - return debug_struct_type; + return o.builder.debugStructType( + name, + null, // file + o.debug_compile_unit.unwrap().?, + 0, // line + null, // underlying type + ty.abiSize(zcu) * 8, + (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, + try o.builder.metadataTuple(fields.items), + ); } const struct_type = zcu.typeToStruct(ty).?; - var fields: std.ArrayList(Builder.Metadata) = .empty; - defer fields.deinit(gpa); - - try fields.ensureUnusedCapacity(gpa, struct_type.field_types.len); + const file = try o.getDebugFile(pt, struct_type.zir_index.resolveFile(ip)); + const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| + try o.namespaceToDebugScope(pt, parent_namespace) + else + file; - const debug_fwd_ref = try o.builder.debugForwardReference(); + const line = ty.typeDeclSrcLine(zcu).? + 1; - // Set as forward reference while the type is lowered in case it references itself - try o.debug_type_map.put(gpa, ty.toIntern(), debug_fwd_ref); + var fields: std.ArrayList(Builder.Metadata) = .empty; + defer fields.deinit(gpa); - comptime assert(struct_layout_version == 2); - var it = struct_type.iterateRuntimeOrder(ip); - while (it.next()) |field_index| { - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; - const field_size = field_ty.abiSize(zcu); - const field_align = ty.fieldAlignment(field_index, zcu); - const field_offset = ty.structFieldOffset(field_index, zcu); - const field_name = struct_type.fieldName(ip, field_index); - fields.appendAssumeCapacity(try o.builder.debugMemberType( - try o.builder.metadataString(field_name.toSlice(ip)), - null, // File - debug_fwd_ref, - 0, // Line - try o.lowerDebugType(pt, field_ty), - field_size * 8, - (field_align.toByteUnits() orelse 0) * 8, - field_offset * 8, - )); + switch (struct_type.layout) { + .@"packed" => { + try fields.ensureTotalCapacityPrecise(gpa, 1); + fields.appendAssumeCapacity(try o.builder.debugMemberType( + try o.builder.metadataString("bits"), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, .fromInterned(struct_type.packed_backing_int_type)), + ty.abiSize(zcu) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, + 0, // offset + )); + }, + .auto, .@"extern" => { + comptime assert(struct_layout_version == 2); + try fields.ensureTotalCapacityPrecise(gpa, struct_type.field_types.len); + var it = struct_type.iterateRuntimeOrder(ip); + while (it.next()) |field_index| { + const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBits(zcu)) continue; + const field_size = field_ty.abiSize(zcu); + const field_align = switch (ty.explicitFieldAlignment(field_index, zcu)) { + .none => field_ty.abiAlignment(zcu), + else => |a| a, + }; + const field_offset = struct_type.field_offsets.get(ip)[field_index]; + const field_name = struct_type.field_names.get(ip)[field_index]; + fields.appendAssumeCapacity(try o.builder.debugMemberType( + try o.builder.metadataString(field_name.toSlice(ip)), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, field_ty), + field_size * 8, + field_align.toByteUnits().? * 8, + field_offset * 8, + )); + } + }, } - const debug_struct_type = try o.builder.debugStructType( - try o.builder.metadataString(name), - null, // File - o.debug_compile_unit.unwrap().?, // Scope - 0, // Line - null, // Underlying type + return o.builder.debugStructType( + name, + file, + scope, + line, + null, // underlying type ty.abiSize(zcu) * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, try o.builder.metadataTuple(fields.items), ); - - o.builder.resolveDebugForwardReference(debug_fwd_ref, debug_struct_type); - - // Set to real type now that it has been lowered fully - const map_ptr = o.debug_type_map.getPtr(ty.toIntern()) orelse unreachable; - map_ptr.* = debug_struct_type; - - return debug_struct_type; }, .@"union" => { - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - const union_type = ip.loadUnionType(ty.toIntern()); - if (!union_type.haveFieldTypes(ip) or - !ty.hasRuntimeBitsIgnoreComptime(zcu) or - !union_type.haveLayout(ip)) - { - const debug_union_type = try o.makeEmptyNamespaceDebugType(pt, ty); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_union_type); - return debug_union_type; - } - const layout = Type.getUnionLayout(union_type, zcu); + const file = try o.getDebugFile(pt, union_type.zir_index.resolveFile(ip)); + const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| + try o.namespaceToDebugScope(pt, parent_namespace) + else + file; - const debug_fwd_ref = try o.builder.debugForwardReference(); + const line = ty.typeDeclSrcLine(zcu).? + 1; - // Set as forward reference while the type is lowered in case it references itself - try o.debug_type_map.put(gpa, ty.toIntern(), debug_fwd_ref); + const enum_tag_ty: Type = .fromInterned(union_type.enum_tag_type); - if (layout.payload_size == 0) { - const debug_union_type = try o.builder.debugStructType( - try o.builder.metadataString(name), - null, // File - o.debug_compile_unit.unwrap().?, // Scope - 0, // Line - null, // Underlying type + if (union_type.layout == .@"packed") { + const bitpack_field = try o.builder.debugMemberType( + try o.builder.metadataString("bits"), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, .fromInterned(union_type.packed_backing_int_type)), ty.abiSize(zcu) * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.metadataTuple( - &.{try o.lowerDebugType(pt, Type.fromInterned(union_type.enum_tag_ty))}, - ), + ty.abiAlignment(zcu).toByteUnits().? * 8, + 0, // offset ); + return o.builder.debugStructType( + name, + file, + scope, + line, + null, // underlying type + ty.abiSize(zcu) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, + try o.builder.metadataTuple(&.{bitpack_field}), + ); + } - // Set to real type now that it has been lowered fully - const map_ptr = o.debug_type_map.getPtr(ty.toIntern()) orelse unreachable; - map_ptr.* = debug_union_type; + const layout = Type.getUnionLayout(union_type, zcu); - return debug_union_type; + if (layout.payload_size == 0) { + const fields_tuple: ?Builder.Metadata = fields: { + if (layout.tag_size == 0) break :fields null; + break :fields try o.builder.metadataTuple(&.{ + try o.builder.debugMemberType( + try o.builder.metadataString("tag"), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, enum_tag_ty), + layout.tag_size * 8, + layout.tag_align.toByteUnits().? * 8, + 0, // offset + ), + }); + }; + return o.builder.debugStructType( + name, + file, + scope, + line, + null, // underlying type + ty.abiSize(zcu) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, + fields_tuple, + ); } - var fields: std.ArrayList(Builder.Metadata) = .empty; + var fields: std.ArrayList(Builder.Metadata) = try .initCapacity(gpa, union_type.field_types.len); defer fields.deinit(gpa); - try fields.ensureUnusedCapacity(gpa, union_type.loadTagType(ip).names.len); - - const debug_union_fwd_ref = if (layout.tag_size == 0) - debug_fwd_ref + const payload_fwd_ref = if (layout.tag_size == 0) + ty_fwd_ref else try o.builder.debugForwardReference(); - const tag_type = union_type.loadTagType(ip); - - for (0..tag_type.names.len) |field_index| { + for (0..union_type.field_types.len) |field_index| { const field_ty = union_type.field_types.get(ip)[field_index]; - if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(zcu)) continue; const field_size = Type.fromInterned(field_ty).abiSize(zcu); - const field_align: InternPool.Alignment = switch (union_type.flagsUnordered(ip).layout) { - .@"packed" => .none, - .auto, .@"extern" => ty.fieldAlignment(field_index, zcu), - }; + const field_align: InternPool.Alignment = ty.explicitFieldAlignment(field_index, zcu); - const field_name = tag_type.names.get(ip)[field_index]; + const field_name = enum_tag_ty.enumFieldName(field_index, zcu); fields.appendAssumeCapacity(try o.builder.debugMemberType( try o.builder.metadataString(field_name.toSlice(ip)), - null, // File - debug_union_fwd_ref, - 0, // Line - try o.lowerDebugType(pt, Type.fromInterned(field_ty)), + null, // file + payload_fwd_ref, + 0, // line + try o.getDebugType(pt, .fromInterned(field_ty)), field_size * 8, (field_align.toByteUnits() orelse 0) * 8, - 0, // Offset + 0, // offset )); } - var union_name_buf: ?[:0]const u8 = null; - defer if (union_name_buf) |buf| gpa.free(buf); - const union_name = if (layout.tag_size == 0) name else name: { - union_name_buf = try std.fmt.allocPrintSentinel(gpa, "{s}:Payload", .{name}, 0); - break :name union_name_buf.?; - }; - - const debug_union_type = try o.builder.debugUnionType( - try o.builder.metadataString(union_name), - null, // File - o.debug_compile_unit.unwrap().?, // Scope - 0, // Line - null, // Underlying type + const debug_payload_type = try o.builder.debugUnionType( + payload_name: { + if (layout.tag_size == 0) break :payload_name name; + break :payload_name try o.builder.metadataStringFmt("{f}:Payload", .{ty.fmt(pt)}); + }, + file, + scope, + line, + null, // underlying type layout.payload_size * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, try o.builder.metadataTuple(fields.items), ); - o.builder.resolveDebugForwardReference(debug_union_fwd_ref, debug_union_type); - if (layout.tag_size == 0) { - // Set to real type now that it has been lowered fully - const map_ptr = o.debug_type_map.getPtr(ty.toIntern()) orelse unreachable; - map_ptr.* = debug_union_type; - - return debug_union_type; + return debug_payload_type; } - var tag_offset: u64 = undefined; - var payload_offset: u64 = undefined; - if (layout.tag_align.compare(.gte, layout.payload_align)) { - tag_offset = 0; - payload_offset = layout.payload_align.forward(layout.tag_size); - } else { - payload_offset = 0; - tag_offset = layout.tag_align.forward(layout.payload_size); - } + o.builder.resolveDebugForwardReference(payload_fwd_ref, debug_payload_type); - const debug_tag_type = try o.builder.debugMemberType( + const tag_offset: u64, const payload_offset: u64 = offsets: { + if (layout.tag_align.compare(.gte, layout.payload_align)) { + break :offsets .{ 0, layout.payload_align.forward(layout.tag_size) }; + } else { + break :offsets .{ layout.tag_align.forward(layout.payload_size), 0 }; + } + }; + + const tag_member_type = try o.builder.debugMemberType( try o.builder.metadataString("tag"), - null, // File - debug_fwd_ref, - 0, // Line - try o.lowerDebugType(pt, Type.fromInterned(union_type.enum_tag_ty)), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, enum_tag_ty), layout.tag_size * 8, - (layout.tag_align.toByteUnits() orelse 0) * 8, + layout.tag_align.toByteUnits().? * 8, tag_offset * 8, ); - const debug_payload_type = try o.builder.debugMemberType( + const payload_member_type = try o.builder.debugMemberType( try o.builder.metadataString("payload"), - null, // File - debug_fwd_ref, - 0, // Line - debug_union_type, + null, // file + ty_fwd_ref, + 0, // line + debug_payload_type, layout.payload_size * 8, - (layout.payload_align.toByteUnits() orelse 0) * 8, + layout.payload_align.toByteUnits().? * 8, payload_offset * 8, ); const full_fields: [2]Builder.Metadata = if (layout.tag_align.compare(.gte, layout.payload_align)) - .{ debug_tag_type, debug_payload_type } + .{ tag_member_type, payload_member_type } else - .{ debug_payload_type, debug_tag_type }; + .{ payload_member_type, tag_member_type }; - const debug_tagged_union_type = try o.builder.debugStructType( - try o.builder.metadataString(name), - null, // File - o.debug_compile_unit.unwrap().?, // Scope - 0, // Line - null, // Underlying type + return o.builder.debugStructType( + name, + file, + scope, + line, + null, // underlying type ty.abiSize(zcu) * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, try o.builder.metadataTuple(&full_fields), ); + }, + .@"enum" => { + const file = try o.getDebugFile(pt, ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip)); + const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| + try o.namespaceToDebugScope(pt, parent_namespace) + else + file; - o.builder.resolveDebugForwardReference(debug_fwd_ref, debug_tagged_union_type); - - // Set to real type now that it has been lowered fully - const map_ptr = o.debug_type_map.getPtr(ty.toIntern()) orelse unreachable; - map_ptr.* = debug_tagged_union_type; + const line = ty.typeDeclSrcLine(zcu).? + 1; - return debug_tagged_union_type; - }, - .@"fn" => { - const fn_info = zcu.typeToFunc(ty).?; + if (!ty.hasRuntimeBits(zcu)) { + return o.builder.debugStructType( + name, + file, + scope, + line, + null, // underlying type + ty.abiSize(zcu) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, + null, // fields + ); + } - var debug_param_types = std.array_list.Managed(Builder.Metadata).init(gpa); - defer debug_param_types.deinit(); + const enum_type = ip.loadEnumType(ty.toIntern()); + const enumerators = try gpa.alloc(Builder.Metadata, enum_type.field_names.len); + defer gpa.free(enumerators); - try debug_param_types.ensureUnusedCapacity(3 + fn_info.param_types.len); + const int_ty: Type = .fromInterned(enum_type.int_tag_type); + const int_info = ty.intInfo(zcu); + assert(int_info.bits != 0); - // Return type goes first. - if (Type.fromInterned(fn_info.return_type).hasRuntimeBitsIgnoreComptime(zcu)) { - const sret = firstParamSRet(fn_info, zcu, target); - const ret_ty = if (sret) Type.void else Type.fromInterned(fn_info.return_type); - debug_param_types.appendAssumeCapacity(try o.lowerDebugType(pt, ret_ty)); - - if (sret) { - const ptr_ty = try pt.singleMutPtrType(Type.fromInterned(fn_info.return_type)); - debug_param_types.appendAssumeCapacity(try o.lowerDebugType(pt, ptr_ty)); - } - } else { - debug_param_types.appendAssumeCapacity(try o.lowerDebugType(pt, Type.void)); + for (enumerators, enum_type.field_names.get(ip), 0..) |*out, field_name, field_index| { + var space: Value.BigIntSpace = undefined; + const field_val: std.math.big.int.Const = switch (enum_type.field_values.len) { + 0 => std.math.big.int.Mutable.init(&space.limbs, field_index).toConst(), + else => Value.fromInterned(enum_type.field_values.get(ip)[field_index]).toBigInt(&space, zcu), + }; + out.* = try o.builder.debugEnumerator( + try o.builder.metadataString(field_name.toSlice(ip)), + int_info.signedness == .unsigned, + int_info.bits, + field_val, + ); } - if (fn_info.cc == .auto and zcu.comp.config.any_error_tracing) { - // Stack trace pointer. - debug_param_types.appendAssumeCapacity(try o.lowerDebugType(pt, .fromInterned(.ptr_usize_type))); + const debug_enum_type = try o.builder.debugEnumerationType( + name, + file, + scope, + line, + try o.getDebugType(pt, int_ty), + ty.abiSize(zcu) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, + try o.builder.metadataTuple(enumerators), + ); + try o.debug_enums.append(gpa, debug_enum_type); + return debug_enum_type; + }, + .@"opaque" => { + if (ty.toIntern() == .anyopaque_type) { + return o.builder.debugSignedType(name, 0); } - for (0..fn_info.param_types.len) |i| { - const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[i]); - if (!param_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + const file = try o.getDebugFile(pt, ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip)); + const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| + try o.namespaceToDebugScope(pt, parent_namespace) + else + file; - if (isByRef(param_ty, zcu)) { - const ptr_ty = try pt.singleMutPtrType(param_ty); - debug_param_types.appendAssumeCapacity(try o.lowerDebugType(pt, ptr_ty)); - } else { - debug_param_types.appendAssumeCapacity(try o.lowerDebugType(pt, param_ty)); - } - } + const line = ty.typeDeclSrcLine(zcu).? + 1; - const debug_function_type = try o.builder.debugSubroutineType( - try o.builder.metadataTuple(debug_param_types.items), + return o.builder.debugStructType( + name, + file, + scope, + line, + null, // underlying type + 0, // size + ty.abiAlignment(zcu).toByteUnits().? * 8, + null, // fields ); - - try o.debug_type_map.put(gpa, ty.toIntern(), debug_function_type); - return debug_function_type; }, - .comptime_int => unreachable, - .comptime_float => unreachable, - .type => unreachable, - .undefined => unreachable, - .null => unreachable, - .enum_literal => unreachable, - .frame => @panic("TODO implement lowerDebugType for Frame types"), .@"anyframe" => @panic("TODO implement lowerDebugType for AnyFrame types"), } } - fn namespaceToDebugScope(o: *Object, pt: Zcu.PerThread, namespace_index: InternPool.NamespaceIndex) !Builder.Metadata { + /// Called in `emit` so that the global error set is fully populated. + fn lowerDebugAnyerrorType(o: *Object, pt: Zcu.PerThread) Allocator.Error!Builder.Metadata { const zcu = pt.zcu; - const namespace = zcu.namespacePtr(namespace_index); - if (namespace.parent == .none) return try o.getDebugFile(pt, namespace.file_scope); + const ip = &zcu.intern_pool; + const gpa = zcu.comp.gpa; - const gop = try o.debug_unresolved_namespace_scopes.getOrPut(o.gpa, namespace_index); + const error_set_bits = zcu.errorSetBits(); + const error_names = ip.global_error_set.getNamesFromMainThread(); - if (!gop.found_existing) gop.value_ptr.* = try o.builder.debugForwardReference(); + const enumerators = try gpa.alloc(Builder.Metadata, error_names.len + 1); + defer gpa.free(enumerators); - return gop.value_ptr.*; + // The value 0 means "no error" in optionals and error unions. + enumerators[0] = try o.builder.debugEnumerator( + try o.builder.metadataString("null"), + true, // unsigned, + error_set_bits, + .{ .limbs = &.{0}, .positive = true }, // zero + ); + + for (enumerators[1..], error_names, 1..) |*out, error_name, error_value| { + var space: Value.BigIntSpace = undefined; + var bigint: std.math.big.int.Mutable = .init(&space.limbs, error_value); + out.* = try o.builder.debugEnumerator( + try o.builder.metadataStringFmt("error.{f}", .{error_name.fmtId(ip)}), + true, // unsigned + error_set_bits, + bigint.toConst(), + ); + } + + const debug_enum_type = try o.builder.debugEnumerationType( + try o.builder.metadataString("anyerror"), + null, // file + o.debug_compile_unit.unwrap().?, // scope + 0, // line + try o.getDebugType(pt, try pt.intType(.unsigned, error_set_bits)), + Type.anyerror.abiSize(zcu) * 8, + Type.anyerror.abiAlignment(zcu).toByteUnits().? * 8, + try o.builder.metadataTuple(enumerators), + ); + try o.debug_enums.append(gpa, debug_enum_type); + return debug_enum_type; } - fn makeEmptyNamespaceDebugType(o: *Object, pt: Zcu.PerThread, ty: Type) !Builder.Metadata { + fn namespaceToDebugScope(o: *Object, pt: Zcu.PerThread, namespace_index: InternPool.NamespaceIndex) !Builder.Metadata { const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const file = try o.getDebugFile(pt, ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip)); - const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| - try o.namespaceToDebugScope(pt, parent_namespace) - else - file; - return o.builder.debugStructType( - try o.builder.metadataString(ty.containerTypeName(ip).toSlice(ip)), // TODO use fully qualified name - file, - scope, - ty.typeDeclSrcLine(zcu).? + 1, - null, - 0, - 0, - null, - ); + const namespace = zcu.namespacePtr(namespace_index); + if (namespace.parent == .none) return try o.getDebugFile(pt, namespace.file_scope); + return o.getDebugType(pt, .fromInterned(namespace.owner_type)); } fn allocTypeName(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error![:0]const u8 { @@ -2804,7 +2746,7 @@ pub const Object = struct { function_index.setCallConv(cc_info.llvm_cc, &o.builder); if (cc_info.align_stack) { - try attributes.addFnAttr(.{ .alignstack = .fromByteUnits(target.stackAlignment()) }, &o.builder); + try attributes.addFnAttr(.{ .alignstack = .wrap(.fromByteUnits(target.stackAlignment())) }, &o.builder); } else { _ = try attributes.removeFnAttr(.alignstack); } @@ -2885,40 +2827,6 @@ pub const Object = struct { if (fn_info.return_type == .noreturn_type) try attributes.addFnAttr(.noreturn, &o.builder); - // Add parameter attributes. We handle only the case of extern functions (no body) - // because functions with bodies are handled in `updateFunc`. - if (is_extern) { - var it = iterateParamTypes(o, pt, fn_info); - it.llvm_index = llvm_arg_i; - while (try it.next()) |lowering| switch (lowering) { - .byval => { - const param_index = it.zig_index - 1; - const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[param_index]); - if (!isByRef(param_ty, zcu)) { - try o.addByValParamAttrs(pt, &attributes, param_ty, param_index, fn_info, it.llvm_index - 1); - } - }, - .byref => { - const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); - const param_llvm_ty = try o.lowerType(pt, param_ty); - const alignment = param_ty.abiAlignment(zcu); - try o.addByRefParamAttrs(&attributes, it.llvm_index - 1, alignment.toLlvm(), it.byval_attr, param_llvm_ty); - }, - .byref_mut => try attributes.addParamAttr(it.llvm_index - 1, .noundef, &o.builder), - // No attributes needed for these. - .no_bits, - .abi_sized_int, - .multiple_llvm_types, - .float_array, - .i32_array, - .i64_array, - => continue, - - .slice => unreachable, // extern functions do not support slice types. - - }; - } - function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); return function_index; } @@ -3223,7 +3131,7 @@ pub const Object = struct { ), .opt_type => |child_ty| { // Must stay in sync with `opt_payload` logic in `lowerPtr`. - if (!Type.fromInterned(child_ty).hasRuntimeBitsIgnoreComptime(zcu)) return .i8; + if (!Type.fromInterned(child_ty).hasRuntimeBits(zcu)) return .i8; const payload_ty = try o.lowerType(pt, Type.fromInterned(child_ty)); if (t.optionalReprIsPayload(zcu)) return payload_ty; @@ -3245,7 +3153,7 @@ pub const Object = struct { // Must stay in sync with `codegen.errUnionPayloadOffset`. // See logic in `lowerPtr`. const error_type = try o.errorIntType(pt); - if (!Type.fromInterned(error_union_type.payload_type).hasRuntimeBitsIgnoreComptime(zcu)) + if (!Type.fromInterned(error_union_type.payload_type).hasRuntimeBits(zcu)) return error_type; const payload_type = try o.lowerType(pt, Type.fromInterned(error_union_type.payload_type)); @@ -3287,7 +3195,7 @@ pub const Object = struct { const struct_type = ip.loadStructType(t.toIntern()); if (struct_type.layout == .@"packed") { - const int_ty = try o.lowerType(pt, Type.fromInterned(struct_type.backingIntTypeUnordered(ip))); + const int_ty = try o.lowerType(pt, .fromInterned(struct_type.packed_backing_int_type)); try o.type_map.put(o.gpa, t.toIntern(), int_ty); return int_ty; } @@ -3301,18 +3209,20 @@ pub const Object = struct { comptime assert(struct_layout_version == 2); var offset: u64 = 0; - var big_align: InternPool.Alignment = .@"1"; var struct_kind: Builder.Type.Structure.Kind = .normal; // When we encounter a zero-bit field, we place it here so we know to map it to the next non-zero-bit field (if any). var it = struct_type.iterateRuntimeOrder(ip); + var max_field_ty_align: InternPool.Alignment = .@"1"; while (it.next()) |field_index| { const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); - const field_align = t.fieldAlignment(field_index, zcu); const field_ty_align = field_ty.abiAlignment(zcu); - if (field_align.compare(.lt, field_ty_align)) struct_kind = .@"packed"; - big_align = big_align.max(field_align); + max_field_ty_align = max_field_ty_align.maxStrict(field_ty_align); + const prev_offset = offset; - offset = field_align.forward(offset); + offset = struct_type.field_offsets.get(ip)[field_index]; + if (@ctz(offset) < field_ty_align.toLog2Units()) { + struct_kind = .@"packed"; // prevent unexpected padding before this field + } const padding_len = offset - prev_offset; if (padding_len > 0) try llvm_field_types.append( @@ -3320,11 +3230,11 @@ pub const Object = struct { try o.builder.arrayType(padding_len, .i8), ); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!field_ty.hasRuntimeBits(zcu)) { // This is a zero-bit field. If there are runtime bits after this field, // map to the next LLVM field (which we know exists): otherwise, don't // map the field, indicating it's at the end of the struct. - if (offset != struct_type.sizeUnordered(ip)) { + if (offset != struct_type.size) { try o.struct_field_map.put(o.gpa, .{ .struct_ty = t.toIntern(), .field_index = field_index, @@ -3343,12 +3253,15 @@ pub const Object = struct { } { const prev_offset = offset; - offset = big_align.forward(offset); + offset = struct_type.alignment.forward(offset); const padding_len = offset - prev_offset; if (padding_len > 0) try llvm_field_types.append( o.gpa, try o.builder.arrayType(padding_len, .i8), ); + if (@ctz(offset) < max_field_ty_align.toLog2Units()) { + struct_kind = .@"packed"; // prevent unexpected trailing padding + } } const ty = try o.builder.opaqueType(try o.builder.string(t.containerTypeName(ip).toSlice(ip))); @@ -3391,7 +3304,7 @@ pub const Object = struct { o.gpa, try o.builder.arrayType(padding_len, .i8), ); - if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(zcu)) { + if (!Type.fromInterned(field_ty).hasRuntimeBits(zcu)) { // This is a zero-bit field. If there are runtime bits after this field, // map to the next LLVM field (which we know exists): otherwise, don't // map the field, indicating it's at the end of the struct. @@ -3426,16 +3339,17 @@ pub const Object = struct { if (o.type_map.get(t.toIntern())) |value| return value; const union_obj = ip.loadUnionType(t.toIntern()); - const layout = Type.getUnionLayout(union_obj, zcu); - if (union_obj.flagsUnordered(ip).layout == .@"packed") { - const int_ty = try o.builder.intType(@intCast(t.bitSize(zcu))); + if (union_obj.layout == .@"packed") { + const int_ty = try o.lowerType(pt, .fromInterned(union_obj.packed_backing_int_type)); try o.type_map.put(o.gpa, t.toIntern(), int_ty); return int_ty; } + const layout = Type.getUnionLayout(union_obj, zcu); + if (layout.payload_size == 0) { - const enum_tag_ty = try o.lowerType(pt, Type.fromInterned(union_obj.enum_tag_ty)); + const enum_tag_ty = try o.lowerType(pt, .fromInterned(union_obj.enum_tag_type)); try o.type_map.put(o.gpa, t.toIntern(), enum_tag_ty); return enum_tag_ty; } @@ -3467,7 +3381,7 @@ pub const Object = struct { ); return ty; } - const enum_tag_ty = try o.lowerType(pt, Type.fromInterned(union_obj.enum_tag_ty)); + const enum_tag_ty = try o.lowerType(pt, .fromInterned(union_obj.enum_tag_type)); // Put the tag before or after the payload depending on which one's // alignment is greater. @@ -3502,7 +3416,7 @@ pub const Object = struct { } return gop.value_ptr.*; }, - .enum_type => try o.lowerType(pt, Type.fromInterned(ip.loadEnumType(t.toIntern()).tag_ty)), + .enum_type => try o.lowerType(pt, t.intTagType(zcu)), .func_type => |func_type| try o.lowerTypeFn(pt, func_type), .error_set_type, .inferred_error_set_type => try o.errorIntType(pt), // values, not types @@ -3516,13 +3430,13 @@ pub const Object = struct { .error_union, .enum_literal, .enum_tag, - .empty_enum_value, .float, .ptr, .slice, .opt, .aggregate, .un, + .bitpack, // memoization, not types .memoized_call, => unreachable, @@ -3530,20 +3444,6 @@ pub const Object = struct { }; } - /// Use this instead of lowerType when you want to handle correctly the case of elem_ty - /// being a zero bit type, but it should still be lowered as an i8 in such case. - /// There are other similar cases handled here as well. - fn lowerPtrElemTy(o: *Object, pt: Zcu.PerThread, elem_ty: Type) Allocator.Error!Builder.Type { - const zcu = pt.zcu; - const lower_elem_ty = switch (elem_ty.zigTypeTag(zcu)) { - .@"opaque" => true, - .@"fn" => !zcu.typeToFunc(elem_ty).?.is_generic, - .array => elem_ty.childType(zcu).hasRuntimeBitsIgnoreComptime(zcu), - else => elem_ty.hasRuntimeBitsIgnoreComptime(zcu), - }; - return if (lower_elem_ty) try o.lowerType(pt, elem_ty) else .i8; - } - fn lowerTypeFn(o: *Object, pt: Zcu.PerThread, fn_info: InternPool.Key.FuncType) Allocator.Error!Builder.Type { const zcu = pt.zcu; const ip = &zcu.intern_pool; @@ -3558,9 +3458,9 @@ pub const Object = struct { } if (fn_info.cc == .auto and zcu.comp.config.any_error_tracing) { - const stack_trace_ty = zcu.builtin_decl_values.get(.StackTrace); - const ptr_ty = try pt.ptrType(.{ .child = stack_trace_ty }); - try llvm_params.append(o.gpa, try o.lowerType(pt, ptr_ty)); + // First parameter is a pointer to `std.builtin.StackTrace`. + const llvm_ptr_ty = try o.builder.ptrType(toLlvmAddressSpace(.generic, target)); + try llvm_params.append(o.gpa, llvm_ptr_ty); } var it = iterateParamTypes(o, pt, fn_info); @@ -3610,84 +3510,6 @@ pub const Object = struct { ); } - fn lowerValueToInt(o: *Object, pt: Zcu.PerThread, llvm_int_ty: Builder.Type, arg_val: InternPool.Index) Error!Builder.Constant { - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const target = zcu.getTarget(); - - const val = Value.fromInterned(arg_val); - const val_key = ip.indexToKey(val.toIntern()); - - if (val.isUndef(zcu)) return o.builder.undefConst(llvm_int_ty); - - const ty = Type.fromInterned(val_key.typeOf()); - switch (val_key) { - .@"extern" => |@"extern"| { - const function_index = try o.resolveLlvmFunction(pt, @"extern".owner_nav); - const ptr = function_index.ptrConst(&o.builder).global.toConst(); - return o.builder.convConst(ptr, llvm_int_ty); - }, - .func => |func| { - const function_index = try o.resolveLlvmFunction(pt, func.owner_nav); - const ptr = function_index.ptrConst(&o.builder).global.toConst(); - return o.builder.convConst(ptr, llvm_int_ty); - }, - .ptr => return o.builder.convConst(try o.lowerPtr(pt, arg_val, 0), llvm_int_ty), - .aggregate => switch (ip.indexToKey(ty.toIntern())) { - .struct_type, .vector_type => {}, - else => unreachable, - }, - .un => |un| { - const layout = ty.unionGetLayout(zcu); - if (layout.payload_size == 0) return o.lowerValue(pt, un.tag); - - const union_obj = zcu.typeToUnion(ty).?; - const container_layout = union_obj.flagsUnordered(ip).layout; - - assert(container_layout == .@"packed"); - - var need_unnamed = false; - if (un.tag == .none) { - assert(layout.tag_size == 0); - const union_val = try o.lowerValueToInt(pt, llvm_int_ty, un.val); - - need_unnamed = true; - return union_val; - } - const field_index = zcu.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?; - const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); - if (!field_ty.hasRuntimeBits(zcu)) return o.builder.intConst(llvm_int_ty, 0); - return o.lowerValueToInt(pt, llvm_int_ty, un.val); - }, - .simple_value => |simple_value| switch (simple_value) { - .false, .true => {}, - else => unreachable, - }, - .int, - .float, - .enum_tag, - => {}, - .opt => {}, // pointer like optional expected - else => unreachable, - } - var stack = std.heap.stackFallback(32, o.gpa); - const allocator = stack.get(); - - const bits: usize = @intCast(ty.bitSize(zcu)); - - const buffer = try allocator.alloc(u8, (bits + 7) / 8); - defer allocator.free(buffer); - const limbs = try allocator.alloc(std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(bits)); - defer allocator.free(limbs); - - val.writeToPackedMemory(ty, pt, buffer, 0) catch unreachable; - - var big: std.math.big.int.Mutable = .init(limbs, 0); - big.readTwosComplement(buffer, bits, target.cpu.arch.endian(), .unsigned); - - return o.builder.bigIntConst(llvm_int_ty, big.toConst()); - } - fn lowerValue(o: *Object, pt: Zcu.PerThread, arg_val: InternPool.Index) Error!Builder.Constant { const zcu = pt.zcu; const ip = &zcu.intern_pool; @@ -3700,7 +3522,9 @@ pub const Object = struct { return o.builder.undefConst(try o.lowerType(pt, Type.fromInterned(val_key.typeOf()))); } - const ty = Type.fromInterned(val_key.typeOf()); + const ty: Type = .fromInterned(val_key.typeOf()); + ty.assertHasLayout(zcu); + return switch (val_key) { .int_type, .ptr_type, @@ -3722,10 +3546,8 @@ pub const Object = struct { .undef => unreachable, // handled above .simple_value => |simple_value| switch (simple_value) { - .undefined => unreachable, // non-runtime value .void => unreachable, // non-runtime value .null => unreachable, // non-runtime value - .empty_tuple => unreachable, // non-runtime value .@"unreachable" => unreachable, // non-runtime value .false => .false, @@ -3733,7 +3555,6 @@ pub const Object = struct { }, .variable, .enum_literal, - .empty_enum_value, => unreachable, // non-runtime values .@"extern" => |@"extern"| { const function_index = try o.resolveLlvmFunction(pt, @"extern".owner_nav); @@ -3763,7 +3584,7 @@ pub const Object = struct { }; const err_int_ty = try pt.errorIntType(); const payload_type = ty.errorUnionPayload(zcu); - if (!payload_type.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_type.hasRuntimeBits(zcu)) { // We use the error type directly as the type. return o.lowerValue(pt, err_val); } @@ -3825,7 +3646,7 @@ pub const Object = struct { const payload_ty = ty.optionalChild(zcu); const non_null_bit = try o.builder.intConst(.i8, @intFromBool(opt.val != .none)); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { return non_null_bit; } const llvm_ty = try o.lowerType(pt, ty); @@ -3861,6 +3682,7 @@ pub const Object = struct { fields[0..llvm_ty_fields.len], ), vals[0..llvm_ty_fields.len]); }, + .bitpack => |bitpack| return o.lowerValue(pt, bitpack.backing_int_val), .aggregate => |aggregate| switch (ip.indexToKey(ty.toIntern())) { .array_type => |array_type| switch (aggregate.storage) { .bytes => |bytes| try o.builder.stringConst(try o.builder.string( @@ -3992,7 +3814,7 @@ pub const Object = struct { 0.., ) |field_ty, field_val, field_index| { if (field_val != .none) continue; - if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!Type.fromInterned(field_ty).hasRuntimeBits(zcu)) continue; const field_align = Type.fromInterned(field_ty).abiAlignment(zcu); big_align = big_align.max(field_align); @@ -4038,16 +3860,8 @@ pub const Object = struct { }, .struct_type => { const struct_type = ip.loadStructType(ty.toIntern()); - assert(struct_type.haveLayout(ip)); const struct_ty = try o.lowerType(pt, ty); - if (struct_type.layout == .@"packed") { - comptime assert(Type.packed_struct_layout_version == 2); - - const bits = ty.bitSize(zcu); - const llvm_int_ty = try o.builder.intType(@intCast(bits)); - - return o.lowerValueToInt(pt, llvm_int_ty, arg_val); - } + assert(struct_type.layout != .@"packed"); const llvm_len = struct_ty.aggregateLen(&o.builder); const ExpectedContents = extern struct { @@ -4067,15 +3881,12 @@ pub const Object = struct { comptime assert(struct_layout_version == 2); var llvm_index: usize = 0; var offset: u64 = 0; - var big_align: InternPool.Alignment = .@"1"; var need_unnamed = false; var field_it = struct_type.iterateRuntimeOrder(ip); while (field_it.next()) |field_index| { const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); - const field_align = ty.fieldAlignment(field_index, zcu); - big_align = big_align.max(field_align); const prev_offset = offset; - offset = field_align.forward(offset); + offset = struct_type.field_offsets.get(ip)[field_index]; const padding_len = offset - prev_offset; if (padding_len > 0) { @@ -4088,7 +3899,7 @@ pub const Object = struct { llvm_index += 1; } - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!field_ty.hasRuntimeBits(zcu)) { // This is a zero-bit field - we only needed it for the alignment. continue; } @@ -4106,7 +3917,7 @@ pub const Object = struct { } { const prev_offset = offset; - offset = big_align.forward(offset); + offset = struct_type.alignment.forward(offset); const padding_len = offset - prev_offset; if (padding_len > 0) { fields[llvm_index] = try o.builder.arrayType(padding_len, .i8); @@ -4130,19 +3941,13 @@ pub const Object = struct { if (layout.payload_size == 0) return o.lowerValue(pt, un.tag); const union_obj = zcu.typeToUnion(ty).?; - const container_layout = union_obj.flagsUnordered(ip).layout; + const container_layout = union_obj.layout; + assert(container_layout != .@"packed"); var need_unnamed = false; const payload = if (un.tag != .none) p: { const field_index = zcu.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?; const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); - if (container_layout == .@"packed") { - if (!field_ty.hasRuntimeBits(zcu)) return o.builder.intConst(union_ty, 0); - const bits = ty.bitSize(zcu); - const llvm_int_ty = try o.builder.intType(@intCast(bits)); - - return o.lowerValueToInt(pt, llvm_int_ty, arg_val); - } // Sometimes we must make an unnamed struct because LLVM does // not support bitcasting our payload struct to the true union payload type. @@ -4150,14 +3955,14 @@ pub const Object = struct { // must pointer cast to the expected type before accessing the union. need_unnamed = layout.most_aligned_field != field_index; - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!field_ty.hasRuntimeBits(zcu)) { const padding_len = layout.payload_size; break :p try o.builder.undefConst(try o.builder.arrayType(padding_len, .i8)); } const payload = try o.lowerValue(pt, un.val); const payload_ty = payload.typeOf(&o.builder); if (payload_ty != union_ty.structFields(&o.builder)[ - @intFromBool(layout.tag_align.compare(.gte, layout.payload_align)) + @intFromBool(layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align)) ]) need_unnamed = true; const field_size = field_ty.abiSize(zcu); if (field_size == layout.payload_size) break :p payload; @@ -4169,13 +3974,6 @@ pub const Object = struct { ); } else p: { assert(layout.tag_size == 0); - if (container_layout == .@"packed") { - const bits = ty.bitSize(zcu); - const llvm_int_ty = try o.builder.intType(@intCast(bits)); - - return o.lowerValueToInt(pt, llvm_int_ty, arg_val); - } - const union_val = try o.lowerValue(pt, un.val); need_unnamed = true; break :p union_val; @@ -4277,7 +4075,14 @@ pub const Object = struct { }; return o.lowerPtr(pt, field.base, offset + field_off); }, - .arr_elem, .comptime_field, .comptime_alloc => unreachable, + .arr_elem => |arr_elem| { + const base_ptr_ty = Value.fromInterned(arr_elem.base).typeOf(zcu); + assert(base_ptr_ty.ptrSize(zcu) == .many); + const elem_size = base_ptr_ty.childType(zcu).abiSize(zcu); + return o.lowerPtr(pt, arr_elem.base, offset + elem_size * arr_elem.index); + }, + .comptime_field => unreachable, + .comptime_alloc => unreachable, }; } @@ -4302,12 +4107,11 @@ pub const Object = struct { const ptr_ty = Type.fromInterned(uav.orig_ty); - const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn"; - if ((!is_fn_body and !uav_ty.hasRuntimeBits(zcu)) or - (is_fn_body and zcu.typeToFunc(uav_ty).?.is_generic)) return o.lowerPtrToVoid(pt, ptr_ty); + if (!uav_ty.isRuntimeFnOrHasRuntimeBits(zcu)) { + return o.lowerPtrToVoid(pt, ptr_ty); + } - if (is_fn_body) - @panic("TODO"); + assert(uav_ty.zigTypeTag(zcu) != .@"fn"); // should be using a Nav ref const llvm_addr_space = toLlvmAddressSpace(ptr_ty.ptrAddressSpace(zcu), target); const alignment = ptr_ty.ptrAlignment(zcu); @@ -4330,14 +4134,11 @@ pub const Object = struct { const nav_ty = Type.fromInterned(nav.typeOf(ip)); const ptr_ty = try pt.navPtrType(nav_index); - const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn"; - if ((!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) or - (is_fn_body and zcu.typeToFunc(nav_ty).?.is_generic)) - { + if (nav.getExtern(ip) == null and !nav_ty.isRuntimeFnOrHasRuntimeBits(zcu)) { return o.lowerPtrToVoid(pt, ptr_ty); } - const llvm_global = if (is_fn_body) + const llvm_global = if (nav_ty.zigTypeTag(zcu) == .@"fn") (try o.resolveLlvmFunction(pt, nav_index)).ptrConst(&o.builder).global else (try o.resolveGlobalNav(pt, nav_index)).ptrConst(&o.builder).global; @@ -4380,21 +4181,18 @@ pub const Object = struct { /// types to work around a LLVM deficiency when targeting ARM/AArch64. fn getAtomicAbiType(o: *Object, pt: Zcu.PerThread, ty: Type, is_rmw_xchg: bool) Allocator.Error!Builder.Type { const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const int_ty = switch (ty.zigTypeTag(zcu)) { - .int => ty, - .@"enum" => ty.intTagType(zcu), - .@"struct" => Type.fromInterned(ip.loadStructType(ty.toIntern()).backingIntTypeUnordered(ip)), + switch (ty.zigTypeTag(zcu)) { + .int, .@"enum", .@"struct", .@"union" => {}, .float => { if (!is_rmw_xchg) return .none; return o.builder.intType(@intCast(ty.abiSize(zcu) * 8)); }, .bool => return .i8, else => return .none, - }; - const bit_count = int_ty.intInfo(zcu).bits; + } + const bit_count = ty.bitSize(zcu); if (!std.math.isPowerOfTwo(bit_count) or (bit_count % 8) != 0) { - return o.builder.intType(@intCast(int_ty.abiSize(zcu) * 8)); + return o.builder.intType(@intCast(ty.abiSize(zcu) * 8)); } else { return .none; } @@ -4435,11 +4233,11 @@ pub const Object = struct { if (ptr_info.flags.is_const) { try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder); } - const elem_align = if (ptr_info.flags.alignment != .none) - ptr_info.flags.alignment - else - Type.fromInterned(ptr_info.child).abiAlignment(zcu).max(.@"1"); - try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = elem_align.toLlvm() }, &o.builder); + const elem_align: Builder.Alignment.Lazy = switch (ptr_info.flags.alignment) { + else => |a| .wrap(a.toLlvm()), + .none => try o.lazyAbiAlignment(pt, .fromInterned(ptr_info.child)), + }; + try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = elem_align }, &o.builder); } else if (ccAbiPromoteInt(fn_info.cc, zcu, param_ty)) |s| switch (s) { .signed => try attributes.addParamAttr(llvm_arg_i, .signext, &o.builder), .unsigned => try attributes.addParamAttr(llvm_arg_i, .zeroext, &o.builder), @@ -4456,7 +4254,7 @@ pub const Object = struct { ) Allocator.Error!void { try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder); - try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = alignment }, &o.builder); + try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = .wrap(alignment) }, &o.builder); if (byval) try attributes.addParamAttr(llvm_arg_i, .{ .byval = param_llvm_ty }, &o.builder); } @@ -4502,7 +4300,7 @@ pub const Object = struct { const ret_ty = try o.lowerType(pt, Type.slice_const_u8_sentinel_0); const target = &zcu.root_mod.resolved_target.result; const function_index = try o.builder.addFunction( - try o.builder.fnType(ret_ty, &.{try o.lowerType(pt, Type.fromInterned(enum_type.tag_ty))}, .normal), + try o.builder.fnType(ret_ty, &.{try o.lowerType(pt, Type.fromInterned(enum_type.int_tag_type))}, .normal), try o.builder.strtabStringFmt("__zig_tag_name_{f}", .{enum_type.name.fmt(ip)}), toLlvmAddressSpace(.generic, target), ); @@ -4525,12 +4323,16 @@ pub const Object = struct { const bad_value_block = try wip.block(1, "BadValue"); const tag_int_value = wip.arg(0); - var wip_switch = - try wip.@"switch"(tag_int_value, bad_value_block, @intCast(enum_type.names.len), .none); + var wip_switch = try wip.@"switch"( + tag_int_value, + bad_value_block, + @intCast(enum_type.field_names.len), + .none, + ); defer wip_switch.finish(&wip); - for (0..enum_type.names.len) |field_index| { - const name = try o.builder.stringNull(enum_type.names.get(ip)[field_index].toSlice(ip)); + for (0..enum_type.field_names.len) |field_index| { + const name = try o.builder.stringNull(enum_type.field_names.get(ip)[field_index].toSlice(ip)); const name_init = try o.builder.stringConst(name); const name_variable_index = try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); @@ -4562,6 +4364,11 @@ pub const Object = struct { try wip.finish(); return function_index; } + + fn lazyAbiAlignment(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error!Builder.Alignment.Lazy { + const index = try o.type_pool.get(pt, .{ .llvm = o }, ty.toIntern()); + return o.lazy_abi_aligns.items[@intFromEnum(index)]; + } }; pub const NavGen = struct { @@ -4601,10 +4408,44 @@ pub const NavGen = struct { const ty = Type.fromInterned(nav.typeOf(ip)); if (linkage != .internal and ip.isFunctionType(ty.toIntern())) { - _ = try o.resolveLlvmFunction(pt, owner_nav); + const function_index = try o.resolveLlvmFunction(pt, owner_nav); + // Add parameter attributes which weren't set by `resolveLlvmFunction` + const fn_info = zcu.typeToFunc(ty).?; + var attributes = try function_index.ptrConst(&o.builder).attributes.toWip(&o.builder); + defer attributes.deinit(&o.builder); + var it = iterateParamTypes(o, pt, fn_info); + if (firstParamSRet(fn_info, zcu, zcu.getTarget())) it.llvm_index += 1; + if (fn_info.cc == .auto and zcu.comp.config.any_error_tracing) it.llvm_index += 1; + while (try it.next()) |lowering| switch (lowering) { + .byval => { + const param_index = it.zig_index - 1; + const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[param_index]); + if (!isByRef(param_ty, zcu)) { + try o.addByValParamAttrs(pt, &attributes, param_ty, param_index, fn_info, it.llvm_index - 1); + } + }, + .byref => { + const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); + const param_llvm_ty = try o.lowerType(pt, param_ty); + const alignment = param_ty.abiAlignment(zcu); + try o.addByRefParamAttrs(&attributes, it.llvm_index - 1, alignment.toLlvm(), it.byval_attr, param_llvm_ty); + }, + .byref_mut => try attributes.addParamAttr(it.llvm_index - 1, .noundef, &o.builder), + // No attributes needed for these. + .no_bits, + .abi_sized_int, + .multiple_llvm_types, + .float_array, + .i32_array, + .i64_array, + => continue, + + .slice => unreachable, // extern functions do not support slice types. + }; + function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); } else { const variable_index = try o.resolveGlobalNav(pt, nav_index); - variable_index.setAlignment(pt.navAlignment(nav_index).toLlvm(), &o.builder); + variable_index.setAlignment(zcu.navAlignment(nav_index).toLlvm(), &o.builder); if (resolved.@"linksection".toSlice(ip)) |section| variable_index.setSection(try o.builder.string(section), &o.builder); if (is_const) variable_index.setMutability(.constant, &o.builder); @@ -4630,7 +4471,7 @@ pub const NavGen = struct { debug_file, // File debug_file, // Scope line_number, - try o.lowerDebugType(pt, ty), + try o.getDebugType(pt, ty), variable_index, .{ .local = linkage == .internal }, ); @@ -4752,7 +4593,7 @@ pub const FuncGen = struct { /// Have we seen loads or stores involving `allowzero` pointers? allowzero_access: bool = false, - pub fn maybeMarkAllowZeroAccess(self: *FuncGen, info: InternPool.Key.PtrType) void { + fn maybeMarkAllowZeroAccess(self: *FuncGen, info: InternPool.Key.PtrType) void { // LLVM already considers null pointers to be valid in non-generic address spaces, so avoid // pessimizing optimization for functions with accesses to such pointers. if (info.flags.address_space == .generic and info.flags.is_allowzero) self.allowzero_access = true; @@ -5220,7 +5061,7 @@ pub const FuncGen = struct { try o.builder.metadataString(nav.fqn.toSlice(&zcu.intern_pool)), line_number, line_number + func.lbrace_line, - try o.lowerDebugType(pt, fn_ty), + try o.getDebugType(pt, fn_ty), .{ .di_flags = .{ .StaticMember = true }, .sp_flags = .{ @@ -5490,10 +5331,10 @@ pub const FuncGen = struct { if (ptr_info.flags.is_const) { try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder); } - const elem_align = (if (ptr_info.flags.alignment != .none) - @as(InternPool.Alignment, ptr_info.flags.alignment) - else - Type.fromInterned(ptr_info.child).abiAlignment(zcu).max(.@"1")).toLlvm(); + const elem_align: Builder.Alignment.Lazy = switch (ptr_info.flags.alignment) { + else => |a| .wrap(a.toLlvm()), + .none => try o.lazyAbiAlignment(pt, .fromInterned(ptr_info.child)), + }; try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = elem_align }, &o.builder); }, }; @@ -5518,7 +5359,7 @@ pub const FuncGen = struct { return .none; } - if (self.liveness.isUnused(inst) or !return_type.hasRuntimeBitsIgnoreComptime(zcu)) { + if (self.liveness.isUnused(inst) or !return_type.hasRuntimeBits(zcu)) { return .none; } @@ -5637,7 +5478,7 @@ pub const FuncGen = struct { return; } const fn_info = zcu.typeToFunc(Type.fromInterned(ip.getNav(self.ng.nav_index).typeOf(ip))).?; - if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!ret_ty.hasRuntimeBits(zcu)) { if (Type.fromInterned(fn_info.return_type).isError(zcu)) { // Functions with an empty error set are emitted with an error code // return type and return zero so they can be function pointers coerced @@ -5702,7 +5543,7 @@ pub const FuncGen = struct { const ptr_ty = self.typeOf(un_op); const ret_ty = ptr_ty.childType(zcu); const fn_info = zcu.typeToFunc(Type.fromInterned(ip.getNav(self.ng.nav_index).typeOf(ip))).?; - if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!ret_ty.hasRuntimeBits(zcu)) { if (Type.fromInterned(fn_info.return_type).isError(zcu)) { // Functions with an empty error set are emitted with an error code // return type and return zero so they can be function pointers coerced @@ -5833,14 +5674,13 @@ pub const FuncGen = struct { const o = self.ng.object; const pt = self.ng.pt; const zcu = pt.zcu; - const ip = &zcu.intern_pool; const scalar_ty = operand_ty.scalarType(zcu); const int_ty = switch (scalar_ty.zigTypeTag(zcu)) { .@"enum" => scalar_ty.intTagType(zcu), .int, .bool, .pointer, .error_set => scalar_ty, .optional => blk: { const payload_ty = operand_ty.optionalChild(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu) or + if (!payload_ty.hasRuntimeBits(zcu) or operand_ty.optionalReprIsPayload(zcu)) { break :blk operand_ty; @@ -5912,12 +5752,7 @@ pub const FuncGen = struct { return phi.toValue(); }, .float => return self.buildFloatCmp(fast, op, operand_ty, .{ lhs, rhs }), - .@"struct" => blk: { - const struct_obj = ip.loadStructType(scalar_ty.toIntern()); - assert(struct_obj.layout == .@"packed"); - const backing_index = struct_obj.backingIntTypeUnordered(ip); - break :blk Type.fromInterned(backing_index); - }, + .@"struct" => scalar_ty.bitpackBackingInt(zcu), else => unreachable, }; const is_signed = int_ty.isSignedInt(zcu); @@ -5953,7 +5788,7 @@ pub const FuncGen = struct { return .none; } - const have_block_result = inst_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu); + const have_block_result = inst_ty.hasRuntimeBits(zcu); var breaks: BreakList = if (have_block_result) .{ .list = .{} } else .{ .len = 0 }; defer if (have_block_result) breaks.list.deinit(self.gpa); @@ -6000,7 +5835,7 @@ pub const FuncGen = struct { // Add the values to the lists only if the break provides a value. const operand_ty = self.typeOf(branch.operand); - if (operand_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) { + if (operand_ty.hasRuntimeBits(zcu)) { const val = try self.resolveInst(branch.operand); // For the phi node, we need the basic blocks and the values of the @@ -6309,7 +6144,7 @@ pub const FuncGen = struct { const pt = fg.ng.pt; const zcu = pt.zcu; const payload_ty = err_union_ty.errorUnionPayload(zcu); - const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(zcu); + const payload_has_bits = payload_ty.hasRuntimeBits(zcu); const err_union_llvm_ty = try o.lowerType(pt, err_union_ty); const error_type = try o.errorIntType(pt); @@ -6645,7 +6480,7 @@ pub const FuncGen = struct { const len = try o.builder.intValue(llvm_usize, array_ty.arrayLen(zcu)); const slice_llvm_ty = try o.lowerType(pt, self.typeOfIndex(inst)); const operand = try self.resolveInst(ty_op.operand); - if (!array_ty.hasRuntimeBitsIgnoreComptime(zcu)) + if (!array_ty.hasRuntimeBits(zcu)) return self.wip.buildAggregate(slice_llvm_ty, &.{ operand, len }, ""); const ptr = try self.wip.gep(.inbounds, try o.lowerType(pt, array_ty), operand, &.{ try o.builder.intValue(llvm_usize, 0), try o.builder.intValue(llvm_usize, 0), @@ -6828,7 +6663,7 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const slice_ptr = try self.resolveInst(ty_op.operand); const slice_ptr_ty = self.typeOf(ty_op.operand); - const slice_llvm_ty = try o.lowerPtrElemTy(pt, slice_ptr_ty.childType(zcu)); + const slice_llvm_ty = try o.lowerType(pt, slice_ptr_ty.childType(zcu)); return self.wip.gepStruct(slice_llvm_ty, slice_ptr, index, ""); } @@ -6842,7 +6677,7 @@ pub const FuncGen = struct { const slice = try self.resolveInst(bin_op.lhs); const index = try self.resolveInst(bin_op.rhs); const elem_ty = slice_ty.childType(zcu); - const llvm_elem_ty = try o.lowerPtrElemTy(pt, elem_ty); + const llvm_elem_ty = try o.lowerType(pt, elem_ty); const base_ptr = try self.wip.extractValue(slice, &.{0}, ""); const ptr = try self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, &.{index}, ""); if (isByRef(elem_ty, zcu)) { @@ -6867,7 +6702,7 @@ pub const FuncGen = struct { const slice = try self.resolveInst(bin_op.lhs); const index = try self.resolveInst(bin_op.rhs); - const llvm_elem_ty = try o.lowerPtrElemTy(pt, slice_ty.childType(zcu)); + const llvm_elem_ty = try o.lowerType(pt, slice_ty.childType(zcu)); const base_ptr = try self.wip.extractValue(slice, &.{0}, ""); return self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, &.{index}, ""); } @@ -6906,16 +6741,11 @@ pub const FuncGen = struct { const zcu = pt.zcu; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ptr_ty = self.typeOf(bin_op.lhs); - const elem_ty = ptr_ty.childType(zcu); - const llvm_elem_ty = try o.lowerPtrElemTy(pt, elem_ty); + const elem_ty = ptr_ty.indexableElem(zcu); + const llvm_elem_ty = try o.lowerType(pt, elem_ty); const base_ptr = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - // TODO: when we go fully opaque pointers in LLVM 16 we can remove this branch - const ptr = try self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, if (ptr_ty.isSinglePointer(zcu)) - // If this is a single-item pointer to an array, we need another index in the GEP. - &.{ try o.builder.intValue(try o.lowerType(pt, Type.usize), 0), rhs } - else - &.{rhs}, ""); + const ptr = try self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, &.{rhs}, ""); if (isByRef(elem_ty, zcu)) { self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu)); const ptr_align = (ptr_ty.ptrAlignment(zcu).min(elem_ty.abiAlignment(zcu))).toLlvm(); @@ -6934,8 +6764,8 @@ pub const FuncGen = struct { const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const ptr_ty = self.typeOf(bin_op.lhs); - const elem_ty = ptr_ty.childType(zcu); - if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) return self.resolveInst(bin_op.lhs); + const elem_ty = ptr_ty.indexableElem(zcu); + assert(elem_ty.hasRuntimeBits(zcu)); const base_ptr = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -6943,12 +6773,8 @@ pub const FuncGen = struct { const elem_ptr = ty_pl.ty.toType(); if (elem_ptr.ptrInfo(zcu).flags.vector_index != .none) return base_ptr; - const llvm_elem_ty = try o.lowerPtrElemTy(pt, elem_ty); - return self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, if (ptr_ty.isSinglePointer(zcu)) - // If this is a single-item pointer to an array, we need another index in the GEP. - &.{ try o.builder.intValue(try o.lowerType(pt, Type.usize), 0), rhs } - else - &.{rhs}, ""); + const llvm_elem_ty = try o.lowerType(pt, elem_ty); + return self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, &.{rhs}, ""); } fn airStructFieldPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { @@ -6956,7 +6782,7 @@ pub const FuncGen = struct { const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; const struct_ptr = try self.resolveInst(struct_field.struct_operand); const struct_ptr_ty = self.typeOf(struct_field.struct_operand); - return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, struct_field.field_index); + return self.fieldPtr(struct_ptr, struct_ptr_ty, struct_field.field_index); } fn airStructFieldPtrIndex( @@ -6967,7 +6793,7 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const struct_ptr = try self.resolveInst(ty_op.operand); const struct_ptr_ty = self.typeOf(ty_op.operand); - return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, field_index); + return self.fieldPtr(struct_ptr, struct_ptr_ty, field_index); } fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { @@ -6980,7 +6806,7 @@ pub const FuncGen = struct { const struct_llvm_val = try self.resolveInst(struct_field.struct_operand); const field_index = struct_field.field_index; const field_ty = struct_ty.fieldType(field_index, zcu); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) return .none; + if (!field_ty.hasRuntimeBits(zcu)) return .none; if (!isByRef(struct_ty, zcu)) { assert(!isByRef(field_ty, zcu)); @@ -6999,11 +6825,6 @@ pub const FuncGen = struct { const truncated_int = try self.wip.cast(.trunc, shifted_value, same_size_int, ""); return self.wip.cast(.bitcast, truncated_int, elem_llvm_ty, ""); - } else if (field_ty.isPtrAtRuntime(zcu)) { - const same_size_int = try o.builder.intType(@intCast(field_ty.bitSize(zcu))); - const truncated_int = - try self.wip.cast(.trunc, shifted_value, same_size_int, ""); - return self.wip.cast(.inttoptr, truncated_int, elem_llvm_ty, ""); } return self.wip.cast(.trunc, shifted_value, elem_llvm_ty, ""); }, @@ -7021,11 +6842,6 @@ pub const FuncGen = struct { const truncated_int = try self.wip.cast(.trunc, containing_int, same_size_int, ""); return self.wip.cast(.bitcast, truncated_int, elem_llvm_ty, ""); - } else if (field_ty.isPtrAtRuntime(zcu)) { - const same_size_int = try o.builder.intType(@intCast(field_ty.bitSize(zcu))); - const truncated_int = - try self.wip.cast(.trunc, containing_int, same_size_int, ""); - return self.wip.cast(.inttoptr, truncated_int, elem_llvm_ty, ""); } return self.wip.cast(.trunc, containing_int, elem_llvm_ty, ""); }, @@ -7041,15 +6857,17 @@ pub const FuncGen = struct { const llvm_field_index = o.llvmFieldIndex(struct_ty, field_index).?; const field_ptr = try self.wip.gepStruct(struct_llvm_ty, struct_llvm_val, llvm_field_index, ""); - const alignment = struct_ty.fieldAlignment(field_index, zcu); + const explicit_alignment = struct_ty.explicitFieldAlignment(field_index, zcu); const field_ptr_ty = try pt.ptrType(.{ .child = field_ty.toIntern(), - .flags = .{ .alignment = alignment }, + .flags = .{ .alignment = explicit_alignment }, }); if (isByRef(field_ty, zcu)) { - assert(alignment != .none); - const field_alignment = alignment.toLlvm(); - return self.loadByRef(field_ptr, field_ty, field_alignment, .normal); + const alignment = switch (explicit_alignment) { + .none => field_ty.abiAlignment(zcu), + else => |a| a, + }; + return self.loadByRef(field_ptr, field_ty, alignment.toLlvm(), .normal); } else { return self.load(field_ptr, field_ptr_ty); } @@ -7057,7 +6875,7 @@ pub const FuncGen = struct { .@"union" => { const union_llvm_ty = try o.lowerType(pt, struct_ty); const layout = struct_ty.unionGetLayout(zcu); - const payload_index = @intFromBool(layout.tag_align.compare(.gte, layout.payload_align)); + const payload_index = @intFromBool(layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align)); const field_ptr = try self.wip.gepStruct(union_llvm_ty, struct_llvm_val, payload_index, ""); const payload_alignment = layout.payload_align.toLlvm(); @@ -7150,7 +6968,7 @@ pub const FuncGen = struct { self.file, self.scope, self.prev_dbg_line, - try o.lowerDebugType(pt, ptr_ty.childType(zcu)), + try o.getDebugType(pt, ptr_ty.childType(zcu)), ); _ = try self.wip.callIntrinsic( @@ -7183,7 +7001,7 @@ pub const FuncGen = struct { self.file, self.scope, self.prev_dbg_line, - try o.lowerDebugType(pt, operand_ty), + try o.getDebugType(pt, operand_ty), arg_no: { self.arg_inline_index += 1; break :arg_no self.arg_inline_index; @@ -7193,7 +7011,7 @@ pub const FuncGen = struct { self.file, self.scope, self.prev_dbg_line, - try o.lowerDebugType(pt, operand_ty), + try o.getDebugType(pt, operand_ty), ); const zcu = pt.zcu; @@ -7284,6 +7102,7 @@ pub const FuncGen = struct { const llvm_param_attrs = try arena.alloc(Builder.Type, max_param_count); const pt = self.ng.pt; const zcu = pt.zcu; + const ip = &zcu.intern_pool; const target = zcu.getTarget(); var llvm_ret_i: usize = 0; @@ -7308,7 +7127,7 @@ pub const FuncGen = struct { const output_inst = try self.resolveInst(output.operand); const output_ty = self.typeOf(output.operand); assert(output_ty.zigTypeTag(zcu) == .pointer); - const elem_llvm_ty = try o.lowerPtrElemTy(pt, output_ty.childType(zcu)); + const elem_llvm_ty = try o.lowerType(pt, output_ty.childType(zcu)); switch (constraint[0]) { '=' => {}, @@ -7426,7 +7245,7 @@ pub const FuncGen = struct { llvm_param_attrs[llvm_param_i] = if (constraint[0] == '*') blk: { if (!is_by_ref) self.maybeMarkAllowZeroAccess(arg_ty.ptrInfo(zcu)); - break :blk try o.lowerPtrElemTy(pt, if (is_by_ref) arg_ty else arg_ty.childType(zcu)); + break :blk try o.lowerType(pt, if (is_by_ref) arg_ty else arg_ty.childType(zcu)); } else .none; llvm_param_i += 1; @@ -7440,7 +7259,7 @@ pub const FuncGen = struct { if (constraint[0] != '+') continue; const rw_ty = self.typeOf(output.operand); - const llvm_elem_ty = try o.lowerPtrElemTy(pt, rw_ty.childType(zcu)); + const llvm_elem_ty = try o.lowerType(pt, rw_ty.childType(zcu)); if (llvm_ret_indirect[output.index]) { llvm_param_values[llvm_param_i] = llvm_rw_vals[output.index]; llvm_param_types[llvm_param_i] = llvm_rw_vals[output.index].typeOfWip(&self.wip); @@ -7467,30 +7286,21 @@ pub const FuncGen = struct { total_i += 1; } - const ip = &zcu.intern_pool; - const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate; - const struct_type: Type = .fromInterned(aggregate.ty); if (total_i != 0) try llvm_constraints.append(gpa, ','); - switch (aggregate.storage) { - .elems => |elems| for (elems, 0..) |elem, i| { - switch (elem) { - .bool_true => { - const name = struct_type.structFieldName(i, zcu).toSlice(ip).?; - total_i += try appendConstraints(gpa, &llvm_constraints, name, target); - }, - .bool_false => continue, - else => unreachable, - } - }, - .repeated_elem => |elem| switch (elem) { - .bool_true => for (0..struct_type.structFieldCount(zcu)) |i| { - const name = struct_type.structFieldName(i, zcu).toSlice(ip).?; - total_i += try appendConstraints(gpa, &llvm_constraints, name, target); - }, - .bool_false => {}, - else => unreachable, - }, - .bytes => @panic("TODO"), + const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers); + const clobbers_ty = clobbers_val.typeOf(zcu); + var clobbers_bigint_buf: Value.BigIntSpace = undefined; + const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu); + for (0..clobbers_ty.structFieldCount(zcu)) |field_index| { + assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type); + const limb_bits = @bitSizeOf(std.math.big.Limb); + if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false + switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) { + 0 => continue, // field is false + 1 => {}, // field is true + } + const name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?; + total_i += try appendConstraints(gpa, &llvm_constraints, name, target); } // We have finished scanning through all inputs/outputs, so the number of @@ -7676,7 +7486,7 @@ pub const FuncGen = struct { comptime assert(optional_layout_version == 3); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { const loaded = if (operand_is_ptr) try self.wip.load(access_kind, optional_llvm_ty, operand, .default, "") else @@ -7719,7 +7529,7 @@ pub const FuncGen = struct { if (operand_is_ptr) self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu)); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { const loaded = if (operand_is_ptr) try self.wip.load(access_kind, try o.lowerType(pt, err_union_ty), operand, .default, "") else @@ -7746,7 +7556,7 @@ pub const FuncGen = struct { const operand = try self.resolveInst(ty_op.operand); const optional_ty = self.typeOf(ty_op.operand).childType(zcu); const payload_ty = optional_ty.optionalChild(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { // We have a pointer to a zero-bit value and we need to return // a pointer to a zero-bit value. return operand; @@ -7774,7 +7584,7 @@ pub const FuncGen = struct { const access_kind: Builder.MemoryAccessKind = if (optional_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { self.maybeMarkAllowZeroAccess(optional_ptr_ty.ptrInfo(zcu)); // We have a pointer to a i8. We need to set it to 1 and then return the same pointer. @@ -7810,7 +7620,7 @@ pub const FuncGen = struct { const operand = try self.resolveInst(ty_op.operand); const optional_ty = self.typeOf(ty_op.operand); const payload_ty = self.typeOfIndex(inst); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) return .none; + if (!payload_ty.hasRuntimeBits(zcu)) return .none; if (optional_ty.optionalReprIsPayload(zcu)) { // Payload value is the same as the optional value. @@ -7832,7 +7642,7 @@ pub const FuncGen = struct { const result_ty = self.typeOfIndex(inst); const payload_ty = if (operand_is_ptr) result_ty.childType(zcu) else result_ty; - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { return if (operand_is_ptr) operand else .none; } const offset = try errUnionPayloadOffset(payload_ty, pt); @@ -7876,7 +7686,7 @@ pub const FuncGen = struct { if (operand_is_ptr and operand_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; const payload_ty = err_union_ty.errorUnionPayload(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { if (!operand_is_ptr) return operand; self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu)); @@ -7912,7 +7722,7 @@ pub const FuncGen = struct { const access_kind: Builder.MemoryAccessKind = if (err_union_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { self.maybeMarkAllowZeroAccess(err_union_ptr_ty.ptrInfo(zcu)); _ = try self.wip.store(access_kind, non_error_val, operand, .default); @@ -7959,9 +7769,8 @@ pub const FuncGen = struct { const struct_llvm_ty = try o.lowerType(pt, struct_ty); const llvm_field_index = o.llvmFieldIndex(struct_ty, field_index).?; assert(self.err_ret_trace != .none); - const field_ptr = - try self.wip.gepStruct(struct_llvm_ty, self.err_ret_trace, llvm_field_index, ""); - const field_alignment = struct_ty.fieldAlignment(field_index, zcu); + const field_ptr = try self.wip.gepStruct(struct_llvm_ty, self.err_ret_trace, llvm_field_index, ""); + const field_alignment = struct_ty.explicitFieldAlignment(field_index, zcu); const field_ty = struct_ty.fieldType(field_index, zcu); const field_ptr_ty = try pt.ptrType(.{ .child = field_ty.toIntern(), @@ -8002,7 +7811,7 @@ pub const FuncGen = struct { const payload_ty = self.typeOf(ty_op.operand); const non_null_bit = try o.builder.intValue(.i8, 1); comptime assert(optional_layout_version == 3); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) return non_null_bit; + assert(payload_ty.hasRuntimeBits(zcu)); const operand = try self.resolveInst(ty_op.operand); const optional_ty = self.typeOfIndex(inst); if (optional_ty.optionalReprIsPayload(zcu)) return operand; @@ -8036,9 +7845,7 @@ pub const FuncGen = struct { const err_un_ty = self.typeOfIndex(inst); const operand = try self.resolveInst(ty_op.operand); const payload_ty = self.typeOf(ty_op.operand); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - return operand; - } + assert(payload_ty.hasRuntimeBits(zcu)); const ok_err_code = try o.builder.intValue(try o.errorIntType(pt), 0); const err_un_llvm_ty = try o.lowerType(pt, err_un_ty); @@ -8078,7 +7885,7 @@ pub const FuncGen = struct { const err_un_ty = self.typeOfIndex(inst); const payload_ty = err_un_ty.errorUnionPayload(zcu); const operand = try self.resolveInst(ty_op.operand); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) return operand; + if (!payload_ty.hasRuntimeBits(zcu)) return operand; const err_un_llvm_ty = try o.lowerType(pt, err_un_ty); const payload_offset = try errUnionPayloadOffset(payload_ty, pt); @@ -8530,7 +8337,7 @@ pub const FuncGen = struct { const ptr = try self.resolveInst(bin_op.lhs); const offset = try self.resolveInst(bin_op.rhs); const ptr_ty = self.typeOf(bin_op.lhs); - const llvm_elem_ty = try o.lowerPtrElemTy(pt, ptr_ty.childType(zcu)); + const llvm_elem_ty = try o.lowerType(pt, ptr_ty.childType(zcu)); switch (ptr_ty.ptrSize(zcu)) { // It's a pointer to an array, so according to LLVM we need an extra GEP index. .one => return self.wip.gep(.inbounds, llvm_elem_ty, ptr, &.{ @@ -8554,7 +8361,7 @@ pub const FuncGen = struct { const offset = try self.resolveInst(bin_op.rhs); const negative_offset = try self.wip.neg(offset, ""); const ptr_ty = self.typeOf(bin_op.lhs); - const llvm_elem_ty = try o.lowerPtrElemTy(pt, ptr_ty.childType(zcu)); + const llvm_elem_ty = try o.lowerType(pt, ptr_ty.childType(zcu)); switch (ptr_ty.ptrSize(zcu)) { // It's a pointer to an array, so according to LLVM we need an extra GEP index. .one => return self.wip.gep(.inbounds, llvm_elem_ty, ptr, &.{ @@ -9515,7 +9322,7 @@ pub const FuncGen = struct { self.file, self.scope, lbrace_line, - try o.lowerDebugType(pt, inst_ty), + try o.getDebugType(pt, inst_ty), self.arg_index, ); @@ -9581,7 +9388,7 @@ pub const FuncGen = struct { const zcu = pt.zcu; const ptr_ty = self.typeOfIndex(inst); const pointee_type = ptr_ty.childType(zcu); - if (!pointee_type.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) + if (!pointee_type.hasRuntimeBits(zcu)) return (try o.lowerPtrToVoid(pt, ptr_ty)).toValue(); const pointee_llvm_ty = try o.lowerType(pt, pointee_type); @@ -9595,7 +9402,7 @@ pub const FuncGen = struct { const zcu = pt.zcu; const ptr_ty = self.typeOfIndex(inst); const ret_ty = ptr_ty.childType(zcu); - if (!ret_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) + if (!ret_ty.hasRuntimeBits(zcu)) return (try o.lowerPtrToVoid(pt, ptr_ty)).toValue(); if (self.ret_ptr != .none) return self.ret_ptr; const ret_llvm_ty = try o.lowerType(pt, ret_ty); @@ -9849,7 +9656,7 @@ pub const FuncGen = struct { const ptr_ty = self.typeOf(atomic_load.ptr); const info = ptr_ty.ptrInfo(zcu); const elem_ty = Type.fromInterned(info.child); - if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) return .none; + if (!elem_ty.hasRuntimeBits(zcu)) return .none; const ordering = toLlvmAtomicOrdering(atomic_load.order); const llvm_abi_ty = try o.getAtomicAbiType(pt, elem_ty, false); const ptr_alignment = (if (info.flags.alignment != .none) @@ -9897,7 +9704,7 @@ pub const FuncGen = struct { const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ptr_ty = self.typeOf(bin_op.lhs); const operand_ty = ptr_ty.childType(zcu); - if (!operand_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return .none; + if (!operand_ty.hasRuntimeBits(zcu)) return .none; const ptr = try self.resolveInst(bin_op.lhs); var element = try self.resolveInst(bin_op.rhs); const llvm_abi_ty = try o.getAtomicAbiType(pt, operand_ty, false); @@ -10310,14 +10117,14 @@ pub const FuncGen = struct { const ip = &zcu.intern_pool; const enum_type = ip.loadEnumType(enum_ty.toIntern()); - // TODO: detect when the type changes and re-emit this function. + // TODO: detect when the type changes (`updateContainerType` will be called) and re-emit this function const gop = try o.named_enum_map.getOrPut(o.gpa, enum_ty.toIntern()); if (gop.found_existing) return gop.value_ptr.*; errdefer assert(o.named_enum_map.remove(enum_ty.toIntern())); const target = &zcu.root_mod.resolved_target.result; const function_index = try o.builder.addFunction( - try o.builder.fnType(.i1, &.{try o.lowerType(pt, Type.fromInterned(enum_type.tag_ty))}, .normal), + try o.builder.fnType(.i1, &.{try o.lowerType(pt, Type.fromInterned(enum_type.int_tag_type))}, .normal), try o.builder.strtabStringFmt("__zig_is_named_enum_value_{f}", .{enum_type.name.fmt(ip)}), toLlvmAddressSpace(.generic, target), ); @@ -10338,13 +10145,13 @@ pub const FuncGen = struct { defer wip.deinit(); wip.cursor = .{ .block = try wip.block(0, "Entry") }; - const named_block = try wip.block(@intCast(enum_type.names.len), "Named"); + const named_block = try wip.block(@intCast(enum_type.field_names.len), "Named"); const unnamed_block = try wip.block(1, "Unnamed"); const tag_int_value = wip.arg(0); - var wip_switch = try wip.@"switch"(tag_int_value, unnamed_block, @intCast(enum_type.names.len), .none); + var wip_switch = try wip.@"switch"(tag_int_value, unnamed_block, @intCast(enum_type.field_names.len), .none); defer wip_switch.finish(&wip); - for (0..enum_type.names.len) |field_index| { + for (0..enum_type.field_names.len) |field_index| { const this_tag_int_value = try o.lowerValue( pt, (try pt.enumValueFieldIndex(enum_ty, @intCast(field_index))).toIntern(), @@ -10813,15 +10620,14 @@ pub const FuncGen = struct { }, .@"struct" => { if (zcu.typeToPackedStruct(result_ty)) |struct_type| { - const backing_int_ty = struct_type.backingIntTypeUnordered(ip); - assert(backing_int_ty != .none); - const big_bits = Type.fromInterned(backing_int_ty).bitSize(zcu); + const backing_int_ty: Type = .fromInterned(struct_type.packed_backing_int_type); + const big_bits = backing_int_ty.bitSize(zcu); const int_ty = try o.builder.intType(@intCast(big_bits)); comptime assert(Type.packed_struct_layout_version == 2); var running_int = try o.builder.intValue(int_ty, 0); var running_bits: u16 = 0; for (elements, struct_type.field_types.get(ip)) |elem, field_ty| { - if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!Type.fromInterned(field_ty).hasRuntimeBits(zcu)) continue; const non_int_val = try self.resolveInst(elem); const ty_bit_size: u16 = @intCast(Type.fromInterned(field_ty).bitSize(zcu)); @@ -10853,12 +10659,12 @@ pub const FuncGen = struct { const llvm_elem = try self.resolveInst(elem); const llvm_i = o.llvmFieldIndex(result_ty, i).?; - const field_ptr = - try self.wip.gepStruct(llvm_result_ty, alloca_inst, llvm_i, ""); + const field_ptr = try self.wip.gepStruct(llvm_result_ty, alloca_inst, llvm_i, ""); + const field_ptr_ty = try pt.ptrType(.{ .child = self.typeOf(elem).toIntern(), .flags = .{ - .alignment = result_ty.fieldAlignment(i, zcu), + .alignment = result_ty.explicitFieldAlignment(i, zcu), }, }); try self.store(field_ptr, field_ptr_ty, llvm_elem, .none); @@ -10920,28 +10726,16 @@ pub const FuncGen = struct { const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; const union_ty = self.typeOfIndex(inst); const union_llvm_ty = try o.lowerType(pt, union_ty); - const layout = union_ty.unionGetLayout(zcu); const union_obj = zcu.typeToUnion(union_ty).?; - if (union_obj.flagsUnordered(ip).layout == .@"packed") { - const big_bits = union_ty.bitSize(zcu); - const int_llvm_ty = try o.builder.intType(@intCast(big_bits)); - const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]); - const non_int_val = try self.resolveInst(extra.init); - const small_int_ty = try o.builder.intType(@intCast(field_ty.bitSize(zcu))); - const small_int_val = if (field_ty.isPtrAtRuntime(zcu)) - try self.wip.cast(.ptrtoint, non_int_val, small_int_ty, "") - else - try self.wip.cast(.bitcast, non_int_val, small_int_ty, ""); - return self.wip.conv(.unsigned, small_int_val, int_llvm_ty, ""); - } + assert(union_obj.layout != .@"packed"); + + const layout = Type.getUnionLayout(union_obj, zcu); const tag_int_val = blk: { const tag_ty = union_ty.unionTagTypeHypothetical(zcu); - const union_field_name = union_obj.loadTagType(ip).names.get(ip)[extra.field_index]; - const enum_field_index = tag_ty.enumFieldIndex(union_field_name, zcu).?; - const tag_val = try pt.enumValueFieldIndex(tag_ty, enum_field_index); - break :blk try tag_val.intFromEnum(tag_ty, pt); + const tag_val = try pt.enumValueFieldIndex(tag_ty, extra.field_index); + break :blk tag_val.intFromEnum(zcu); }; if (layout.payload_size == 0) { if (layout.tag_size == 0) { @@ -10963,16 +10757,14 @@ pub const FuncGen = struct { const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]); const field_llvm_ty = try o.lowerType(pt, field_ty); const field_size = field_ty.abiSize(zcu); - const field_align = union_ty.fieldAlignment(extra.field_index, zcu); + const field_align = union_ty.explicitFieldAlignment(extra.field_index, zcu); const llvm_usize = try o.lowerType(pt, Type.usize); const usize_zero = try o.builder.intValue(llvm_usize, 0); + assert(field_ty.hasRuntimeBits(zcu)); + const llvm_union_ty = t: { const payload_ty = p: { - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - const padding_len = layout.payload_size; - break :p try o.builder.arrayType(padding_len, .i8); - } if (field_size == layout.payload_size) { break :p field_llvm_ty; } @@ -10982,7 +10774,7 @@ pub const FuncGen = struct { }); }; if (layout.tag_size == 0) break :t try o.builder.structType(.normal, &.{payload_ty}); - const tag_ty = try o.lowerType(pt, Type.fromInterned(union_obj.enum_tag_ty)); + const tag_ty = try o.lowerType(pt, .fromInterned(union_obj.enum_tag_type)); var fields: [3]Builder.Type = undefined; var fields_len: usize = 2; if (layout.tag_align.compare(.gte, layout.payload_align)) { @@ -11023,11 +10815,11 @@ pub const FuncGen = struct { const tag_index = @intFromBool(layout.tag_align.compare(.lt, layout.payload_align)); const indices: [2]Builder.Value = .{ usize_zero, try o.builder.intValue(.i32, tag_index) }; const field_ptr = try self.wip.gep(.inbounds, llvm_union_ty, result_ptr, &indices, ""); - const tag_ty = try o.lowerType(pt, Type.fromInterned(union_obj.enum_tag_ty)); + const tag_ty = try o.lowerType(pt, .fromInterned(union_obj.enum_tag_type)); var big_int_space: Value.BigIntSpace = undefined; const tag_big_int = tag_int_val.toBigInt(&big_int_space, zcu); const llvm_tag = try o.builder.bigIntValue(tag_ty, tag_big_int); - const tag_alignment = Type.fromInterned(union_obj.enum_tag_ty).abiAlignment(zcu).toLlvm(); + const tag_alignment = Type.fromInterned(union_obj.enum_tag_type).abiAlignment(zcu).toLlvm(); _ = try self.wip.store(.normal, llvm_tag, field_ptr, tag_alignment); } @@ -11274,63 +11066,45 @@ pub const FuncGen = struct { fn fieldPtr( self: *FuncGen, - inst: Air.Inst.Index, - struct_ptr: Builder.Value, - struct_ptr_ty: Type, + aggregate_ptr: Builder.Value, + aggregate_ptr_ty: Type, field_index: u32, ) !Builder.Value { const o = self.ng.object; const pt = self.ng.pt; const zcu = pt.zcu; - const struct_ty = struct_ptr_ty.childType(zcu); - switch (struct_ty.zigTypeTag(zcu)) { - .@"struct" => switch (struct_ty.containerLayout(zcu)) { - .@"packed" => { - const result_ty = self.typeOfIndex(inst); - const result_ty_info = result_ty.ptrInfo(zcu); - const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu); - const struct_type = zcu.typeToStruct(struct_ty).?; - - if (result_ty_info.packed_offset.host_size != 0) { - // From LLVM's perspective, a pointer to a packed struct and a pointer - // to a field of a packed struct are the same. The difference is in the - // Zig pointer type which provides information for how to mask and shift - // out the relevant bits when accessing the pointee. - return struct_ptr; - } - - // We have a pointer to a packed struct field that happens to be byte-aligned. - // Offset our operand pointer by the correct number of bytes. - const byte_offset = @divExact(zcu.structPackedFieldBitOffset(struct_type, field_index) + struct_ptr_ty_info.packed_offset.bit_offset, 8); - if (byte_offset == 0) return struct_ptr; - const usize_ty = try o.lowerType(pt, Type.usize); - const llvm_index = try o.builder.intValue(usize_ty, byte_offset); - return self.wip.gep(.inbounds, .i8, struct_ptr, &.{llvm_index}, ""); - }, - else => { - const struct_llvm_ty = try o.lowerPtrElemTy(pt, struct_ty); - - if (o.llvmFieldIndex(struct_ty, field_index)) |llvm_field_index| { - return self.wip.gepStruct(struct_llvm_ty, struct_ptr, llvm_field_index, ""); - } else { - // If we found no index then this means this is a zero sized field at the - // end of the struct. Treat our struct pointer as an array of two and get - // the index to the element at index `1` to get a pointer to the end of - // the struct. - const llvm_index = try o.builder.intValue( - try o.lowerType(pt, Type.usize), - @intFromBool(struct_ty.hasRuntimeBitsIgnoreComptime(zcu)), - ); - return self.wip.gep(.inbounds, struct_llvm_ty, struct_ptr, &.{llvm_index}, ""); - } - }, + const aggregate_ty = aggregate_ptr_ty.childType(zcu); + if (aggregate_ty.containerLayout(zcu) == .@"packed") { + // A pointer to a bitpack field is equivalent to a pointer to the whole bitpack; the + // bit offset is represented in the pointer *type*. + return aggregate_ptr; + } + switch (aggregate_ty.zigTypeTag(zcu)) { + .@"struct" => { + if (!aggregate_ty.hasRuntimeBits(zcu)) { + return aggregate_ptr; + } + const struct_llvm_ty = try o.lowerType(pt, aggregate_ty); + if (o.llvmFieldIndex(aggregate_ty, field_index)) |llvm_field_index| { + return self.wip.gepStruct(struct_llvm_ty, aggregate_ptr, llvm_field_index, ""); + } else { + // If we found no index then this means this is a zero sized field at the + // end of the struct. Treat our struct pointer as an array of two and get + // the index to the element at index `1` to get a pointer to the end of + // the struct. + const llvm_index = try o.builder.intValue( + try o.lowerType(pt, Type.usize), + @intFromBool(aggregate_ty.hasRuntimeBits(zcu)), + ); + return self.wip.gep(.inbounds, struct_llvm_ty, aggregate_ptr, &.{llvm_index}, ""); + } }, .@"union" => { - const layout = struct_ty.unionGetLayout(zcu); - if (layout.payload_size == 0 or struct_ty.containerLayout(zcu) == .@"packed") return struct_ptr; - const payload_index = @intFromBool(layout.tag_align.compare(.gte, layout.payload_align)); - const union_llvm_ty = try o.lowerType(pt, struct_ty); - return self.wip.gepStruct(union_llvm_ty, struct_ptr, payload_index, ""); + const layout = aggregate_ty.unionGetLayout(zcu); + if (layout.payload_size == 0) return aggregate_ptr; + const payload_index = @intFromBool(layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align)); + const union_llvm_ty = try o.lowerType(pt, aggregate_ty); + return self.wip.gepStruct(union_llvm_ty, aggregate_ptr, payload_index, ""); }, else => unreachable, } @@ -11406,7 +11180,7 @@ pub const FuncGen = struct { const zcu = pt.zcu; const info = ptr_ty.ptrInfo(zcu); const elem_ty = Type.fromInterned(info.child); - if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) return .none; + if (!elem_ty.hasRuntimeBits(zcu)) return .none; const ptr_alignment = (if (info.flags.alignment != .none) @as(InternPool.Alignment, info.flags.alignment) @@ -11478,7 +11252,7 @@ pub const FuncGen = struct { const zcu = pt.zcu; const info = ptr_ty.ptrInfo(zcu); const elem_ty = Type.fromInterned(info.child); - if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) { + if (!elem_ty.hasRuntimeBits(zcu)) { return; } const ptr_alignment = ptr_ty.ptrAlignment(zcu).toLlvm(); @@ -12061,7 +11835,7 @@ fn returnTypeByRef(zcu: *Zcu, target: *const std.Target, ty: Type) bool { fn firstParamSRet(fn_info: InternPool.Key.FuncType, zcu: *Zcu, target: *const std.Target) bool { const return_type = Type.fromInterned(fn_info.return_type); - if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) return false; + if (!return_type.hasRuntimeBits(zcu)) return false; return switch (fn_info.cc) { .auto => returnTypeByRef(zcu, target, return_type), @@ -12101,11 +11875,9 @@ fn firstParamSRetSystemV(ty: Type, zcu: *Zcu, target: *const std.Target) bool { fn lowerFnRetTy(o: *Object, pt: Zcu.PerThread, fn_info: InternPool.Key.FuncType) Allocator.Error!Builder.Type { const zcu = pt.zcu; const return_type = Type.fromInterned(fn_info.return_type); - if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) { - // If the return type is an error set or an error union, then we make this - // anyerror return type instead, so that it can be coerced into a function - // pointer type which has anyerror as the return type. - return if (return_type.isError(zcu)) try o.errorIntType(pt) else .void; + if (!return_type.hasRuntimeBits(zcu)) { + assert(!return_type.isError(zcu)); + return .void; } const target = zcu.getTarget(); switch (fn_info.cc) { @@ -12149,7 +11921,7 @@ fn lowerFnRetTy(o: *Object, pt: Zcu.PerThread, fn_info: InternPool.Key.FuncType) var types: [8]Builder.Type = undefined; for (0..return_type.structFieldCount(zcu)) |field_index| { const field_ty = return_type.fieldType(field_index, zcu); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!field_ty.hasRuntimeBits(zcu)) continue; types[types_len] = try o.lowerType(pt, field_ty); types_len += 1; } @@ -12187,6 +11959,7 @@ fn lowerSystemVFnRetTy(o: *Object, pt: Zcu.PerThread, fn_info: InternPool.Key.Fu const zcu = pt.zcu; const ip = &zcu.intern_pool; const return_type = Type.fromInterned(fn_info.return_type); + return_type.assertHasLayout(zcu); if (isScalar(zcu, return_type)) { return o.lowerType(pt, return_type); } @@ -12235,9 +12008,7 @@ fn lowerSystemVFnRetTy(o: *Object, pt: Zcu.PerThread, fn_info: InternPool.Key.Fu assert(first_non_integer orelse classes.len == types_index); switch (ip.indexToKey(return_type.toIntern())) { .struct_type => { - const struct_type = ip.loadStructType(return_type.toIntern()); - assert(struct_type.haveLayout(ip)); - const size: u64 = struct_type.sizeUnordered(ip); + const size = return_type.abiSize(zcu); assert((std.math.divCeil(u64, size, 8) catch unreachable) == types_index); if (size % 8 > 0) { types_buffer[types_index - 1] = try o.builder.intType(@intCast(size % 8 * 8)); @@ -12273,7 +12044,7 @@ const ParamTypeIterator = struct { i64_array: u8, }; - pub fn next(it: *ParamTypeIterator) Allocator.Error!?Lowering { + fn next(it: *ParamTypeIterator) Allocator.Error!?Lowering { if (it.zig_index >= it.fn_info.param_types.len) return null; const ip = &it.pt.zcu.intern_pool; const ty = it.fn_info.param_types.get(ip)[it.zig_index]; @@ -12282,7 +12053,7 @@ const ParamTypeIterator = struct { } /// `airCall` uses this instead of `next` so that it can take into account variadic functions. - pub fn nextCall(it: *ParamTypeIterator, fg: *FuncGen, args: []const Air.Inst.Ref) Allocator.Error!?Lowering { + fn nextCall(it: *ParamTypeIterator, fg: *FuncGen, args: []const Air.Inst.Ref) Allocator.Error!?Lowering { assert(std.meta.eql(it.pt, fg.ng.pt)); const ip = &it.pt.zcu.intern_pool; if (it.zig_index >= it.fn_info.param_types.len) { @@ -12301,7 +12072,7 @@ const ParamTypeIterator = struct { const zcu = pt.zcu; const target = zcu.getTarget(); - if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!ty.hasRuntimeBits(zcu)) { it.zig_index += 1; return .no_bits; } @@ -12396,7 +12167,7 @@ const ParamTypeIterator = struct { it.types_len = 0; for (0..ty.structFieldCount(zcu)) |field_index| { const field_ty = ty.fieldType(field_index, zcu); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!field_ty.hasRuntimeBits(zcu)) continue; it.types_buffer[it.types_len] = try it.object.lowerType(pt, field_ty); it.types_len += 1; } @@ -12473,6 +12244,7 @@ const ParamTypeIterator = struct { fn nextSystemV(it: *ParamTypeIterator, ty: Type) Allocator.Error!?Lowering { const zcu = it.pt.zcu; const ip = &zcu.intern_pool; + ty.assertHasLayout(zcu); const classes = x86_64_abi.classifySystemV(ty, zcu, zcu.getTarget(), .arg); if (classes[0] == .memory) { it.zig_index += 1; @@ -12544,9 +12316,7 @@ const ParamTypeIterator = struct { } switch (ip.indexToKey(ty.toIntern())) { .struct_type => { - const struct_type = ip.loadStructType(ty.toIntern()); - assert(struct_type.haveLayout(ip)); - const size: u64 = struct_type.sizeUnordered(ip); + const size = ty.abiSize(zcu); assert((std.math.divCeil(u64, size, 8) catch unreachable) == types_index); if (size % 8 > 0) { types_buffer[types_index - 1] = @@ -12720,14 +12490,14 @@ fn isByRef(ty: Type, zcu: *Zcu) bool { }, .error_union => { const payload_ty = ty.errorUnionPayload(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { return false; } return true; }, .optional => { const payload_ty = ty.optionalChild(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { return false; } if (ty.optionalReprIsPayload(zcu)) { diff --git a/src/codegen/mips/abi.zig b/src/codegen/mips/abi.zig @@ -13,7 +13,7 @@ pub const Context = enum { ret, arg }; pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { const target = zcu.getTarget(); - std.debug.assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + std.debug.assert(ty.hasRuntimeBits(zcu)); const max_direct_size = target.ptrBitWidth() * 2; switch (ty.zigTypeTag(zcu)) { @@ -44,7 +44,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { return .byval; }, .vector => { - const elem_type = ty.elemType2(zcu); + const elem_type = ty.childType(zcu); switch (elem_type.zigTypeTag(zcu)) { .bool, .int => { const bit_size = ty.bitSize(zcu); diff --git a/src/codegen/riscv64/CodeGen.zig b/src/codegen/riscv64/CodeGen.zig @@ -2673,7 +2673,7 @@ fn genBinOp( defer func.register_manager.unlockReg(tmp_lock); // RISC-V has no immediate mul, so we copy the size to a temporary register - const elem_size = lhs_ty.elemType2(zcu).abiSize(zcu); + const elem_size = lhs_ty.indexableElem(zcu).abiSize(zcu); const elem_size_reg = try func.copyToTmpRegister(Type.u64, .{ .immediate = elem_size }); try func.genBinOp( @@ -3257,7 +3257,7 @@ fn airOptionalPayload(func: *Func, inst: Air.Inst.Index) !void { const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = result: { const pl_ty = func.typeOfIndex(inst); - if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .none; + if (!pl_ty.hasRuntimeBits(zcu)) break :result .none; const opt_mcv = try func.resolveInst(ty_op.operand); if (func.reuseOperand(inst, ty_op.operand, 0, opt_mcv)) { @@ -3331,7 +3331,7 @@ fn airUnwrapErrErr(func: *Func, inst: Air.Inst.Index) !void { break :result .{ .immediate = 0 }; } - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { break :result operand; } @@ -3384,7 +3384,7 @@ fn genUnwrapErrUnionPayloadMir( const payload_ty = err_union_ty.errorUnionPayload(zcu); const result: MCValue = result: { - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .none; + if (!payload_ty.hasRuntimeBits(zcu)) break :result .none; const payload_off: u31 = @intCast(errUnionPayloadOffset(payload_ty, zcu)); switch (err_union) { @@ -3547,7 +3547,7 @@ fn airWrapErrUnionPayload(func: *Func, inst: Air.Inst.Index) !void { const operand = try func.resolveInst(ty_op.operand); const result: MCValue = result: { - if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .{ .immediate = 0 }; + if (!pl_ty.hasRuntimeBits(zcu)) break :result .{ .immediate = 0 }; const frame_index = try func.allocFrameIndex(FrameAlloc.initSpill(eu_ty, zcu)); const pl_off: i32 = @intCast(errUnionPayloadOffset(pl_ty, zcu)); @@ -3571,7 +3571,7 @@ fn airWrapErrUnionErr(func: *Func, inst: Air.Inst.Index) !void { const err_ty = eu_ty.errorUnionSet(zcu); const result: MCValue = result: { - if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result try func.resolveInst(ty_op.operand); + if (!pl_ty.hasRuntimeBits(zcu)) break :result try func.resolveInst(ty_op.operand); const frame_index = try func.allocFrameIndex(FrameAlloc.initSpill(eu_ty, zcu)); const pl_off: i32 = @intCast(errUnionPayloadOffset(pl_ty, zcu)); @@ -3761,7 +3761,7 @@ fn airSliceElemVal(func: *Func, inst: Air.Inst.Index) !void { const result: MCValue = result: { const elem_ty = func.typeOfIndex(inst); - if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .none; + assert(elem_ty.hasRuntimeBits(zcu)); const slice_ty = func.typeOf(bin_op.lhs); const slice_ptr_field_type = slice_ty.slicePtrFieldType(zcu); @@ -3913,9 +3913,8 @@ fn airPtrElemVal(func: *Func, inst: Air.Inst.Index) !void { const base_ptr_ty = func.typeOf(bin_op.lhs); const result: MCValue = if (!is_volatile and func.liveness.isUnused(inst)) .unreach else result: { - const elem_ty = base_ptr_ty.elemType2(zcu); - if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .none; - + const elem_ty = base_ptr_ty.indexableElem(zcu); + assert(elem_ty.hasRuntimeBits(zcu)); const base_ptr_mcv = try func.resolveInst(bin_op.lhs); const base_ptr_lock: ?RegisterLock = switch (base_ptr_mcv) { .register => |reg| func.register_manager.lockRegAssumeUnused(reg), @@ -4618,7 +4617,7 @@ fn airStructFieldVal(func: *Func, inst: Air.Inst.Index) !void { const src_mcv = try func.resolveInst(operand); const struct_ty = func.typeOf(operand); const field_ty = struct_ty.fieldType(index, zcu); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .none; + assert(field_ty.hasRuntimeBits(zcu)); const field_off: u32 = switch (struct_ty.containerLayout(zcu)) { .auto, .@"extern" => @intCast(struct_ty.structFieldOffset(index, zcu) * 8), @@ -5127,7 +5126,6 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const pt = func.pt; const zcu = pt.zcu; - const ip = &zcu.intern_pool; const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { const lhs_ty = func.typeOf(bin_op.lhs); @@ -5141,28 +5139,23 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { .optional, .@"struct", => { - const int_ty = switch (lhs_ty.zigTypeTag(zcu)) { + const int_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) { .@"enum" => lhs_ty.intTagType(zcu), .int => lhs_ty, - .bool => Type.u1, - .pointer => Type.u64, - .error_set => Type.anyerror, + .bool => .u1, + .pointer => .u64, + .error_set => .anyerror, .optional => blk: { const payload_ty = lhs_ty.optionalChild(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - break :blk Type.u1; + if (!payload_ty.hasRuntimeBits(zcu)) { + break :blk .u1; } else if (lhs_ty.isPtrLikeOptional(zcu)) { - break :blk Type.u64; + break :blk .u64; } else { return func.fail("TODO riscv cmp non-pointer optionals", .{}); } }, - .@"struct" => blk: { - const struct_obj = ip.loadStructType(lhs_ty.toIntern()); - assert(struct_obj.layout == .@"packed"); - const backing_index = struct_obj.backingIntTypeUnordered(ip); - break :blk Type.fromInterned(backing_index); - }, + .@"struct", .@"union" => lhs_ty.bitpackBackingInt(zcu), else => unreachable, }; @@ -5926,8 +5919,7 @@ fn airBr(func: *Func, inst: Air.Inst.Index) !void { const br = func.air.instructions.items(.data)[@intFromEnum(inst)].br; const block_ty = func.typeOfIndex(br.block_inst); - const block_unused = - !block_ty.hasRuntimeBitsIgnoreComptime(zcu) or func.liveness.isUnused(br.block_inst); + const block_unused = !block_ty.hasRuntimeBits(zcu) or func.liveness.isUnused(br.block_inst); const block_tracking = func.inst_tracking.getPtr(br.block_inst).?; const block_data = func.blocks.getPtr(br.block_inst).?; const first_br = block_data.relocs.items.len == 0; @@ -6150,31 +6142,26 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { const zcu = func.pt.zcu; const ip = &zcu.intern_pool; - const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate; - const struct_type: Type = .fromInterned(aggregate.ty); - switch (aggregate.storage) { - .elems => |elems| for (elems, 0..) |elem, i| { - switch (elem) { - .bool_true => { - const clobber = struct_type.structFieldName(i, zcu).toSlice(ip).?; - assert(clobber.len != 0); - if (std.mem.eql(u8, clobber, "memory")) { - // nothing really to do - } else { - try func.register_manager.getReg(parseRegName(clobber) orelse - return func.fail("invalid clobber: '{s}'", .{clobber}), null); - } - }, - .bool_false => continue, - else => unreachable, - } - }, - .repeated_elem => |elem| switch (elem) { - .bool_true => @panic("TODO"), - .bool_false => {}, - else => unreachable, - }, - .bytes => @panic("TODO"), + const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers); + const clobbers_ty = clobbers_val.typeOf(zcu); + var clobbers_bigint_buf: Value.BigIntSpace = undefined; + const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu); + for (0..clobbers_ty.structFieldCount(zcu)) |field_index| { + assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type); + const limb_bits = @bitSizeOf(std.math.big.Limb); + if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false + switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) { + 0 => continue, // field is false + 1 => {}, // field is true + } + const clobber = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?; + assert(clobber.len != 0); + if (std.mem.eql(u8, clobber, "memory")) { + // nothing really to do + } else { + try func.register_manager.getReg(parseRegName(clobber) orelse + return func.fail("invalid clobber: '{s}'", .{clobber}), null); + } } const Label = struct { @@ -8255,7 +8242,7 @@ fn resolveCallingConventionValues( // Return values if (ret_ty.zigTypeTag(zcu) == .noreturn) { result.return_value = InstTracking.init(.unreach); - } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + } else if (!ret_ty.hasRuntimeBits(zcu)) { result.return_value = InstTracking.init(.none); } else { var ret_tracking: [2]InstTracking = undefined; @@ -8306,7 +8293,7 @@ fn resolveCallingConventionValues( var param_float_reg_i: usize = 0; for (param_types, result.args) |ty, *arg| { - if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!ty.hasRuntimeBits(zcu)) { assert(cc == .auto); arg.* = .none; continue; @@ -8421,10 +8408,10 @@ fn hasFeature(func: *Func, feature: Target.riscv.Feature) bool { } pub fn errUnionPayloadOffset(payload_ty: Type, zcu: *Zcu) u64 { - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) return 0; + if (!payload_ty.hasRuntimeBits(zcu)) return 0; const payload_align = payload_ty.abiAlignment(zcu); const error_align = Type.anyerror.abiAlignment(zcu); - if (payload_align.compare(.gte, error_align) or !payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (payload_align.compare(.gte, error_align) or !payload_ty.hasRuntimeBits(zcu)) { return 0; } else { return payload_align.forward(Type.anyerror.abiSize(zcu)); @@ -8432,10 +8419,10 @@ pub fn errUnionPayloadOffset(payload_ty: Type, zcu: *Zcu) u64 { } pub fn errUnionErrorOffset(payload_ty: Type, zcu: *Zcu) u64 { - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) return 0; + if (!payload_ty.hasRuntimeBits(zcu)) return 0; const payload_align = payload_ty.abiAlignment(zcu); const error_align = Type.anyerror.abiAlignment(zcu); - if (payload_align.compare(.gte, error_align) and payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (payload_align.compare(.gte, error_align) and payload_ty.hasRuntimeBits(zcu)) { return error_align.forward(payload_ty.abiSize(zcu)); } else { return 0; diff --git a/src/codegen/riscv64/abi.zig b/src/codegen/riscv64/abi.zig @@ -11,7 +11,7 @@ pub const Class = enum { memory, byval, integer, double_integer, fields }; pub fn classifyType(ty: Type, zcu: *Zcu) Class { const target = zcu.getTarget(); - std.debug.assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + std.debug.assert(ty.hasRuntimeBits(zcu)); const max_byval_size = target.ptrBitWidth() * 2; switch (ty.zigTypeTag(zcu)) { @@ -27,7 +27,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu) Class { var field_count: usize = 0; for (0..ty.structFieldCount(zcu)) |field_index| { const field_ty = ty.fieldType(field_index, zcu); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!field_ty.hasRuntimeBits(zcu)) continue; if (field_ty.isRuntimeFloat()) any_fp = true else if (!field_ty.isAbiInt(zcu)) diff --git a/src/codegen/sparc64/CodeGen.zig b/src/codegen/sparc64/CodeGen.zig @@ -1102,7 +1102,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void { try self.blocks.putNoClobber(self.gpa, inst, .{ // A block is a setup to be able to jump to the end. - .relocs = .{}, + .relocs = .empty, // It also acts as a receptacle for break operands. // Here we use `MCValue.none` to represent a null value so that the first // break instruction will choose a MCValue for the block result and overwrite @@ -1376,19 +1376,19 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { const rhs = try self.resolveInst(bin_op.rhs); const lhs_ty = self.typeOf(bin_op.lhs); - const int_ty = switch (lhs_ty.zigTypeTag(zcu)) { + const int_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) { .vector => unreachable, // Handled by cmp_vector. .@"enum" => lhs_ty.intTagType(zcu), .int => lhs_ty, - .bool => Type.u1, - .pointer => Type.usize, - .error_set => Type.u16, + .bool => .u1, + .pointer => .usize, + .error_set => .u16, .optional => blk: { const payload_ty = lhs_ty.optionalChild(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - break :blk Type.u1; + if (!payload_ty.hasRuntimeBits(zcu)) { + break :blk .u1; } else if (lhs_ty.isPtrLikeOptional(zcu)) { - break :blk Type.usize; + break :blk .usize; } else { return self.fail("TODO SPARCv9 cmp non-pointer optionals", .{}); } @@ -3452,8 +3452,8 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) if (err_ty.errorSetIsEmpty(zcu)) { return error_union_mcv; } - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - return MCValue.none; + if (!payload_ty.hasRuntimeBits(zcu)) { + return .none; } const payload_offset: u32 = @intCast(errUnionPayloadOffset(payload_ty, zcu)); @@ -4481,7 +4481,7 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!MCValue { const ty = self.typeOf(ref); // If the type has no codegen bits, no need to store it. - if (!ty.hasRuntimeBitsIgnoreComptime(pt.zcu)) return .none; + if (!ty.hasRuntimeBits(pt.zcu)) return .none; if (ref.toIndex()) |inst| { return self.getResolvedInstValue(inst); diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig @@ -208,7 +208,7 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void { try cg.args.ensureUnusedCapacity(gpa, fn_info.param_types.len); for (fn_info.param_types.get(ip)) |param_ty_index| { const param_ty: Type = .fromInterned(param_ty_index); - if (!param_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!param_ty.hasRuntimeBits(zcu)) continue; const param_type_id = try cg.resolveType(param_ty, .direct); const arg_result_id = cg.module.allocId(); @@ -689,7 +689,7 @@ fn constInt(cg: *CodeGen, ty: Type, value: anytype) !Id { .comptime_int => if (value < 0) .signed else .unsigned, else => unreachable, }; - if (@sizeOf(@TypeOf(value)) >= 4 and big_int) { + if (@TypeOf(value) != comptime_int and @sizeOf(@TypeOf(value)) >= 4 and big_int) { const value64: u64 = switch (signedness) { .signed => @bitCast(@as(i64, @intCast(value))), .unsigned => @as(u64, @intCast(value)), @@ -814,14 +814,11 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id { .@"extern", .func, .enum_literal, - .empty_enum_value, => unreachable, // non-runtime values .simple_value => |simple_value| switch (simple_value) { - .undefined, .void, .null, - .empty_tuple, .@"unreachable", => unreachable, // non-runtime values @@ -887,7 +884,7 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id { return try cg.constructComposite(comp_ty_id, &constituents); }, .enum_tag => { - const int_val = try val.intFromEnum(ty, pt); + const int_val = val.intFromEnum(zcu); const int_ty = ty.intTagType(zcu); break :cache try cg.constant(int_ty, int_val, repr); }, @@ -962,18 +959,7 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id { }, .struct_type => { const struct_type = zcu.typeToStruct(ty).?; - - if (struct_type.layout == .@"packed") { - // TODO: composite int - // TODO: endianness - const bits: u16 = @intCast(ty.bitSize(zcu)); - const bytes = std.mem.alignForward(u16, cg.module.backingIntBits(bits).@"0", 8) / 8; - var limbs: [8]u8 = undefined; - @memset(&limbs, 0); - val.writeToPackedMemory(ty, pt, limbs[0..bytes], 0) catch unreachable; - const backing_ty: Type = .fromInterned(struct_type.backingIntTypeUnordered(ip)); - return try cg.constInt(backing_ty, @as(u64, @bitCast(limbs))); - } + assert(struct_type.layout != .@"packed"); // packed structs use `bitpack` var types = std.array_list.Managed(Type).init(gpa); defer types.deinit(); @@ -984,7 +970,7 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id { var it = struct_type.iterateRuntimeOrder(ip); while (it.next()) |field_index| { const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!field_ty.hasRuntimeBits(zcu)) { // This is a zero-bit field - we only needed it for the alignment. continue; } @@ -1004,20 +990,24 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id { else => unreachable, }, .un => |un| { + assert(ty.containerLayout(zcu) != .@"packed"); // packed unions use `bitpack` if (un.tag == .none) { - assert(ty.containerLayout(zcu) == .@"packed"); // TODO - const int_ty = try pt.intType(.unsigned, @intCast(ty.bitSize(zcu))); - return try cg.constInt(int_ty, Value.toUnsignedInt(.fromInterned(un.val), zcu)); + @panic("TODO"); } const active_field = ty.unionTagFieldIndex(.fromInterned(un.tag), zcu).?; const union_obj = zcu.typeToUnion(ty).?; const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[active_field]); - const payload = if (field_ty.hasRuntimeBitsIgnoreComptime(zcu)) + const payload = if (field_ty.hasRuntimeBits(zcu)) try cg.constant(field_ty, .fromInterned(un.val), .direct) else null; return try cg.unionInit(ty, active_field, payload); }, + .bitpack => |bitpack| { + const int_val: Value = .fromInterned(bitpack.backing_int_val); + break :cache try cg.constant(int_val.typeOf(zcu), int_val, repr); + }, + .memoized_call => unreachable, } }; @@ -1041,7 +1031,7 @@ fn constantPtr(cg: *CodeGen, ptr_val: Value) !Id { var arena = std.heap.ArenaAllocator.init(gpa); defer arena.deinit(); - const derivation = try ptr_val.pointerDerivation(arena.allocator(), pt); + const derivation = try ptr_val.pointerDerivation(arena.allocator(), pt, null); return cg.derivePtr(derivation); } @@ -1150,7 +1140,7 @@ fn constantUavRef( } // const is_fn_body = decl_ty.zigTypeTag(zcu) == .@"fn"; - if (!uav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) { + if (!uav_ty.hasRuntimeBits(zcu)) { // Pointer to nothing - return undefined return cg.module.constUndef(ty_id); } @@ -1196,7 +1186,7 @@ fn constantNavRef(cg: *CodeGen, ty: Type, nav_index: InternPool.Nav.Index) !Id { }, } - if (!nav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) { + if (!nav_ty.hasRuntimeBits(zcu)) { // Pointer to nothing - return undefined. return cg.module.constUndef(ty_id); } @@ -1258,17 +1248,16 @@ fn resolveTypeName(cg: *CodeGen, ty: Type) ![]const u8 { fn resolveUnionType(cg: *CodeGen, ty: Type) !Id { const gpa = cg.module.gpa; const zcu = cg.module.zcu; - const ip = &zcu.intern_pool; const union_obj = zcu.typeToUnion(ty).?; - if (union_obj.flagsUnordered(ip).layout == .@"packed") { + if (union_obj.layout == .@"packed") { return try cg.module.intType(.unsigned, @intCast(ty.bitSize(zcu))); } const layout = cg.unionLayout(ty); if (!layout.has_payload) { // No payload, so represent this as just the tag type. - return try cg.resolveType(.fromInterned(union_obj.enum_tag_ty), .indirect); + return try cg.resolveType(.fromInterned(union_obj.enum_tag_type), .indirect); } var member_types: [4]Id = undefined; @@ -1277,7 +1266,7 @@ fn resolveUnionType(cg: *CodeGen, ty: Type) !Id { const u8_ty_id = try cg.resolveType(.u8, .direct); if (layout.tag_size != 0) { - const tag_ty_id = try cg.resolveType(.fromInterned(union_obj.enum_tag_ty), .indirect); + const tag_ty_id = try cg.resolveType(.fromInterned(union_obj.enum_tag_type), .indirect); member_types[layout.tag_index] = tag_ty_id; member_names[layout.tag_index] = "(tag)"; } @@ -1318,7 +1307,7 @@ fn resolveUnionType(cg: *CodeGen, ty: Type) !Id { fn resolveFnReturnType(cg: *CodeGen, ret_ty: Type) !Id { const zcu = cg.module.zcu; - if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!ret_ty.hasRuntimeBits(zcu)) { // If the return type is an error set or an error union, then we make this // anyerror return type instead, so that it can be coerced into a function // pointer type which has anyerror as the return type. @@ -1392,7 +1381,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { return cg.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel(zcu)}); }; - if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!elem_ty.hasRuntimeBits(zcu)) { assert(repr == .indirect); if (target.os.tag != .opencl) return cg.fail("cannot generate opaque type", .{}); return try cg.module.opaqueType("zero-sized-array"); @@ -1456,7 +1445,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { var param_index: usize = 0; for (fn_info.param_types.get(ip)) |param_ty_index| { const param_ty: Type = .fromInterned(param_ty_index); - if (!param_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!param_ty.hasRuntimeBits(zcu)) continue; param_ty_ids[param_index] = try cg.resolveType(param_ty, .direct); param_index += 1; @@ -1521,7 +1510,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { }; if (struct_type.layout == .@"packed") { - return try cg.resolveType(.fromInterned(struct_type.backingIntTypeUnordered(ip)), .direct); + return try cg.resolveType(.fromInterned(struct_type.packed_backing_int_type), .direct); } var member_types = std.array_list.Managed(Id).init(gpa); @@ -1536,9 +1525,9 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { var it = struct_type.iterateRuntimeOrder(ip); while (it.next()) |field_index| { const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!field_ty.hasRuntimeBits(zcu)) continue; - const field_name = struct_type.fieldName(ip, field_index); + const field_name = struct_type.field_names.get(ip)[field_index]; try member_types.append(try cg.resolveType(field_ty, .indirect)); try member_names.append(field_name.toSlice(ip)); try member_offsets.append(@intCast(ty.structFieldOffset(field_index, zcu))); @@ -1559,7 +1548,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { }, .optional => { const payload_ty = ty.optionalChild(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { // Just use a bool. // Note: Always generate the bool with indirect format, to save on some sanity // Perform the conversion to a direct bool when the field is extracted. @@ -1656,7 +1645,7 @@ fn errorUnionLayout(cg: *CodeGen, payload_ty: Type) ErrorUnionLayout { const error_first = error_align.compare(.gt, payload_align); return .{ - .payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(zcu), + .payload_has_bits = payload_ty.hasRuntimeBits(zcu), .error_first = error_first, }; } @@ -3727,7 +3716,6 @@ fn cmp( const gpa = cg.module.gpa; const pt = cg.pt; const zcu = cg.module.zcu; - const ip = &zcu.intern_pool; const scalar_ty = lhs.ty.scalarType(zcu); const is_vector = lhs.ty.isVector(zcu); @@ -3740,7 +3728,7 @@ fn cmp( }, .@"struct" => { const struct_ty = zcu.typeToPackedStruct(scalar_ty).?; - const ty: Type = .fromInterned(struct_ty.backingIntTypeUnordered(ip)); + const ty: Type = .fromInterned(struct_ty.packed_backing_int_type); return try cg.cmp(op, lhs.pun(ty), rhs.pun(ty)); }, .error_set => { @@ -3781,7 +3769,7 @@ fn cmp( const payload_ty = ty.optionalChild(zcu); if (ty.optionalReprIsPayload(zcu)) { - assert(payload_ty.hasRuntimeBitsIgnoreComptime(zcu)); + assert(payload_ty.hasRuntimeBits(zcu)); assert(!payload_ty.isSlice(zcu)); return try cg.cmp(op, lhs.pun(payload_ty), rhs.pun(payload_ty)); @@ -3790,12 +3778,12 @@ fn cmp( const lhs_id = try lhs.materialize(cg); const rhs_id = try rhs.materialize(cg); - const lhs_valid_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) + const lhs_valid_id = if (payload_ty.hasRuntimeBits(zcu)) try cg.extractField(.bool, lhs_id, 1) else try cg.convertToDirect(.bool, lhs_id); - const rhs_valid_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) + const rhs_valid_id = if (payload_ty.hasRuntimeBits(zcu)) try cg.extractField(.bool, rhs_id, 1) else try cg.convertToDirect(.bool, rhs_id); @@ -3803,7 +3791,7 @@ fn cmp( const lhs_valid: Temporary = .init(.bool, lhs_valid_id); const rhs_valid: Temporary = .init(.bool, rhs_valid_id); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { return try cg.cmp(op, lhs_valid, rhs_valid); } @@ -4141,7 +4129,7 @@ fn airArrayToSlice(cg: *CodeGen, inst: Air.Inst.Index) !?Id { const array_ptr_id = try cg.resolve(ty_op.operand); const len_id = try cg.constInt(.usize, array_ty.arrayLen(zcu)); - const elem_ptr_id = if (!array_ty.hasRuntimeBitsIgnoreComptime(zcu)) + const elem_ptr_id = if (!array_ty.hasRuntimeBits(zcu)) // Note: The pointer is something like *opaque{}, so we need to bitcast it to the element type. try cg.bitCast(elem_ptr_ty, array_ptr_ty, array_ptr_id) else @@ -4177,12 +4165,12 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) !?Id { .@"struct" => { if (zcu.typeToPackedStruct(result_ty)) |struct_type| { comptime assert(Type.packed_struct_layout_version == 2); - const backing_int_ty: Type = .fromInterned(struct_type.backingIntTypeUnordered(ip)); + const backing_int_ty: Type = .fromInterned(struct_type.packed_backing_int_type); var running_int_id = try cg.constInt(backing_int_ty, 0); var running_bits: u16 = 0; for (struct_type.field_types.get(ip), elements) |field_ty_ip, element| { const field_ty: Type = .fromInterned(field_ty_ip); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!field_ty.hasRuntimeBits(zcu)) continue; const field_id = try cg.resolve(element); const ty_bit_size: u16 = @intCast(field_ty.bitSize(zcu)); const field_int_ty = try cg.pt.intType(.unsigned, ty_bit_size); @@ -4242,7 +4230,7 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) !?Id { const field_index = it.next().?; if ((try result_ty.structFieldValueComptime(pt, i)) != null) continue; const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); - assert(field_ty.hasRuntimeBitsIgnoreComptime(zcu)); + assert(field_ty.hasRuntimeBits(zcu)); const id = try cg.resolve(element); types[index] = field_ty; @@ -4381,7 +4369,7 @@ fn airSliceElemVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id { fn ptrElemPtr(cg: *CodeGen, ptr_ty: Type, ptr_id: Id, index_id: Id) !Id { const zcu = cg.module.zcu; // Construct new pointer type for the resulting pointer - const elem_ty = ptr_ty.elemType2(zcu); // use elemType() so that we get T for *[N]T. + const elem_ty = ptr_ty.indexableElem(zcu); const elem_ty_id = try cg.resolveType(elem_ty, .indirect); const elem_ptr_ty_id = try cg.module.ptrType(elem_ty_id, cg.module.storageClass(ptr_ty.ptrAddressSpace(zcu))); if (ptr_ty.isSinglePointer(zcu)) { @@ -4402,10 +4390,7 @@ fn airPtrElemPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id { const elem_ty = src_ptr_ty.childType(zcu); const ptr_id = try cg.resolve(bin_op.lhs); - if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - const dst_ptr_ty = cg.typeOfIndex(inst); - return try cg.bitCast(dst_ptr_ty, src_ptr_ty, ptr_id); - } + assert(elem_ty.hasRuntimeBits(zcu)); const index_id = try cg.resolve(bin_op.rhs); return try cg.ptrElemPtr(src_ptr_ty, ptr_id, index_id); @@ -4483,7 +4468,7 @@ fn airSetUnionTag(cg: *CodeGen, inst: Air.Inst.Index) !void { if (layout.tag_size == 0) return; - const tag_ty = un_ty.unionTagTypeSafety(zcu).?; + const tag_ty = un_ty.unionTagTypeRuntime(zcu).?; const tag_ty_id = try cg.resolveType(tag_ty, .indirect); const tag_ptr_ty_id = try cg.module.ptrType(tag_ty_id, cg.module.storageClass(un_ptr_ty.ptrAddressSpace(zcu))); @@ -4509,7 +4494,7 @@ fn airGetUnionTag(cg: *CodeGen, inst: Air.Inst.Index) !?Id { const union_handle = try cg.resolve(ty_op.operand); if (!layout.has_payload) return union_handle; - const tag_ty = un_ty.unionTagTypeSafety(zcu).?; + const tag_ty = un_ty.unionTagTypeRuntime(zcu).?; return try cg.extractField(tag_ty, union_handle, layout.tag_index); } @@ -4529,39 +4514,16 @@ fn unionInit( const zcu = cg.module.zcu; const ip = &zcu.intern_pool; const union_ty = zcu.typeToUnion(ty).?; - const tag_ty: Type = .fromInterned(union_ty.enum_tag_ty); + const tag_ty: Type = .fromInterned(union_ty.enum_tag_type); const layout = cg.unionLayout(ty); const payload_ty: Type = .fromInterned(union_ty.field_types.get(ip)[active_field]); - if (union_ty.flagsUnordered(ip).layout == .@"packed") { - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - const int_ty = try pt.intType(.unsigned, @intCast(ty.bitSize(zcu))); - return cg.constInt(int_ty, 0); - } - - assert(payload != null); - if (payload_ty.isInt(zcu)) { - if (ty.bitSize(zcu) == payload_ty.bitSize(zcu)) { - return cg.bitCast(ty, payload_ty, payload.?); - } - - const trunc = try cg.buildConvert(ty, .{ .ty = payload_ty, .value = .{ .singleton = payload.? } }); - return try trunc.materialize(cg); - } - - const payload_int_ty = try pt.intType(.unsigned, @intCast(payload_ty.bitSize(zcu))); - const payload_int = if (payload_ty.ip_index == .bool_type) - try cg.convertToIndirect(payload_ty, payload.?) - else - try cg.bitCast(payload_int_ty, payload_ty, payload.?); - const trunc = try cg.buildConvert(ty, .{ .ty = payload_int_ty, .value = .{ .singleton = payload_int } }); - return try trunc.materialize(cg); - } + assert(union_ty.layout != .@"packed"); const tag_int = if (layout.tag_size != 0) blk: { const tag_val = try pt.enumValueFieldIndex(tag_ty, active_field); - const tag_int_val = try tag_val.intFromEnum(tag_ty, pt); + const tag_int_val = tag_val.intFromEnum(zcu); break :blk tag_int_val.toUnsignedInt(zcu); } else 0; @@ -4580,7 +4542,7 @@ fn unionInit( try cg.store(tag_ty, ptr_id, tag_id, .{}); } - if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (payload_ty.hasRuntimeBits(zcu)) { const layout_payload_ty_id = try cg.resolveType(layout.payload_ty, .indirect); const pl_ptr_ty_id = try cg.module.ptrType(layout_payload_ty_id, .function); const pl_ptr_id = try cg.accessChain(pl_ptr_ty_id, tmp_id, &.{layout.payload_index}); @@ -4616,7 +4578,7 @@ fn airUnionInit(cg: *CodeGen, inst: Air.Inst.Index) !?Id { const union_obj = zcu.typeToUnion(ty).?; const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[extra.field_index]); - const payload = if (field_ty.hasRuntimeBitsIgnoreComptime(zcu)) + const payload = if (field_ty.hasRuntimeBits(zcu)) try cg.resolve(extra.init) else null; @@ -4634,7 +4596,7 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id { const field_index = struct_field.field_index; const field_ty = object_ty.fieldType(field_index, zcu); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) return null; + assert(field_ty.hasRuntimeBits(zcu)); switch (object_ty.zigTypeTag(zcu)) { .@"struct" => switch (object_ty.containerLayout(zcu)) { @@ -4776,33 +4738,36 @@ fn structFieldPtr( }, .@"struct" => switch (object_ty.containerLayout(zcu)) { .@"packed" => return cg.todo("implement field access for packed structs", .{}), - else => { + .auto, .@"extern" => { return try cg.accessChain(result_ty_id, object_ptr, &.{field_index}); }, }, - .@"union" => { - const layout = cg.unionLayout(object_ty); - if (!layout.has_payload) { - // Asked to get a pointer to a zero-sized field. Just lower this - // to undefined, there is no reason to make it be a valid pointer. - return try cg.module.constUndef(result_ty_id); - } + .@"union" => switch (object_ty.containerLayout(zcu)) { + .@"packed" => return cg.todo("implement field access for packed unions", .{}), + .auto, .@"extern" => { + const layout = cg.unionLayout(object_ty); + if (!layout.has_payload) { + // Asked to get a pointer to a zero-sized field. Just lower this + // to undefined, there is no reason to make it be a valid pointer. + return try cg.module.constUndef(result_ty_id); + } - const storage_class = cg.module.storageClass(object_ptr_ty.ptrAddressSpace(zcu)); - const layout_payload_ty_id = try cg.resolveType(layout.payload_ty, .indirect); - const pl_ptr_ty_id = try cg.module.ptrType(layout_payload_ty_id, storage_class); - const pl_ptr_id = blk: { - if (object_ty.containerLayout(zcu) == .@"packed") break :blk object_ptr; - break :blk try cg.accessChain(pl_ptr_ty_id, object_ptr, &.{layout.payload_index}); - }; + const storage_class = cg.module.storageClass(object_ptr_ty.ptrAddressSpace(zcu)); + const layout_payload_ty_id = try cg.resolveType(layout.payload_ty, .indirect); + const pl_ptr_ty_id = try cg.module.ptrType(layout_payload_ty_id, storage_class); + const pl_ptr_id = blk: { + if (object_ty.containerLayout(zcu) == .@"packed") break :blk object_ptr; + break :blk try cg.accessChain(pl_ptr_ty_id, object_ptr, &.{layout.payload_index}); + }; - const active_pl_ptr_id = cg.module.allocId(); - try cg.body.emit(cg.module.gpa, .OpBitcast, .{ - .id_result_type = result_ty_id, - .id_result = active_pl_ptr_id, - .operand = pl_ptr_id, - }); - return active_pl_ptr_id; + const active_pl_ptr_id = cg.module.allocId(); + try cg.body.emit(cg.module.gpa, .OpBitcast, .{ + .id_result_type = result_ty_id, + .id_result = active_pl_ptr_id, + .operand = pl_ptr_id, + }); + return active_pl_ptr_id; + }, }, else => unreachable, } @@ -5028,7 +4993,7 @@ fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, body: []const Air.Inst.Index) const gpa = cg.module.gpa; const zcu = cg.module.zcu; const ty = cg.typeOfIndex(inst); - const have_block_result = ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu); + const have_block_result = ty.hasRuntimeBits(zcu); const cf = switch (cg.control_flow) { .structured => |*cf| cf, @@ -5166,7 +5131,7 @@ fn airBr(cg: *CodeGen, inst: Air.Inst.Index) !void { switch (cg.control_flow) { .structured => |*cf| { - if (operand_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) { + if (operand_ty.hasRuntimeBits(zcu)) { const operand_id = try cg.resolve(br.operand); const block_result_var_id = cf.block_results.get(br.block_inst).?; try cg.store(operand_ty, block_result_var_id, operand_id, .{}); @@ -5177,7 +5142,7 @@ fn airBr(cg: *CodeGen, inst: Air.Inst.Index) !void { }, .unstructured => |cf| { const block = cf.blocks.get(br.block_inst).?; - if (operand_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) { + if (operand_ty.hasRuntimeBits(zcu)) { const operand_id = try cg.resolve(br.operand); // block_label should not be undefined here, lest there // is a br or br_void in the function's body. @@ -5335,7 +5300,7 @@ fn airRet(cg: *CodeGen, inst: Air.Inst.Index) !void { const zcu = cg.module.zcu; const operand = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const ret_ty = cg.typeOf(operand); - if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!ret_ty.hasRuntimeBits(zcu)) { const fn_info = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?; if (Type.fromInterned(fn_info.return_type).isError(zcu)) { // Functions with an empty error set are emitted with an error code @@ -5359,7 +5324,7 @@ fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) !void { const ptr_ty = cg.typeOf(un_op); const ret_ty = ptr_ty.childType(zcu); - if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!ret_ty.hasRuntimeBits(zcu)) { const fn_info = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?; if (Type.fromInterned(fn_info.return_type).isError(zcu)) { // Functions with an empty error set are emitted with an error code @@ -5576,7 +5541,7 @@ fn airIsNull(cg: *CodeGen, inst: Air.Inst.Index, is_pointer: bool, pred: enum { const is_non_null_id = blk: { if (is_pointer) { - if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (payload_ty.hasRuntimeBits(zcu)) { const storage_class = cg.module.storageClass(operand_ty.ptrAddressSpace(zcu)); const bool_indirect_ty_id = try cg.resolveType(.bool, .indirect); const bool_ptr_ty_id = try cg.module.ptrType(bool_indirect_ty_id, storage_class); @@ -5587,7 +5552,7 @@ fn airIsNull(cg: *CodeGen, inst: Air.Inst.Index, is_pointer: bool, pred: enum { break :blk try cg.load(.bool, operand_id, .{}); } - break :blk if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) + break :blk if (payload_ty.hasRuntimeBits(zcu)) try cg.extractField(.bool, operand_id, 1) else // Optional representation is bool indicating whether the optional is set @@ -5656,7 +5621,7 @@ fn airUnwrapOptional(cg: *CodeGen, inst: Air.Inst.Index) !?Id { const optional_ty = cg.typeOf(ty_op.operand); const payload_ty = cg.typeOfIndex(inst); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) return null; + if (!payload_ty.hasRuntimeBits(zcu)) return null; if (optional_ty.optionalReprIsPayload(zcu)) { return operand_id; @@ -5675,7 +5640,7 @@ fn airUnwrapOptionalPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id { const result_ty = cg.typeOfIndex(inst); const result_ty_id = try cg.resolveType(result_ty, .direct); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { // There is no payload, but we still need to return a valid pointer. // We can just return anything here, so just return a pointer to the operand. return try cg.bitCast(result_ty, operand_ty, operand_id); @@ -5694,9 +5659,7 @@ fn airWrapOptional(cg: *CodeGen, inst: Air.Inst.Index) !?Id { const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const payload_ty = cg.typeOf(ty_op.operand); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - return try cg.constBool(true, .indirect); - } + assert(payload_ty.hasRuntimeBits(zcu)); const operand_id = try cg.resolve(ty_op.operand); @@ -5792,8 +5755,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void { const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) { .bool, .int => if (cond_ty.isSignedInt(zcu)) @bitCast(value.toSignedInt(zcu)) else value.toUnsignedInt(zcu), .@"enum" => blk: { - // TODO: figure out of cond_ty is correct (something with enum literals) - break :blk (try value.intFromEnum(cond_ty, pt)).toUnsignedInt(zcu); // TODO: composite integer constants + break :blk value.intFromEnum(zcu).toUnsignedInt(zcu); // TODO: composite integer constants }, .error_set => value.getErrorInt(zcu), .pointer => value.toUnsignedInt(zcu), @@ -6070,7 +6032,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie // before starting to emit OpFunctionCall instructions. Hence the // temporary params buffer. const arg_ty = cg.typeOf(arg); - if (!arg_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!arg_ty.hasRuntimeBits(zcu)) continue; const arg_id = try cg.resolve(arg); params[n_params] = arg_id; @@ -6084,7 +6046,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie .id_ref_3 = params[0..n_params], }); - if (cg.liveness.isUnused(inst) or !Type.fromInterned(return_type).hasRuntimeBitsIgnoreComptime(zcu)) { + if (cg.liveness.isUnused(inst) or !Type.fromInterned(return_type).hasRuntimeBits(zcu)) { return null; } diff --git a/src/codegen/wasm/CodeGen.zig b/src/codegen/wasm/CodeGen.zig @@ -759,7 +759,7 @@ fn resolveInst(cg: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue { const zcu = pt.zcu; const val = (try cg.air.value(ref, pt)).?; const ty = cg.typeOf(ref); - if (!ty.hasRuntimeBitsIgnoreComptime(zcu) and !ty.isInt(zcu) and !ty.isError(zcu)) { + if (!ty.hasRuntimeBits(zcu) and !ty.isInt(zcu) and !ty.isError(zcu)) { gop.value_ptr.* = .none; return .none; } @@ -773,7 +773,7 @@ fn resolveInst(cg: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue { const result: WValue = if (isByRef(ty, zcu, cg.target)) .{ .uav_ref = .{ .ip_index = val.toIntern() } } else - try cg.lowerConstant(val, ty); + try cg.lowerConstant(val); gop.value_ptr.* = result; return result; @@ -786,7 +786,7 @@ fn resolveValue(cg: *CodeGen, val: Value) InnerError!WValue { return if (isByRef(ty, zcu, cg.target)) .{ .uav_ref = .{ .ip_index = val.toIntern() } } else - try cg.lowerConstant(val, ty); + try cg.lowerConstant(val); } /// NOTE: if result == .stack, it will be stored in .local @@ -980,7 +980,6 @@ fn addExtraAssumeCapacity(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 { /// For `std.builtin.CallingConvention.auto`. pub fn typeToValtype(ty: Type, zcu: *const Zcu, target: *const std.Target) std.wasm.Valtype { - const ip = &zcu.intern_pool; return switch (ty.zigTypeTag(zcu)) { .float => switch (ty.floatBits(target)) { 16 => .i32, // stored/loaded as u16 @@ -994,25 +993,13 @@ pub fn typeToValtype(ty: Type, zcu: *const Zcu, target: *const std.Target) std.w 33...64 => .i64, else => .i32, }, - .@"struct" => blk: { - if (zcu.typeToPackedStruct(ty)) |packed_struct| { - const backing_int_ty = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)); - break :blk typeToValtype(backing_int_ty, zcu, target); - } else { - break :blk .i32; - } - }, .vector => switch (CodeGen.determineSimdStoreStrategy(ty, zcu, target)) { .direct => .v128, .unrolled => .i32, }, - .@"union" => switch (ty.containerLayout(zcu)) { - .@"packed" => switch (ty.bitSize(zcu)) { - 0...32 => .i32, - 33...64 => .i64, - else => .i32, - }, - else => .i32, + .@"union", .@"struct" => switch (ty.containerLayout(zcu)) { + .@"packed" => typeToValtype(ty.bitpackBackingInt(zcu), zcu, target), + .auto, .@"extern" => .i32, }, else => .i32, // all represented as reference/immediate }; @@ -1185,7 +1172,7 @@ pub fn generate( const fn_ty = zcu.navValue(cg.owner_nav).typeOf(zcu); const fn_info = zcu.typeToFunc(fn_ty).?; const ret_ty: Type = .fromInterned(fn_info.return_type); - const any_returns = !firstParamSRet(fn_info.cc, ret_ty, zcu, target) and ret_ty.hasRuntimeBitsIgnoreComptime(zcu); + const any_returns = !firstParamSRet(fn_info.cc, ret_ty, zcu, target) and ret_ty.hasRuntimeBits(zcu); var cc_result = try resolveCallingConventionValues(zcu, fn_ty, target); defer cc_result.deinit(gpa); @@ -1244,7 +1231,7 @@ fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir { if (any_returns and cg.air.instructions.len > 0) { const inst: Air.Inst.Index = @enumFromInt(cg.air.instructions.len - 1); const last_inst_ty = cg.typeOfIndex(inst); - if (!last_inst_ty.hasRuntimeBitsIgnoreComptime(zcu) or last_inst_ty.isNoReturn(zcu)) { + if (!last_inst_ty.hasRuntimeBits(zcu)) { try cg.addTag(.@"unreachable"); } } @@ -1316,7 +1303,7 @@ fn resolveCallingConventionValues( switch (cc) { .auto => { for (fn_info.param_types.get(ip)) |ty| { - if (!Type.fromInterned(ty).hasRuntimeBitsIgnoreComptime(zcu)) { + if (!Type.fromInterned(ty).hasRuntimeBits(zcu)) { continue; } @@ -1326,7 +1313,7 @@ fn resolveCallingConventionValues( }, .wasm_mvp => { for (fn_info.param_types.get(ip)) |ty| { - if (!Type.fromInterned(ty).hasRuntimeBitsIgnoreComptime(zcu)) { + if (!Type.fromInterned(ty).hasRuntimeBits(zcu)) { continue; } switch (abi.classifyType(.fromInterned(ty), zcu)) { @@ -1357,7 +1344,7 @@ pub fn firstParamSRet( zcu: *const Zcu, target: *const std.Target, ) bool { - if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) return false; + if (!return_type.hasRuntimeBits(zcu)) return false; switch (cc) { .@"inline" => unreachable, .auto => return isByRef(return_type, zcu, target), @@ -1457,7 +1444,7 @@ fn restoreStackPointer(cg: *CodeGen) !void { fn allocStack(cg: *CodeGen, ty: Type) !WValue { const pt = cg.pt; const zcu = pt.zcu; - assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + assert(ty.hasRuntimeBits(zcu)); if (cg.initial_stack_value == .none) { try cg.initializeStack(); } @@ -1491,7 +1478,7 @@ fn allocStackPtr(cg: *CodeGen, inst: Air.Inst.Index) !WValue { try cg.initializeStack(); } - if (!pointee_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!pointee_ty.hasRuntimeBits(zcu)) { return cg.allocStack(Type.usize); // create a value containing just the stack pointer. } @@ -1676,7 +1663,6 @@ fn ptrSize(cg: *const CodeGen) u16 { /// For a given `Type`, will return true when the type will be passed /// by reference, rather than by value fn isByRef(ty: Type, zcu: *const Zcu, target: *const std.Target) bool { - const ip = &zcu.intern_pool; switch (ty.zigTypeTag(zcu)) { .type, .comptime_int, @@ -1697,20 +1683,10 @@ fn isByRef(ty: Type, zcu: *const Zcu, target: *const std.Target) bool { .array, .frame, - => return ty.hasRuntimeBitsIgnoreComptime(zcu), - .@"union" => { - if (zcu.typeToUnion(ty)) |union_obj| { - if (union_obj.flagsUnordered(ip).layout == .@"packed") { - return ty.abiSize(zcu) > 8; - } - } - return ty.hasRuntimeBitsIgnoreComptime(zcu); - }, - .@"struct" => { - if (zcu.typeToPackedStruct(ty)) |packed_struct| { - return isByRef(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)), zcu, target); - } - return ty.hasRuntimeBitsIgnoreComptime(zcu); + => return ty.hasRuntimeBits(zcu), + .@"struct", .@"union" => switch (ty.containerLayout(zcu)) { + .@"packed" => return isByRef(ty.bitpackBackingInt(zcu), zcu, target), + .@"extern", .auto => return ty.hasRuntimeBits(zcu), }, .vector => return determineSimdStoreStrategy(ty, zcu, target) == .unrolled, .int => return ty.intInfo(zcu).bits > 64, @@ -1718,7 +1694,7 @@ fn isByRef(ty: Type, zcu: *const Zcu, target: *const std.Target) bool { .float => return ty.floatBits(target) > 64, .error_union => { const pl_ty = ty.errorUnionPayload(zcu); - if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!pl_ty.hasRuntimeBits(zcu)) { return false; } return true; @@ -1727,7 +1703,7 @@ fn isByRef(ty: Type, zcu: *const Zcu, target: *const std.Target) bool { if (ty.isPtrLikeOptional(zcu)) return false; const pl_type = ty.optionalChild(zcu); if (pl_type.zigTypeTag(zcu) == .error_set) return false; - return pl_type.hasRuntimeBitsIgnoreComptime(zcu); + return pl_type.hasRuntimeBits(zcu); }, .pointer => { // Slices act like struct and will be passed by reference @@ -2069,7 +2045,7 @@ fn airRet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { // to the stack instead if (cg.return_value != .none) { try cg.store(cg.return_value, operand, ret_ty, 0); - } else if (fn_info.cc == .wasm_mvp and ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + } else if (fn_info.cc == .wasm_mvp and ret_ty.hasRuntimeBits(zcu)) { switch (abi.classifyType(ret_ty, zcu)) { .direct => |scalar_type| { assert(!abi.lowerAsDoubleI64(scalar_type, zcu)); @@ -2082,7 +2058,7 @@ fn airRet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { .indirect => unreachable, } } else { - if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and ret_ty.isError(zcu)) { + if (!ret_ty.hasRuntimeBits(zcu) and ret_ty.isError(zcu)) { try cg.addImm32(0); } else { try cg.emitWValue(operand); @@ -2099,7 +2075,7 @@ fn airRetPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { const child_type = cg.typeOfIndex(inst).childType(zcu); const result = result: { - if (!child_type.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) { + if (!child_type.hasRuntimeBits(zcu)) { break :result try cg.allocStack(Type.usize); // create pointer to void } @@ -2121,7 +2097,7 @@ fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { const ret_ty = cg.typeOf(un_op).childType(zcu); const fn_info = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?; - if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!ret_ty.hasRuntimeBits(zcu)) { if (ret_ty.isError(zcu)) { try cg.addImm32(0); } @@ -2177,7 +2153,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie const arg_val = try cg.resolveInst(arg); const arg_ty = cg.typeOf(arg); - if (!arg_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!arg_ty.hasRuntimeBits(zcu)) continue; try cg.lowerArg(zcu.typeToFunc(fn_ty).?.cc, arg_ty, arg_val); } @@ -2199,10 +2175,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie } const result_value = result_value: { - if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu) and !ret_ty.isError(zcu)) { - break :result_value .none; - } else if (ret_ty.isNoReturn(zcu)) { - try cg.addTag(.@"unreachable"); + if (!ret_ty.hasRuntimeBits(zcu) and !ret_ty.isError(zcu)) { break :result_value .none; } else if (first_param_sret) { break :result_value sret; @@ -2323,12 +2296,12 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr const zcu = pt.zcu; const abi_size = ty.abiSize(zcu); - if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return; + if (!ty.hasRuntimeBits(zcu)) return; switch (ty.zigTypeTag(zcu)) { .error_union => { const pl_ty = ty.errorUnionPayload(zcu); - if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!pl_ty.hasRuntimeBits(zcu)) { return cg.store(lhs, rhs, Type.anyerror, offset); } @@ -2341,7 +2314,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr return cg.store(lhs, rhs, Type.usize, offset); } const pl_ty = ty.optionalChild(zcu); - if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!pl_ty.hasRuntimeBits(zcu)) { return cg.store(lhs, rhs, Type.u8, offset); } if (pl_ty.zigTypeTag(zcu) == .error_set) { @@ -2441,7 +2414,7 @@ fn airLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { const ptr_ty = cg.typeOf(ty_op.operand); const ptr_info = ptr_ty.ptrInfo(zcu); - if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return cg.finishAir(inst, .none, &.{ty_op.operand}); + if (!ty.hasRuntimeBits(zcu)) return cg.finishAir(inst, .none, &.{ty_op.operand}); const result = result: { if (isByRef(ty, zcu, cg.target)) { @@ -3092,7 +3065,7 @@ fn lowerPtr(cg: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerErro return switch (ptr.base_addr) { .nav => |nav| return .{ .nav_ref = .{ .nav_index = nav, .offset = @intCast(offset) } }, .uav => |uav| return .{ .uav_ref = .{ .ip_index = uav.val, .offset = @intCast(offset), .orig_ptr_ty = uav.orig_ty } }, - .int => return cg.lowerConstant(try pt.intValue(Type.usize, offset), Type.usize), + .int => return cg.lowerConstant(try pt.intValue(.usize, offset)), .eu_payload => |eu_ptr| try cg.lowerPtr( eu_ptr, offset + codegen.errUnionPayloadOffset( @@ -3129,10 +3102,11 @@ fn lowerPtr(cg: *CodeGen, ptr_val: InternPool.Index, prev_offset: u64) InnerErro }; } -/// Asserts that `isByRef` returns `false` for `ty`. -fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue { +/// Asserts that `isByRef` returns `false` for `val.typeOf(zcu)`. +fn lowerConstant(cg: *CodeGen, val: Value) InnerError!WValue { const pt = cg.pt; const zcu = pt.zcu; + const ty = val.typeOf(zcu); assert(!isByRef(ty, zcu, cg.target)); const ip = &zcu.intern_pool; if (val.isUndef(zcu)) return cg.emitUndefined(ty); @@ -3158,10 +3132,8 @@ fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue { .undef => unreachable, // handled above .simple_value => |simple_value| switch (simple_value) { - .undefined, .void, .null, - .empty_tuple, .@"unreachable", => unreachable, // non-runtime values .false, .true => return .{ .imm32 = switch (simple_value) { @@ -3174,7 +3146,6 @@ fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue { .@"extern", .func, .enum_literal, - .empty_enum_value, => unreachable, // non-runtime values .int => { const int_info = ty.intInfo(zcu); @@ -3197,31 +3168,22 @@ fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue { }, .error_union => |error_union| { const err_int_ty = try pt.errorIntType(); - const err_ty, const err_val = switch (error_union.val) { - .err_name => |err_name| .{ - ty.errorUnionSet(zcu), - Value.fromInterned(try pt.intern(.{ .err = .{ - .ty = ty.errorUnionSet(zcu).toIntern(), - .name = err_name, - } })), - }, - .payload => .{ - err_int_ty, - try pt.intValue(err_int_ty, 0), - }, + const err_val: Value = switch (error_union.val) { + .err_name => |err_name| .fromInterned(try pt.intern(.{ .err = .{ + .ty = ty.errorUnionSet(zcu).toIntern(), + .name = err_name, + } })), + .payload => try pt.intValue(err_int_ty, 0), }; const payload_type = ty.errorUnionPayload(zcu); - if (!payload_type.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_type.hasRuntimeBits(zcu)) { // We use the error type directly as the type. - return cg.lowerConstant(err_val, err_ty); + return cg.lowerConstant(err_val); } return cg.fail("Wasm TODO: lowerConstant error union with non-zero-bit payload type", .{}); }, - .enum_tag => |enum_tag| { - const int_tag_ty = ip.typeOf(enum_tag.int); - return cg.lowerConstant(Value.fromInterned(enum_tag.int), Type.fromInterned(int_tag_ty)); - }, + .enum_tag => |enum_tag| return cg.lowerConstant(.fromInterned(enum_tag.int)), .float => |float| switch (float.storage) { .f16 => |f16_val| return .{ .imm32 = @as(u16, @bitCast(f16_val)) }, .f32 => |f32_val| return .{ .float32 = f32_val }, @@ -3231,9 +3193,8 @@ fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue { .slice => unreachable, // isByRef == true .ptr => return cg.lowerPtr(val.toIntern(), 0), .opt => if (ty.optionalReprIsPayload(zcu)) { - const pl_ty = ty.optionalChild(zcu); if (val.optionalValue(zcu)) |payload| { - return cg.lowerConstant(payload, pl_ty); + return cg.lowerConstant(payload); } else { return .{ .imm32 = 0 }; } @@ -3248,33 +3209,11 @@ fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue { val.writeToMemory(pt, &buf) catch unreachable; return cg.storeSimdImmd(buf); }, - .struct_type => { - const struct_type = ip.loadStructType(ty.toIntern()); - // non-packed structs are not handled in this function because they - // are by-ref types. - assert(struct_type.layout == .@"packed"); - var buf: [8]u8 = .{0} ** 8; // zero the buffer so we do not read 0xaa as integer - val.writeToPackedMemory(ty, pt, &buf, 0) catch unreachable; - const backing_int_ty = Type.fromInterned(struct_type.backingIntTypeUnordered(ip)); - const int_val = try pt.intValue( - backing_int_ty, - mem.readInt(u64, &buf, .little), - ); - return cg.lowerConstant(int_val, backing_int_ty); - }, + .struct_type => unreachable, // packed structs use `bitpack` else => unreachable, }, - .un => { - const int_type = try pt.intType(.unsigned, @intCast(ty.bitSize(zcu))); - - var buf: [8]u8 = .{0} ** 8; // zero the buffer so we do not read 0xaa as integer - val.writeToPackedMemory(ty, pt, &buf, 0) catch unreachable; - const int_val = try pt.intValue( - int_type, - mem.readInt(u64, &buf, .little), - ); - return cg.lowerConstant(int_val, int_type); - }, + .un => unreachable, // packed unions use `bitpack` + .bitpack => |bitpack| return cg.lowerConstant(.fromInterned(bitpack.backing_int_val)), .memoized_call => unreachable, } } @@ -3289,7 +3228,6 @@ fn storeSimdImmd(cg: *CodeGen, value: [16]u8) !WValue { fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue { const zcu = cg.pt.zcu; - const ip = &zcu.intern_pool; switch (ty.zigTypeTag(zcu)) { .bool, .error_set => return .{ .imm32 = 0xaaaaaaaa }, .int, .@"enum" => switch (ty.intInfo(zcu).bits) { @@ -3317,17 +3255,9 @@ fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue { .error_union => { return .{ .imm32 = 0xaaaaaaaa }; }, - .@"struct" => { - const packed_struct = zcu.typeToPackedStruct(ty).?; - return cg.emitUndefined(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip))); - }, - .@"union" => switch (ty.containerLayout(zcu)) { - .@"packed" => switch (ty.bitSize(zcu)) { - 0...32 => return .{ .imm32 = 0xaaaaaaaa }, - 33...64 => return .{ .imm64 = 0xaaaaaaaaaaaaaaaa }, - else => unreachable, - }, - else => unreachable, + .@"struct", .@"union" => { + const backing_int_ty = ty.bitpackBackingInt(zcu); + return cg.emitUndefined(backing_int_ty); }, else => return cg.fail("Wasm TODO: emitUndefined for type: {t}\n", .{ty.zigTypeTag(zcu)}), } @@ -3341,7 +3271,7 @@ fn airBlock(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const Air.Inst.Index) InnerError!void { const zcu = cg.pt.zcu; // if wasm_block_ty is non-empty, we create a register to store the temporary value - const block_result: WValue = if (block_ty.hasRuntimeBitsIgnoreComptime(zcu)) + const block_result: WValue = if (block_ty.hasRuntimeBits(zcu)) try cg.allocLocal(block_ty) else .none; @@ -3455,7 +3385,7 @@ fn cmp(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOpe const zcu = cg.pt.zcu; if (ty.zigTypeTag(zcu) == .optional and !ty.optionalReprIsPayload(zcu)) { const payload_ty = ty.optionalChild(zcu); - if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (payload_ty.hasRuntimeBits(zcu)) { // When we hit this case, we must check the value of optionals // that are not pointers. This means first checking against non-null for // both lhs and rhs, as well as checking the payload are matching of lhs and rhs @@ -3798,7 +3728,6 @@ fn structFieldPtr( fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { const pt = cg.pt; const zcu = pt.zcu; - const ip = &zcu.intern_pool; const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const struct_field = cg.air.extraData(Air.StructField, ty_pl.payload).data; @@ -3806,14 +3735,14 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { const operand = try cg.resolveInst(struct_field.struct_operand); const field_index = struct_field.field_index; const field_ty = struct_ty.fieldType(field_index, zcu); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) return cg.finishAir(inst, .none, &.{struct_field.struct_operand}); + if (!field_ty.hasRuntimeBits(zcu)) return cg.finishAir(inst, .none, &.{struct_field.struct_operand}); const result: WValue = switch (struct_ty.containerLayout(zcu)) { .@"packed" => switch (struct_ty.zigTypeTag(zcu)) { .@"struct" => result: { const packed_struct = zcu.typeToPackedStruct(struct_ty).?; const offset = zcu.structPackedFieldBitOffset(packed_struct, field_index); - const backing_ty = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)); + const backing_ty = Type.fromInterned(packed_struct.packed_backing_int_type); const host_bits = backing_ty.intInfo(zcu).bits; const const_wvalue: WValue = if (33 <= host_bits and host_bits <= 64) @@ -3891,7 +3820,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner const switch_br = cg.air.unwrapSwitch(inst); const target_ty = cg.typeOf(switch_br.operand); - assert(target_ty.hasRuntimeBitsIgnoreComptime(zcu)); + assert(target_ty.hasRuntimeBits(zcu)); // swap target value with placeholder local, for dispatching const target = if (is_dispatch_loop) target: { @@ -4125,7 +4054,7 @@ fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode, op_kind } try cg.emitWValue(operand); - if (op_kind == .ptr or pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (op_kind == .ptr or pl_ty.hasRuntimeBits(zcu)) { try cg.addMemArg(.i32_load16_u, .{ .offset = operand.offset() + @as(u32, @intCast(errUnionErrorOffset(pl_ty, zcu))), .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?), @@ -4152,7 +4081,7 @@ fn airUnwrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) const payload_ty = eu_ty.errorUnionPayload(zcu); const result: WValue = result: { - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { if (op_is_ptr) { break :result cg.reuseOperand(ty_op.operand, operand); } else { @@ -4172,7 +4101,7 @@ fn airUnwrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) } /// E!T -> E op_is_ptr == false -/// *(E!T) -> E op_is_prt == true +/// *(E!T) -> E op_is_ptr == true /// NOTE: op_is_ptr will not change return type fn airUnwrapErrUnionError(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void { const zcu = cg.pt.zcu; @@ -4192,7 +4121,7 @@ fn airUnwrapErrUnionError(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) I if (op_is_ptr or isByRef(eu_ty, zcu, cg.target)) { break :result try cg.load(operand, Type.anyerror, err_offset); } else { - assert(!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)); + assert(!payload_ty.hasRuntimeBits(zcu)); break :result cg.reuseOperand(ty_op.operand, operand); } }; @@ -4208,7 +4137,7 @@ fn airWrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { const pl_ty = cg.typeOf(ty_op.operand); const result = result: { - if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!pl_ty.hasRuntimeBits(zcu)) { break :result cg.reuseOperand(ty_op.operand, operand); } @@ -4238,7 +4167,7 @@ fn airWrapErrUnionErr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { const pl_ty = err_ty.errorUnionPayload(zcu); const result = result: { - if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!pl_ty.hasRuntimeBits(zcu)) { break :result cg.reuseOperand(ty_op.operand, operand); } @@ -4354,7 +4283,7 @@ fn isNull(cg: *CodeGen, operand: WValue, optional_ty: Type, opcode: std.wasm.Opc if (!optional_ty.optionalReprIsPayload(zcu)) { // When payload is zero-bits, we can treat operand as a value, rather than // a pointer to the stack value - if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (payload_ty.hasRuntimeBits(zcu)) { const offset = std.math.cast(u32, payload_ty.abiSize(zcu)) orelse { return cg.fail("Optional type {f} too big to fit into stack frame", .{optional_ty.fmt(pt)}); }; @@ -4379,7 +4308,7 @@ fn airOptionalPayload(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const opt_ty = cg.typeOf(ty_op.operand); const payload_ty = cg.typeOfIndex(inst); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { return cg.finishAir(inst, .none, &.{ty_op.operand}); } @@ -4404,7 +4333,7 @@ fn airOptionalPayloadPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { const result = result: { const payload_ty = opt_ty.optionalChild(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu) or opt_ty.optionalReprIsPayload(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu) or opt_ty.optionalReprIsPayload(zcu)) { break :result cg.reuseOperand(ty_op.operand, operand); } @@ -4444,7 +4373,7 @@ fn airWrapOptional(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { const zcu = pt.zcu; const result = result: { - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { const non_null_bit = try cg.allocStack(Type.u1); try cg.emitWValue(non_null_bit); try cg.addImm32(1); @@ -4612,7 +4541,7 @@ fn airArrayToSlice(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { const slice_local = try cg.allocStack(slice_ty); // store the array ptr in the slice - if (array_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (array_ty.hasRuntimeBits(zcu)) { try cg.store(slice_local, operand, Type.usize, 0); } @@ -5111,7 +5040,7 @@ fn airShuffleOne(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { try cg.emitWValue(dest_alloc); const elem_val = switch (mask_elem.unwrap()) { .elem => |idx| try cg.load(operand, elem_ty, @intCast(elem_size * idx)), - .value => |val| try cg.lowerConstant(.fromInterned(val), elem_ty), + .value => |val| try cg.lowerConstant(.fromInterned(val)), }; try cg.store(.stack, elem_val, elem_ty, @intCast(dest_alloc.offset() + elem_size * out_idx)); } @@ -5252,7 +5181,7 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { } const packed_struct = zcu.typeToPackedStruct(result_ty).?; const field_types = packed_struct.field_types; - const backing_type = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)); + const backing_type = Type.fromInterned(packed_struct.packed_backing_int_type); // ensure the result is zero'd const result = try cg.allocLocal(backing_type); @@ -5265,7 +5194,7 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { var current_bit: u16 = 0; for (elements, 0..) |elem, elem_index| { const field_ty = Type.fromInterned(field_types.get(ip)[elem_index]); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!field_ty.hasRuntimeBits(zcu)) continue; const shift_val: WValue = if (backing_type.bitSize(zcu) <= 32) .{ .imm32 = current_bit } @@ -5338,13 +5267,13 @@ fn airUnionInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { const layout = union_ty.unionGetLayout(zcu); const union_obj = zcu.typeToUnion(union_ty).?; const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]); - const field_name = union_obj.loadTagType(ip).names.get(ip)[extra.field_index]; + const field_name = ip.loadEnumType(union_obj.enum_tag_type).field_names.get(ip)[extra.field_index]; const tag_int = blk: { const tag_ty = union_ty.unionTagTypeHypothetical(zcu); const enum_field_index = tag_ty.enumFieldIndex(field_name, zcu).?; const tag_val = try pt.enumValueFieldIndex(tag_ty, enum_field_index); - break :blk try cg.lowerConstant(tag_val, tag_ty); + break :blk try cg.lowerConstant(tag_val); }; if (layout.payload_size == 0) { if (layout.tag_size == 0) { @@ -5366,7 +5295,7 @@ fn airUnionInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { } if (layout.tag_size > 0) { - try cg.store(result_ptr, tag_int, Type.fromInterned(union_obj.enum_tag_ty), 0); + try cg.store(result_ptr, tag_int, .fromInterned(union_obj.enum_tag_type), 0); } } else { try cg.store(result_ptr, payload, field_ty, 0); @@ -5374,7 +5303,7 @@ fn airUnionInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { try cg.store( result_ptr, tag_int, - Type.fromInterned(union_obj.enum_tag_ty), + .fromInterned(union_obj.enum_tag_type), @intCast(layout.payload_size), ); } @@ -5421,7 +5350,7 @@ fn airWasmMemoryGrow(cg: *CodeGen, inst: Air.Inst.Index) !void { fn cmpOptionals(cg: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { const zcu = cg.pt.zcu; - assert(operand_ty.hasRuntimeBitsIgnoreComptime(zcu)); + assert(operand_ty.hasRuntimeBits(zcu)); assert(op == .eq or op == .neq); const payload_ty = operand_ty.optionalChild(zcu); assert(!isByRef(payload_ty, zcu, cg.target)); @@ -5675,7 +5604,7 @@ fn airErrUnionPayloadPtrSet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void ); const result = result: { - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { break :result cg.reuseOperand(ty_op.operand, operand); } @@ -6464,7 +6393,7 @@ fn lowerTry( const zcu = cg.pt.zcu; const pl_ty = err_union_ty.errorUnionPayload(zcu); - const pl_has_bits = pl_ty.hasRuntimeBitsIgnoreComptime(zcu); + const pl_has_bits = pl_ty.hasRuntimeBits(zcu); if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) { // Block we can jump out of when error is not set @@ -7102,16 +7031,13 @@ fn callIntrinsic( // Lower all arguments to the stack before we call our function for (args, 0..) |arg, arg_i| { assert(!(want_sret_param and arg == .stack)); - assert(Type.fromInterned(param_types[arg_i]).hasRuntimeBitsIgnoreComptime(zcu)); + assert(Type.fromInterned(param_types[arg_i]).hasRuntimeBits(zcu)); try cg.lowerArg(.{ .wasm_mvp = .{} }, Type.fromInterned(param_types[arg_i]), arg); } try cg.addInst(.{ .tag = .call_intrinsic, .data = .{ .intrinsic = intrinsic } }); - if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) { - return .none; - } else if (return_type.isNoReturn(zcu)) { - try cg.addTag(.@"unreachable"); + if (!return_type.hasRuntimeBits(zcu)) { return .none; } else if (want_sret_param) { return sret; diff --git a/src/codegen/wasm/abi.zig b/src/codegen/wasm/abi.zig @@ -22,7 +22,7 @@ pub const Class = union(enum) { /// or returned as value within a wasm function. pub fn classifyType(ty: Type, zcu: *const Zcu) Class { const ip = &zcu.intern_pool; - assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + assert(ty.hasRuntimeBits(zcu)); switch (ty.zigTypeTag(zcu)) { .int, .@"enum", .error_set => return .{ .direct = ty }, .float => return .{ .direct = ty }, @@ -47,7 +47,7 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) Class { return .indirect; } const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[0]); - const explicit_align = struct_type.fieldAlign(ip, 0); + const explicit_align = struct_type.field_aligns.getOrNone(ip, 0); if (explicit_align != .none) { if (explicit_align.compareStrict(.gt, field_ty.abiAlignment(zcu))) return .indirect; @@ -56,7 +56,7 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) Class { }, .@"union" => { const union_obj = zcu.typeToUnion(ty).?; - if (union_obj.flagsUnordered(ip).layout == .@"packed") { + if (union_obj.layout == .@"packed") { return .{ .direct = ty }; } const layout = ty.unionGetLayout(zcu); diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig @@ -43261,7 +43261,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }); try ops[0].toSlicePtr(cg); var res: [1]Temp = undefined; - if (!hack_around_sema_opv_bugs or ty_pl.ty.toType().elemType2(zcu).hasRuntimeBitsIgnoreComptime(zcu)) cg.select(&res, &.{ty_pl.ty.toType()}, &ops, comptime &.{ .{ + if (!hack_around_sema_opv_bugs or ty_pl.ty.toType().childType(zcu).hasRuntimeBits(zcu)) cg.select(&res, &.{ty_pl.ty.toType()}, &ops, comptime &.{ .{ .patterns = &.{ .{ .src = .{ .to_gpr, .simm32, .none } }, }, @@ -43375,7 +43375,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }); try ops[0].toSlicePtr(cg); var res: [1]Temp = undefined; - if (!hack_around_sema_opv_bugs or ty_pl.ty.toType().elemType2(zcu).hasRuntimeBitsIgnoreComptime(zcu)) cg.select(&res, &.{ty_pl.ty.toType()}, &ops, comptime &.{ .{ + if (!hack_around_sema_opv_bugs or ty_pl.ty.toType().childType(zcu).hasRuntimeBits(zcu)) cg.select(&res, &.{ty_pl.ty.toType()}, &ops, comptime &.{ .{ .patterns = &.{ .{ .src = .{ .to_gpr, .simm32, .none } }, }, @@ -103699,7 +103699,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .optional_payload => { const ty_op = air_datas[@intFromEnum(inst)].ty_op; var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); - const pl = if (!hack_around_sema_opv_bugs or ty_op.ty.toType().hasRuntimeBitsIgnoreComptime(zcu)) + const pl = if (!hack_around_sema_opv_bugs or ty_op.ty.toType().hasRuntimeBits(zcu)) try ops[0].read(ty_op.ty.toType(), .{}, cg) else try cg.tempInit(ty_op.ty.toType(), .none); @@ -103745,7 +103745,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { const eu_pl_ty = ty_op.ty.toType(); const eu_pl_off: i32 = @intCast(codegen.errUnionPayloadOffset(eu_pl_ty, zcu)); var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); - const pl = if (!hack_around_sema_opv_bugs or eu_pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) + const pl = if (!hack_around_sema_opv_bugs or eu_pl_ty.hasRuntimeBits(zcu)) try ops[0].read(eu_pl_ty, .{ .disp = eu_pl_off }, cg) else try cg.tempInit(eu_pl_ty, .none); @@ -103864,7 +103864,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .@"packed" => unreachable, }; var ops = try cg.tempsFromOperands(inst, .{struct_field.struct_operand}); - var res = if (!hack_around_sema_opv_bugs or field_ty.hasRuntimeBitsIgnoreComptime(zcu)) + var res = if (!hack_around_sema_opv_bugs or field_ty.hasRuntimeBits(zcu)) try ops[0].read(field_ty, .{ .disp = field_off }, cg) else try cg.tempInit(field_ty, .none); @@ -103926,7 +103926,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .array_elem_val, .legalize_vec_elem_val => { const bin_op = air_datas[@intFromEnum(inst)].bin_op; const array_ty = cg.typeOf(bin_op.lhs); - const res_ty = array_ty.elemType2(zcu); + const res_ty = array_ty.childType(zcu); var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }); var res: [1]Temp = undefined; cg.select(&res, &.{res_ty}, &ops, comptime &.{ .{ @@ -104121,11 +104121,11 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .slice_elem_val, .ptr_elem_val => { const bin_op = air_datas[@intFromEnum(inst)].bin_op; - const res_ty = cg.typeOf(bin_op.lhs).elemType2(zcu); + const res_ty = cg.typeOf(bin_op.lhs).indexableElem(zcu); var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }); try ops[0].toSlicePtr(cg); var res: [1]Temp = undefined; - if (!hack_around_sema_opv_bugs or res_ty.hasRuntimeBitsIgnoreComptime(zcu)) cg.select(&res, &.{res_ty}, &ops, comptime &.{ .{ + if (!hack_around_sema_opv_bugs or res_ty.hasRuntimeBits(zcu)) cg.select(&res, &.{res_ty}, &ops, comptime &.{ .{ .dst_constraints = .{ .{ .int = .byte }, .any }, .patterns = &.{ .{ .src = .{ .to_gpr, .simm32, .none } }, @@ -171422,10 +171422,10 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { .auto, .@"extern" => { for (elems, 0..) |elem_ref, field_index| { const elem_dies = bt.feed(); - if (loaded_struct.fieldIsComptime(ip, field_index)) continue; - if (!hack_around_sema_opv_bugs or Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]).hasRuntimeBitsIgnoreComptime(zcu)) { + if (loaded_struct.field_is_comptime_bits.get(ip, field_index)) continue; + if (!hack_around_sema_opv_bugs or Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]).hasRuntimeBits(zcu)) { var elem = try cg.tempFromOperand(elem_ref, elem_dies); - try res.write(&elem, .{ .disp = @intCast(loaded_struct.offsets.get(ip)[field_index]) }, cg); + try res.write(&elem, .{ .disp = @intCast(loaded_struct.field_offsets.get(ip)[field_index]) }, cg); try elem.die(cg); try cg.resetTemps(reset_index); } @@ -171441,7 +171441,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { const elem_dies = bt.feed(); if (tuple_type.values.get(ip)[field_index] != .none) continue; const field_type = Type.fromInterned(tuple_type.types.get(ip)[field_index]); - if (!hack_around_sema_opv_bugs or field_type.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!hack_around_sema_opv_bugs or field_type.hasRuntimeBits(zcu)) { elem_disp = @intCast(field_type.abiAlignment(zcu).forward(elem_disp)); var elem = try cg.tempFromOperand(elem_ref, elem_dies); try res.write(&elem, .{ .disp = elem_disp }, cg); @@ -171467,7 +171467,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { const union_layout = union_ty.unionGetLayout(zcu); if (union_layout.tag_size > 0) { var tag_temp = try cg.tempFromValue(try pt.enumValueFieldIndex( - union_ty.unionTagTypeSafety(zcu).?, + union_ty.unionTagTypeRuntime(zcu).?, union_init.field_index, )); try res.write(&tag_temp, .{ @@ -173756,7 +173756,7 @@ fn genLazy(cg: *CodeGen, lazy_sym: link.File.LazySymbol) InnerError!void { var data_off: i32 = 0; const reset_index = cg.next_temp_index; - const tag_names = ip.loadEnumType(lazy_sym.ty).names; + const tag_names = ip.loadEnumType(lazy_sym.ty).field_names; for (0..tag_names.len) |tag_index| { var enum_temp = try cg.tempInit(enum_ty, if (enum_ty.abiSize(zcu) <= @as(u4, switch (cg.target.cpu.arch) { else => unreachable, @@ -174334,7 +174334,7 @@ fn genUnwrapErrUnionPayloadMir( const payload_ty = err_union_ty.errorUnionPayload(zcu); const result: MCValue = result: { - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .none; + if (!payload_ty.hasRuntimeBits(zcu)) break :result .none; const payload_off: u31 = @intCast(codegen.errUnionPayloadOffset(payload_ty, zcu)); switch (err_union) { @@ -174450,7 +174450,7 @@ fn load(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerE const pt = self.pt; const zcu = pt.zcu; const dst_ty = ptr_ty.childType(zcu); - if (!dst_ty.hasRuntimeBitsIgnoreComptime(zcu)) return; + if (!dst_ty.hasRuntimeBits(zcu)) return; switch (ptr_mcv) { .none, .unreach, @@ -174503,7 +174503,7 @@ fn store( const pt = self.pt; const zcu = pt.zcu; const src_ty = ptr_ty.childType(zcu); - if (!src_ty.hasRuntimeBitsIgnoreComptime(zcu)) return; + if (!src_ty.hasRuntimeBits(zcu)) return; switch (ptr_mcv) { .none, .unreach, @@ -176615,7 +176615,7 @@ fn lowerSwitchBr( break :condition_index condition_index; }; try cg.spillEflagsIfOccupied(); - if (min.?.orderAgainstZero(zcu).compare(.neq)) try cg.genBinOpMir( + if (Value.compareHetero(min.?, .neq, .zero_comptime_int, zcu)) try cg.genBinOpMir( .{ ._, .sub }, condition_ty, condition_index, @@ -176957,7 +176957,7 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void { const unsigned_condition_ty = try self.pt.intType(.unsigned, self.intInfo(condition_ty).?.bits); const condition_mcv = block_tracking.short; try self.spillEflagsIfOccupied(); - if (table.min.orderAgainstZero(self.pt.zcu).compare(.neq)) try self.genBinOpMir( + if (Value.compareHetero(table.min, .neq, .zero_comptime_int, self.pt.zcu)) try self.genBinOpMir( .{ ._, .sub }, condition_ty, condition_mcv, @@ -177054,8 +177054,7 @@ fn airBr(self: *CodeGen, inst: Air.Inst.Index) !void { const br = self.air.instructions.items(.data)[@intFromEnum(inst)].br; const block_ty = self.typeOfIndex(br.block_inst); - const block_unused = - !block_ty.hasRuntimeBitsIgnoreComptime(zcu) or self.liveness.isUnused(br.block_inst); + const block_unused = !block_ty.hasRuntimeBits(zcu) or self.liveness.isUnused(br.block_inst); const block_tracking = self.inst_tracking.getPtr(br.block_inst).?; const block_data = self.blocks.getPtr(br.block_inst).?; const first_br = block_data.relocs.items.len == 0; @@ -177295,41 +177294,38 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { } const ip = &zcu.intern_pool; - const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate; - const struct_type: Type = .fromInterned(aggregate.ty); - switch (aggregate.storage) { - .elems => |elems| for (elems, 0..) |elem, i| switch (elem) { - .bool_true => { - const clobber = struct_type.structFieldName(i, zcu).toSlice(ip).?; - assert(clobber.len != 0); - - if (std.mem.eql(u8, clobber, "memory") or - std.mem.eql(u8, clobber, "fpsr") or - std.mem.eql(u8, clobber, "fpcr") or - std.mem.eql(u8, clobber, "mxcsr") or - std.mem.eql(u8, clobber, "dirflag")) - { - // ok, sure - } else if (std.mem.eql(u8, clobber, "cc") or - std.mem.eql(u8, clobber, "flags") or - std.mem.eql(u8, clobber, "eflags") or - std.mem.eql(u8, clobber, "rflags")) - { - try self.spillEflagsIfOccupied(); - } else { - try self.register_manager.getReg(parseRegName(clobber) orelse - return self.fail("invalid clobber: '{s}'", .{clobber}), null); - } - }, - .bool_false => continue, - else => unreachable, - }, - .repeated_elem => |elem| switch (elem) { - .bool_true => @panic("TODO"), - .bool_false => {}, - else => unreachable, - }, - .bytes => @panic("TODO"), + const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers); + const clobbers_ty = clobbers_val.typeOf(zcu); + var clobbers_bigint_buf: Value.BigIntSpace = undefined; + const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu); + for (0..clobbers_ty.structFieldCount(zcu)) |field_index| { + assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type); + const limb_bits = @bitSizeOf(std.math.big.Limb); + if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false + switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) { + 0 => continue, // field is false + 1 => {}, // field is true + } + const clobber = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?; + assert(clobber.len != 0); + + if (std.mem.eql(u8, clobber, "memory") or + std.mem.eql(u8, clobber, "fpsr") or + std.mem.eql(u8, clobber, "fpcr") or + std.mem.eql(u8, clobber, "mxcsr") or + std.mem.eql(u8, clobber, "dirflag")) + { + // ok, sure + } else if (std.mem.eql(u8, clobber, "cc") or + std.mem.eql(u8, clobber, "flags") or + std.mem.eql(u8, clobber, "eflags") or + std.mem.eql(u8, clobber, "rflags")) + { + try self.spillEflagsIfOccupied(); + } else { + try self.register_manager.getReg(parseRegName(clobber) orelse + return self.fail("invalid clobber: '{s}'", .{clobber}), null); + } } const Label = struct { @@ -180986,7 +180982,7 @@ fn resolveInst(self: *CodeGen, ref: Air.Inst.Ref) InnerError!MCValue { const ty = self.typeOf(ref); // If the type has no codegen bits, no need to store it. - if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return .none; + if (!ty.hasRuntimeBits(zcu)) return .none; const mcv: MCValue = if (ref.toIndex()) |inst| mcv: { break :mcv self.inst_tracking.getPtr(inst).?.short; @@ -181105,7 +181101,7 @@ fn resolveCallingConventionValues( // Return values if (ret_ty.isNoReturn(zcu)) { result.return_value = .init(.unreach); - } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + } else if (!ret_ty.hasRuntimeBits(zcu)) { // TODO: is this even possible for C calling convention? result.return_value = .init(.none); } else { @@ -181115,7 +181111,7 @@ fn resolveCallingConventionValues( var ret_sse = abi.getCAbiSseReturnRegs(cc); var ret_x87 = abi.getCAbiX87ReturnRegs(cc); - const classes = switch (cc) { + const classes: []const abi.Class = switch (cc) { .x86_64_sysv => std.mem.sliceTo(&abi.classifySystemV(ret_ty, zcu, cg.target, .ret), .none), .x86_64_win => &.{abi.classifyWindows(ret_ty, zcu, cg.target, .ret)}, else => unreachable, @@ -181182,7 +181178,7 @@ fn resolveCallingConventionValues( // Input params params: for (param_types, result.args) |ty, *arg| { - assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + assert(ty.hasRuntimeBits(zcu)); result.air_arg_count += 1; switch (cc) { .x86_64_sysv => {}, @@ -181327,7 +181323,7 @@ fn resolveCallingConventionValues( // Return values result.return_value = if (ret_ty.isNoReturn(zcu)) .init(.unreach) - else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) + else if (!ret_ty.hasRuntimeBits(zcu)) .init(.none) else return_value: { const ret_gpr = abi.getCAbiIntReturnRegs(cc); @@ -181357,7 +181353,7 @@ fn resolveCallingConventionValues( // Input params for (param_types, result.args) |param_ty, *arg| { - if (!param_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!param_ty.hasRuntimeBits(zcu)) { arg.* = .none; continue; } @@ -181721,7 +181717,7 @@ fn intInfo(cg: *CodeGen, ty: Type) ?std.builtin.Type.Int { .one, .many, .c => .{ .signedness = .unsigned, .bits = cg.target.ptrBitWidth() }, .slice => null, }, - .opt_type => |opt_child| return if (!Type.fromInterned(opt_child).hasRuntimeBitsIgnoreComptime(zcu)) + .opt_type => |opt_child| return if (!Type.fromInterned(opt_child).hasRuntimeBits(zcu)) .{ .signedness = .unsigned, .bits = 1 } else switch (ip.indexToKey(opt_child)) { .ptr_type => |ptr_type| switch (ptr_type.flags.size) { @@ -181734,7 +181730,7 @@ fn intInfo(cg: *CodeGen, ty: Type) ?std.builtin.Type.Int { else => null, }, .error_union_type => |error_union_type| return if (!Type.fromInterned(error_union_type.payload_type) - .hasRuntimeBitsIgnoreComptime(zcu)) .{ .signedness = .unsigned, .bits = zcu.errorSetBits() } else null, + .hasRuntimeBits(zcu)) .{ .signedness = .unsigned, .bits = zcu.errorSetBits() } else null, .simple_type => |simple_type| return switch (simple_type) { .bool => .{ .signedness = .unsigned, .bits = 1 }, .anyerror => .{ .signedness = .unsigned, .bits = zcu.errorSetBits() }, @@ -181767,14 +181763,17 @@ fn intInfo(cg: *CodeGen, ty: Type) ?std.builtin.Type.Int { const loaded_struct = ip.loadStructType(ty_index); switch (loaded_struct.layout) { .auto, .@"extern" => return null, - .@"packed" => ty_index = loaded_struct.backingIntTypeUnordered(ip), + .@"packed" => ty_index = loaded_struct.packed_backing_int_type, } }, - .union_type => return switch (ip.loadUnionType(ty_index).flagsUnordered(ip).layout) { - .auto, .@"extern" => null, - .@"packed" => .{ .signedness = .unsigned, .bits = @intCast(ty.bitSize(zcu)) }, + .union_type => { + const loaded_union = ip.loadUnionType(ty_index); + switch (loaded_union.layout) { + .auto, .@"extern" => return null, + .@"packed" => ty_index = loaded_union.packed_backing_int_type, + } }, - .enum_type => ty_index = ip.loadEnumType(ty_index).tag_ty, + .enum_type => ty_index = ip.loadEnumType(ty_index).int_tag_type, .error_set_type, .inferred_error_set_type => return .{ .signedness = .unsigned, .bits = zcu.errorSetBits() }, else => return null, }; @@ -187919,7 +187918,6 @@ const Select = struct { unsigned_int: Memory.Size, elem_size_is: u8, po2_elem_size, - elem_int: Memory.Size, const OfIsSizes = struct { of: Memory.Size, is: Memory.Size }; @@ -188178,12 +188176,8 @@ const Select = struct { .signed => false, .unsigned => size.bitSize(cg.target) >= int_info.bits, } else false, - .elem_size_is => |size| size == ty.elemType2(zcu).abiSize(zcu), - .po2_elem_size => std.math.isPowerOfTwo(ty.elemType2(zcu).abiSize(zcu)), - .elem_int => |size| if (cg.intInfo(ty.elemType2(zcu))) |elem_int_info| - size.bitSize(cg.target) >= elem_int_info.bits - else - false, + .elem_size_is => |size| size == ty.indexableElem(zcu).abiSize(zcu), + .po2_elem_size => std.math.isPowerOfTwo(ty.indexableElem(zcu).abiSize(zcu)), }; } }; @@ -189918,20 +189912,20 @@ const Select = struct { .dst0_size => @intCast(Select.Operand.Ref.dst0.typeOf(s).abiSize(s.cg.pt.zcu)), .delta_size => @intCast(@as(SignedImm, @intCast(op.flags.base.ref.typeOf(s).abiSize(s.cg.pt.zcu))) - @as(SignedImm, @intCast(op.flags.index.ref.typeOf(s).abiSize(s.cg.pt.zcu)))), - .delta_elem_size => @intCast(@as(SignedImm, @intCast(op.flags.base.ref.typeOf(s).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu))) - - @as(SignedImm, @intCast(op.flags.index.ref.typeOf(s).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)))), + .delta_elem_size => @intCast(@as(SignedImm, @intCast(op.flags.base.ref.typeOf(s).scalarType(s.cg.pt.zcu).abiSize(s.cg.pt.zcu))) - + @as(SignedImm, @intCast(op.flags.index.ref.typeOf(s).scalarType(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)))), .unaligned_size => @intCast(s.cg.unalignedSize(op.flags.base.ref.typeOf(s))), .unaligned_size_add_elem_size => { const ty = op.flags.base.ref.typeOf(s); - break :lhs @intCast(s.cg.unalignedSize(ty) + ty.elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)); + break :lhs @intCast(s.cg.unalignedSize(ty) + ty.scalarType(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)); }, .unaligned_size_sub_elem_size => { const ty = op.flags.base.ref.typeOf(s); - break :lhs @intCast(s.cg.unalignedSize(ty) - ty.elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)); + break :lhs @intCast(s.cg.unalignedSize(ty) - ty.scalarType(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)); }, .unaligned_size_sub_2_elem_size => { const ty = op.flags.base.ref.typeOf(s); - break :lhs @intCast(s.cg.unalignedSize(ty) - ty.elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu) * 2); + break :lhs @intCast(s.cg.unalignedSize(ty) - ty.scalarType(s.cg.pt.zcu).abiSize(s.cg.pt.zcu) * 2); }, .bit_size => @intCast(s.cg.nonBoolScalarBitSize(op.flags.base.ref.typeOf(s))), .src0_bit_size => @intCast(s.cg.nonBoolScalarBitSize(Select.Operand.Ref.src0.typeOf(s))), @@ -189944,10 +189938,10 @@ const Select = struct { op.flags.base.ref.typeOf(s).scalarType(s.cg.pt.zcu).abiSize(s.cg.pt.zcu), @divExact(op.flags.base.size.bitSize(s.cg.target), 8), )), - .elem_size => @intCast(op.flags.base.ref.typeOf(s).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)), - .src0_elem_size => @intCast(Select.Operand.Ref.src0.typeOf(s).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)), - .dst0_elem_size => @intCast(Select.Operand.Ref.dst0.typeOf(s).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)), - .src0_elem_size_mul_src1 => @intCast(Select.Operand.Ref.src0.typeOf(s).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu) * + .elem_size => @intCast(op.flags.base.ref.typeOf(s).indexableElem(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)), + .src0_elem_size => @intCast(Select.Operand.Ref.src0.typeOf(s).indexableElem(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)), + .dst0_elem_size => @intCast(Select.Operand.Ref.dst0.typeOf(s).indexableElem(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)), + .src0_elem_size_mul_src1 => @intCast(Select.Operand.Ref.src0.typeOf(s).indexableElem(s.cg.pt.zcu).abiSize(s.cg.pt.zcu) * Select.Operand.Ref.src1.valueOf(s).immediate), .vector_index => switch (op.flags.base.ref.typeOf(s).ptrInfo(s.cg.pt.zcu).flags.vector_index) { .none => unreachable, @@ -189956,7 +189950,7 @@ const Select = struct { .src1 => @intCast(Select.Operand.Ref.src1.valueOf(s).immediate), .src1_sub_bit_size => @as(SignedImm, @intCast(Select.Operand.Ref.src1.valueOf(s).immediate)) - @as(SignedImm, @intCast(s.cg.nonBoolScalarBitSize(op.flags.base.ref.typeOf(s)))), - .log2_src0_elem_size => @intCast(std.math.log2(Select.Operand.Ref.src0.typeOf(s).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu))), + .log2_src0_elem_size => @intCast(std.math.log2(Select.Operand.Ref.src0.typeOf(s).indexableElem(s.cg.pt.zcu).abiSize(s.cg.pt.zcu))), .elem_mask => @as(u8, std.math.maxInt(u8)) >> @intCast( 8 - ((s.cg.unalignedSize(op.flags.base.ref.typeOf(s)) - 1) % @divExact(op.flags.base.size.bitSize(s.cg.target), 8) + 1 >> diff --git a/src/codegen/x86_64/abi.zig b/src/codegen/x86_64/abi.zig @@ -339,7 +339,7 @@ fn classifySystemVStruct( var field_it = loaded_struct.iterateRuntimeOrder(ip); while (field_it.next()) |field_index| { const field_ty = Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]); - const field_align = loaded_struct.fieldAlign(ip, field_index); + const field_align = loaded_struct.field_aligns.getOrNone(ip, field_index); byte_offset = std.mem.alignForward( u64, byte_offset, @@ -355,7 +355,7 @@ fn classifySystemVStruct( .@"packed" => {}, } } else if (zcu.typeToUnion(field_ty)) |field_loaded_union| { - switch (field_loaded_union.flagsUnordered(ip).layout) { + switch (field_loaded_union.layout) { .auto => unreachable, .@"extern" => { byte_offset = classifySystemVUnion(result, byte_offset, field_loaded_union, zcu, target); @@ -369,11 +369,11 @@ fn classifySystemVStruct( result_class.* = result_class.combineSystemV(field_class); byte_offset += field_ty.abiSize(zcu); } - const final_byte_offset = starting_byte_offset + loaded_struct.sizeUnordered(ip); + const final_byte_offset = starting_byte_offset + loaded_struct.size; std.debug.assert(final_byte_offset == std.mem.alignForward( u64, byte_offset, - loaded_struct.flagsUnordered(ip).alignment.toByteUnits().?, + loaded_struct.alignment.toByteUnits().?, )); return final_byte_offset; } @@ -398,7 +398,7 @@ fn classifySystemVUnion( .@"packed" => {}, } } else if (zcu.typeToUnion(field_ty)) |field_loaded_union| { - switch (field_loaded_union.flagsUnordered(ip).layout) { + switch (field_loaded_union.layout) { .auto => unreachable, .@"extern" => { _ = classifySystemVUnion(result, starting_byte_offset, field_loaded_union, zcu, target); @@ -411,7 +411,7 @@ fn classifySystemVUnion( for (result[@intCast(starting_byte_offset / 8)..][0..field_classes.len], field_classes) |*result_class, field_class| result_class.* = result_class.combineSystemV(field_class); } - return starting_byte_offset + loaded_union.sizeUnordered(ip); + return starting_byte_offset + loaded_union.size; } pub const zigcc = struct { diff --git a/src/link.zig b/src/link.zig @@ -29,6 +29,7 @@ const codegen = @import("codegen.zig"); pub const aarch64 = @import("link/aarch64.zig"); pub const LdScript = @import("link/LdScript.zig"); pub const Queue = @import("link/Queue.zig"); +pub const ConstPool = @import("link/ConstPool.zig"); pub const Diags = struct { /// Stored here so that function definitions can distinguish between @@ -798,14 +799,27 @@ pub const File = struct { }; /// Never called when LLVM is codegenning the ZCU. - fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index) UpdateContainerTypeError!void { + fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) UpdateContainerTypeError!void { + assert(base.comp.zcu.?.llvm_object == null); + switch (base.tag) { + .lld => unreachable, + else => {}, + inline .elf, .c => |tag| { + dev.check(tag.devFeature()); + return @as(*tag.Type(), @fieldParentPtr("base", base)).updateContainerType(pt, ty, success); + }, + } + } + + /// Never called when LLVM is codegenning the ZCU. + fn clearContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index) UpdateContainerTypeError!void { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { .lld => unreachable, else => {}, inline .elf => |tag| { dev.check(tag.devFeature()); - return @as(*tag.Type(), @fieldParentPtr("base", base)).updateContainerType(pt, ty); + return @as(*tag.Type(), @fieldParentPtr("base", base)).clearContainerType(pt, ty); }, } } @@ -1375,8 +1389,14 @@ pub const ZcuTask = union(enum) { link_nav: InternPool.Nav.Index, /// Write the machine code for a function to the output file. link_func: Zcu.CodegenTaskPool.Index, - link_type: InternPool.Index, - update_line_number: InternPool.TrackedInst.Index, + /// This struct/union/enum type has finished type resolution (successfully or otherwise), so the + /// linker can now lower debug information for this type (and any structural types which depend + /// on it, such as `?T`, `struct { T }`, `[2]T`, etc). + debug_update_container_type: struct { + ty: InternPool.Index, + success: bool, + }, + debug_update_line_number: InternPool.TrackedInst.Index, }; pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { @@ -1537,7 +1557,10 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void .link_func => |codegen_task| nav: { timer.pause(io); const func, var mir = codegen_task.wait(&zcu.codegen_task_pool, io) catch |err| switch (err) { - error.Canceled, error.AlreadyReported => return, + error.Canceled, error.AlreadyReported => { + comp.link_prog_node.completeOne(); + return; + }, }; defer mir.deinit(zcu); timer.@"resume"(io); @@ -1563,21 +1586,25 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void } break :nav ip.indexToKey(func).func.owner_nav; }, - .link_type => |ty| nav: { - const name = Type.fromInterned(ty).containerTypeName(ip).toSlice(ip); - const nav_prog_node = comp.link_prog_node.start(name, 0); - defer nav_prog_node.end(); - if (zcu.llvm_object == null) { + .debug_update_container_type => |container_update| nav: { + const name = Type.fromInterned(container_update.ty).containerTypeName(ip).toSlice(ip); + const ty_prog_node = comp.link_prog_node.start(name, 0); + defer ty_prog_node.end(); + if (zcu.llvm_object) |llvm_object| { + llvm_object.updateContainerType(pt, container_update.ty, container_update.success) catch |err| switch (err) { + error.OutOfMemory => diags.setAllocFailure(), + }; + } else { if (comp.bin_file) |lf| { - lf.updateContainerType(pt, ty) catch |err| switch (err) { + lf.updateContainerType(pt, container_update.ty, container_update.success) catch |err| switch (err) { error.OutOfMemory => diags.setAllocFailure(), - error.TypeFailureReported => assert(zcu.failed_types.contains(ty)), + error.TypeFailureReported => assert(zcu.failed_types.contains(container_update.ty)), }; } } break :nav null; }, - .update_line_number => |ti| nav: { + .debug_update_line_number => |ti| nav: { const nav_prog_node = comp.link_prog_node.start("Update line number", 0); defer nav_prog_node.end(); if (pt.zcu.llvm_object == null) { diff --git a/src/link/C.zig b/src/link/C.zig @@ -1,3 +1,9 @@ +/// Unlike other linker implementations, `link.C` does not attempt to incrementally link its output, +/// because C has many language rules which make that impractical. Instead, we individually generate +/// each declaration (NAV), and the output is stitched together (alongside types and UAVs) in an +/// appropriate order in `flush`. +const C = @This(); + const std = @import("std"); const mem = std.mem; const assert = std.debug.assert; @@ -5,7 +11,6 @@ const Allocator = std.mem.Allocator; const fs = std.fs; const Path = std.Build.Cache.Path; -const C = @This(); const build_options = @import("build_options"); const Zcu = @import("../Zcu.zig"); const Module = @import("../Package/Module.zig"); @@ -19,40 +24,45 @@ const Type = @import("../Type.zig"); const Value = @import("../Value.zig"); const AnyMir = @import("../codegen.zig").AnyMir; -pub const zig_h = "#include \"zig.h\"\n"; - base: link.File, -/// This linker backend does not try to incrementally link output C source code. -/// Instead, it tracks all declarations in this table, and iterates over it -/// in the flush function, stitching pre-rendered pieces of C code together. -navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, AvBlock), -/// All the string bytes of rendered C code, all squished into one array. -/// While in progress, a separate buffer is used, and then when finished, the -/// buffer is copied into this one. + +/// All the string bytes of rendered C code, all squished into one array. `String` is used to refer +/// to specific slices of this array, used for the rendered C code of an individual UAV/NAV/type. +/// +/// During code generation for functions, a separate buffer is used, and the contents of that buffer +/// are copied into `string_bytes` when the function is emitted by `updateFunc`. string_bytes: std.ArrayList(u8), -/// Tracks all the anonymous decls that are used by all the decls so they can -/// be rendered during flush(). -uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, AvBlock), -/// Sparse set of uavs that are overaligned. Underaligned anon decls are -/// lowered the same as ABI-aligned anon decls. The keys here are a subset of -/// the keys of `uavs`. -aligned_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), - -exported_navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ExportedBlock), -exported_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, ExportedBlock), - -/// Optimization, `updateDecl` reuses this buffer rather than creating a new -/// one with every call. -fwd_decl_buf: []u8, -/// Optimization, `updateDecl` reuses this buffer rather than creating a new -/// one with every call. -code_header_buf: []u8, -/// Optimization, `updateDecl` reuses this buffer rather than creating a new -/// one with every call. -code_buf: []u8, -/// Optimization, `flush` reuses this buffer rather than creating a new -/// one with every call. -scratch_buf: []u32, + +/// Like with `string_bytes`, we concatenate all type dependencies into one array, and slice into it +/// for specific groups of dependencies. These values are indices into `type_pool`, and thus also +/// into `types`. We store these instead of `InternPool.Index` because it lets us avoid some hash +/// map lookups in `flush`. +type_dependencies: std.ArrayList(link.ConstPool.Index), +/// For storing dependencies on "aligned" versions of types, we must associate each type with a +/// bitmask of required alignments. As with `type_dependencies`, we concatenate all such masks into +/// one array. +align_dependency_masks: std.ArrayList(u64), + +/// All NAVs, regardless of whether they are functions or simple constants, are put in this map. +navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, RenderedDecl), +/// All UAVs which may be referenced are in this map. The UAV alignment is not included in the +/// rendered C code stored here, because we don't know the alignment a UAV needs until `flush`. +uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, RenderedDecl), +/// Contains all types which are needed by some other rendered code. Does not contain any constants +/// other than types. +type_pool: link.ConstPool, +/// Indices are `link.ConstPool.Index` from `type_pool`. Contains rendered C code for every type +/// which may be referenced. Logic in `flush` will perform the appropriate topological sort to emit +/// these type definitions in an order which C allows. +types: std.ArrayList(RenderedType), + +/// The set of big int types required by *any* generated code so far. These are always safe to emit, +/// so they do not participate in the dependency graph traversal in `flush`. Therefore, redundant +/// big-int types may be emitted under incremental compilation. +bigint_types: std.AutoArrayHashMapUnmanaged(codegen.CType.BigInt, void), + +exported_navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, String), +exported_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, String), /// A reference into `string_bytes`. const String = extern struct { @@ -64,50 +74,320 @@ const String = extern struct { .len = 0, }; - fn concat(lhs: String, rhs: String) String { - assert(lhs.start + lhs.len == rhs.start); + fn get(s: String, c: *C) []const u8 { + return c.string_bytes.items[s.start..][0..s.len]; + } +}; + +const CTypeDependencies = struct { + len: u32, + errunion_len: u32, + fwd_len: u32, + errunion_fwd_len: u32, + aligned_fwd_len: u32, + + /// Index into `C.type_dependencies`. Starting at this index are: + /// * `len` dependencies on complete types + /// * `errunion_len` dependencies on complete error union types + /// * `fwd_len` dependencies on forward-declared types + /// * `errunion_fwd_len` dependencies on forward-declared error union types + /// * `aligned_fwd_len` dependencies on aligned types + type_start: u32, + /// Index into `C.align_dependency_masks`. Starting at this index are `aligned_type_fwd_len` + /// items containing the bitmasks for each aligned type (in `C.type_dependencies`). + align_mask_start: u32, + + const Resolved = struct { + type: []const link.ConstPool.Index, + errunion_type: []const link.ConstPool.Index, + type_fwd: []const link.ConstPool.Index, + errunion_type_fwd: []const link.ConstPool.Index, + aligned_type_fwd: []const link.ConstPool.Index, + aligned_type_masks: []const u64, + }; + + fn get(td: *const CTypeDependencies, c: *const C) Resolved { + const types_overlong = c.type_dependencies.items[td.type_start..]; return .{ - .start = lhs.start, - .len = lhs.len + rhs.len, + .type = types_overlong[0..td.len], + .errunion_type = types_overlong[td.len..][0..td.errunion_len], + .type_fwd = types_overlong[td.len + td.errunion_len ..][0..td.fwd_len], + .errunion_type_fwd = types_overlong[td.len + td.errunion_len + td.fwd_len ..][0..td.errunion_fwd_len], + .aligned_type_fwd = types_overlong[td.len + td.errunion_len + td.fwd_len + td.errunion_fwd_len ..][0..td.aligned_fwd_len], + .aligned_type_masks = c.align_dependency_masks.items[td.align_mask_start..][0..td.aligned_fwd_len], }; } + + const empty: CTypeDependencies = .{ + .len = 0, + .errunion_len = 0, + .fwd_len = 0, + .errunion_fwd_len = 0, + .aligned_fwd_len = 0, + .type_start = 0, + .align_mask_start = 0, + }; }; -/// Per-declaration data. -pub const AvBlock = struct { - fwd_decl: String = .empty, - code: String = .empty, - /// Each `Decl` stores a set of used `CType`s. In `flush()`, we iterate - /// over each `Decl` and generate the definition for each used `CType` once. - ctype_pool: codegen.CType.Pool = .empty, - /// May contain string references to ctype_pool - lazy_fns: codegen.LazyFnMap = .{}, - - fn deinit(ab: *AvBlock, gpa: Allocator) void { - ab.lazy_fns.deinit(gpa); - ab.ctype_pool.deinit(gpa); - ab.* = undefined; +const RenderedDecl = struct { + fwd_decl: String, + code: String, + ctype_deps: CTypeDependencies, + need_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), + need_tag_name_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), + need_never_tail_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void), + need_never_inline_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void), + + const init: RenderedDecl = .{ + .fwd_decl = .empty, + .code = .empty, + .ctype_deps = .empty, + .need_uavs = .empty, + .need_tag_name_funcs = .empty, + .need_never_tail_funcs = .empty, + .need_never_inline_funcs = .empty, + }; + + fn deinit(rd: *RenderedDecl, gpa: Allocator) void { + rd.need_uavs.deinit(gpa); + rd.need_tag_name_funcs.deinit(gpa); + rd.need_never_tail_funcs.deinit(gpa); + rd.need_never_inline_funcs.deinit(gpa); + rd.* = undefined; + } + + /// We are about to re-render this declaration, but we want to reuse the existing buffers, so + /// call `clearRetainCapacity` on the containers. Sets `fwd_decl` and `code` to `undefined`, + /// because we shouldn't be using the old values any longer. + fn clearRetainingCapacity(rd: *RenderedDecl) void { + rd.fwd_decl = undefined; + rd.code = undefined; + rd.need_uavs.clearRetainingCapacity(); + rd.need_tag_name_funcs.clearRetainingCapacity(); + rd.need_never_tail_funcs.clearRetainingCapacity(); + rd.need_never_inline_funcs.clearRetainingCapacity(); } }; -/// Per-exported-symbol data. -pub const ExportedBlock = struct { - fwd_decl: String = .empty, +const RenderedType = struct { + /// If this type lowers to an aggregate, this is a forward declaration of its struct/union tag. + /// Otherwise, this is `.empty`. + /// + /// Populated immediately and never changes. + fwd_decl: String, + + /// A forward declaration of an error union type with this type as its *payload*. + /// + /// Populated immediately and never changes. + errunion_fwd_decl: String, + + /// If this type lowers to an aggregate, this is the struct/union definition. + /// If this type lowers to a typedef, this is that typedef. + /// Otherwise, this is `.empty`. + definition: String, + /// The `struct` definition for an error union type with this type as its *payload*. + /// + /// This string is empty iff the payload type does not have a resolved layout. If the layout is + /// resolved, the error union struct is defined, even if the payload type lacks runtime bits. + errunion_definition: String, + + /// Dependencies which must be satisfied before emitting the name of this type. As such, they + /// must be satisfied before emitting `errunion_definition` or any aligned typedef. + /// + /// Populated immediately and never changes. + deps: CTypeDependencies, + + /// Dependencies which must be satisfied before emitting `definition`. + definition_deps: CTypeDependencies, }; -pub fn getString(this: C, s: String) []const u8 { - return this.string_bytes.items[s.start..][0..s.len]; +/// Only called by `link.ConstPool` due to `c.type_pool`, so `val` is always a type. +pub fn addConst( + c: *C, + pt: Zcu.PerThread, + pool_index: link.ConstPool.Index, + val: InternPool.Index, +) Allocator.Error!void { + const zcu = pt.zcu; + const gpa = zcu.comp.gpa; + assert(zcu.intern_pool.typeOf(val) == .type_type); + assert(@intFromEnum(pool_index) == c.types.items.len); + + const ty: Type = .fromInterned(val); + + const fwd_decl: String = fwd_decl: { + var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes); + defer c.string_bytes = aw.toArrayList(); + const start = aw.written().len; + codegen.CType.render_defs.fwdDecl(ty, &aw.writer, zcu) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + }; + break :fwd_decl .{ + .start = @intCast(start), + .len = @intCast(aw.written().len - start), + }; + }; + + const errunion_fwd_decl: String = errunion_fwd_decl: { + var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes); + defer c.string_bytes = aw.toArrayList(); + const start = aw.written().len; + codegen.CType.render_defs.errunionFwdDecl(ty, &aw.writer, zcu) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + }; + break :errunion_fwd_decl .{ + .start = @intCast(start), + .len = @intCast(aw.written().len - start), + }; + }; + + try c.types.append(gpa, .{ + .fwd_decl = fwd_decl, + .errunion_fwd_decl = errunion_fwd_decl, + // This field will be populated just below. + .deps = undefined, + // The remaining fields will be populated later by either `updateConstIncomplete` or + // `updateConstComplete` (it is guaranteed that at least one will be called). + .definition = undefined, + .errunion_definition = undefined, + .definition_deps = undefined, + }); + + { + // Find the dependencies required to just render the type `ty`. + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + var deps: codegen.CType.Dependencies = .empty; + defer deps.deinit(gpa); + _ = try codegen.CType.lower(ty, &deps, arena.allocator(), zcu); + // This call may add more items to `c.types`. + const type_deps = try c.addCTypeDependencies(pt, &deps); + c.types.items[@intFromEnum(pool_index)].deps = type_deps; + } } -pub fn addString(this: *C, s: []const u8) Allocator.Error!String { - const comp = this.base.comp; - const gpa = comp.gpa; - try this.string_bytes.appendSlice(gpa, s); - return .{ - .start = @intCast(this.string_bytes.items.len - s.len), - .len = @intCast(s.len), +/// Only called by `link.ConstPool` due to `c.type_pool`, so `val` is always a type. +pub fn updateConstIncomplete( + c: *C, + pt: Zcu.PerThread, + index: link.ConstPool.Index, + val: InternPool.Index, +) Allocator.Error!void { + const zcu = pt.zcu; + const gpa = zcu.comp.gpa; + + assert(zcu.intern_pool.typeOf(val) == .type_type); + const ty: Type = .fromInterned(val); + + const rendered: *RenderedType = &c.types.items[@intFromEnum(index)]; + + rendered.errunion_definition = .empty; + rendered.definition_deps = .empty; + rendered.definition = definition: { + if (rendered.fwd_decl.len != 0) { + // This is a struct or union type. We will never complete it, but we must forward + // declare it to ensure that its first usage does not appear in a different scope. + break :definition rendered.fwd_decl; + } + // Otherwise, we might need to `typedef` to `void`. + var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes); + defer c.string_bytes = aw.toArrayList(); + const start = aw.written().len; + codegen.CType.render_defs.defineIncomplete(ty, &aw.writer, pt) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + }; + break :definition .{ + .start = @intCast(start), + .len = @intCast(aw.written().len - start), + }; }; } +/// Only called by `link.ConstPool` due to `c.type_pool`, so `val` is always a type. +pub fn updateConst( + c: *C, + pt: Zcu.PerThread, + index: link.ConstPool.Index, + val: InternPool.Index, +) Allocator.Error!void { + const zcu = pt.zcu; + const gpa = zcu.comp.gpa; + + assert(zcu.intern_pool.typeOf(val) == .type_type); + const ty: Type = .fromInterned(val); + + const rendered: *RenderedType = &c.types.items[@intFromEnum(index)]; + + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + + var deps: codegen.CType.Dependencies = .empty; + defer deps.deinit(gpa); + + { + var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes); + defer c.string_bytes = aw.toArrayList(); + const start = aw.written().len; + codegen.CType.render_defs.errunionDefineComplete( + ty, + &deps, + arena.allocator(), + &aw.writer, + pt, + ) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + error.OutOfMemory => |e| return e, + }; + rendered.errunion_definition = .{ + .start = @intCast(start), + .len = @intCast(aw.written().len - start), + }; + } + + deps.clearRetainingCapacity(); + + { + var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes); + defer c.string_bytes = aw.toArrayList(); + const start = aw.written().len; + codegen.CType.render_defs.defineComplete( + ty, + &deps, + arena.allocator(), + &aw.writer, + pt, + ) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + error.OutOfMemory => |e| return e, + }; + // Remove dependency on a forward declaration of ourselves; we're defining this type so that + // forward declaration obviously exists! + _ = deps.type_fwd.swapRemove(ty.toIntern()); + rendered.definition = .{ + .start = @intCast(start), + .len = @intCast(aw.written().len - start), + }; + } + + { + // This call invalidates `rendered`. + const definition_deps = try c.addCTypeDependencies(pt, &deps); + c.types.items[@intFromEnum(index)].definition_deps = definition_deps; + } +} + +fn addString(c: *C, vec: []const []const u8) Allocator.Error!String { + const gpa = c.base.comp.gpa; + + var len: u32 = 0; + for (vec) |s| len += @intCast(s.len); + try c.string_bytes.ensureUnusedCapacity(gpa, len); + + const start: u32 = @intCast(c.string_bytes.items.len); + for (vec) |s| c.string_bytes.appendSliceAssumeCapacity(s); + assert(c.string_bytes.items.len == start + len); + + return .{ .start = start, .len = len }; +} pub fn open( arena: Allocator, @@ -156,267 +436,622 @@ pub fn createEmpty( .file = file, .build_id = options.build_id, }, - .navs = .empty, .string_bytes = .empty, + .type_dependencies = .empty, + .align_dependency_masks = .empty, + .navs = .empty, .uavs = .empty, - .aligned_uavs = .empty, + .type_pool = .empty, + .types = .empty, + .bigint_types = .empty, .exported_navs = .empty, .exported_uavs = .empty, - .fwd_decl_buf = &.{}, - .code_header_buf = &.{}, - .code_buf = &.{}, - .scratch_buf = &.{}, }; return c_file; } -pub fn deinit(self: *C) void { - const gpa = self.base.comp.gpa; - - for (self.navs.values()) |*db| { - db.deinit(gpa); - } - self.navs.deinit(gpa); - - for (self.uavs.values()) |*db| { - db.deinit(gpa); - } - self.uavs.deinit(gpa); - self.aligned_uavs.deinit(gpa); +pub fn deinit(c: *C) void { + const gpa = c.base.comp.gpa; - self.exported_navs.deinit(gpa); - self.exported_uavs.deinit(gpa); + for (c.navs.values()) |*r| r.deinit(gpa); + for (c.uavs.values()) |*r| r.deinit(gpa); + + c.string_bytes.deinit(gpa); + c.type_dependencies.deinit(gpa); + c.align_dependency_masks.deinit(gpa); + c.navs.deinit(gpa); + c.uavs.deinit(gpa); + c.type_pool.deinit(gpa); + c.types.deinit(gpa); + c.bigint_types.deinit(gpa); + c.exported_navs.deinit(gpa); + c.exported_uavs.deinit(gpa); +} - self.string_bytes.deinit(gpa); - gpa.free(self.fwd_decl_buf); - gpa.free(self.code_header_buf); - gpa.free(self.code_buf); - gpa.free(self.scratch_buf); +pub fn updateContainerType( + c: *C, + pt: Zcu.PerThread, + ty: InternPool.Index, + success: bool, +) link.File.UpdateContainerTypeError!void { + try c.type_pool.updateContainerType(pt, .{ .c = c }, ty, success); } pub fn updateFunc( - self: *C, + c: *C, pt: Zcu.PerThread, func_index: InternPool.Index, mir: *AnyMir, -) link.File.UpdateNavError!void { +) Allocator.Error!void { const zcu = pt.zcu; const gpa = zcu.gpa; - const func = zcu.funcInfo(func_index); + const nav = zcu.funcInfo(func_index).owner_nav; - const gop = try self.navs.getOrPut(gpa, func.owner_nav); - if (gop.found_existing) gop.value_ptr.deinit(gpa); - gop.value_ptr.* = .{ - .code = .empty, - .fwd_decl = .empty, - .ctype_pool = mir.c.ctype_pool.move(), - .lazy_fns = mir.c.lazy_fns.move(), + const rendered_decl: *RenderedDecl = rd: { + const gop = try c.navs.getOrPut(gpa, nav); + if (gop.found_existing) gop.value_ptr.deinit(gpa); + break :rd gop.value_ptr; }; - gop.value_ptr.fwd_decl = try self.addString(mir.c.fwd_decl); - const code_header = try self.addString(mir.c.code_header); - const code = try self.addString(mir.c.code); - gop.value_ptr.code = code_header.concat(code); - try self.addUavsFromCodegen(&mir.c.uavs); -} - -fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) link.File.FlushError!void { - const gpa = self.base.comp.gpa; - const uav = self.uavs.keys()[i]; - - var object: codegen.Object = .{ - .dg = .{ - .gpa = gpa, - .pt = pt, - .mod = pt.zcu.root_mod, - .error_msg = null, - .pass = .{ .uav = uav }, - .is_naked_fn = false, - .expected_block = null, - .fwd_decl = undefined, - .ctype_pool = .empty, - .scratch = .initBuffer(self.scratch_buf), - .uavs = .empty, - }, - .code_header = undefined, - .code = undefined, - .indent_counter = 0, - }; - object.dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf); - object.code = .initOwnedSlice(gpa, self.code_buf); - defer { - object.dg.uavs.deinit(gpa); - object.dg.ctype_pool.deinit(object.dg.gpa); - - self.fwd_decl_buf = object.dg.fwd_decl.toArrayList().allocatedSlice(); - self.code_buf = object.code.toArrayList().allocatedSlice(); - self.scratch_buf = object.dg.scratch.allocatedSlice(); - } - try object.dg.ctype_pool.init(gpa); - - const c_value: codegen.CValue = .{ .constant = Value.fromInterned(uav) }; - const alignment: Alignment = self.aligned_uavs.get(uav) orelse .none; - codegen.genDeclValue(&object, c_value.constant, c_value, alignment, .none) catch |err| switch (err) { - error.AnalysisFail => { - @panic("TODO: C backend AnalysisFail on anonymous decl"); - //try zcu.failed_decls.put(gpa, decl_index, object.dg.error_msg.?); - //return; - }, - error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, + c.navs.lockPointers(); + defer c.navs.unlockPointers(); + + rendered_decl.* = .{ + .fwd_decl = try c.addString(&.{mir.c.fwd_decl}), + .code = try c.addString(&.{ mir.c.code_header, mir.c.code }), + .ctype_deps = try c.addCTypeDependencies(pt, &mir.c.ctype_deps), + .need_uavs = mir.c.need_uavs.move(), + .need_tag_name_funcs = mir.c.need_tag_name_funcs.move(), + .need_never_tail_funcs = mir.c.need_never_tail_funcs.move(), + .need_never_inline_funcs = mir.c.need_never_inline_funcs.move(), }; - try self.addUavsFromCodegen(&object.dg.uavs); + const old_uavs_len = c.uavs.count(); + try c.uavs.ensureUnusedCapacity(gpa, rendered_decl.need_uavs.count()); + for (rendered_decl.need_uavs.keys()) |val| { + const gop = c.uavs.getOrPutAssumeCapacity(val); + if (gop.found_existing) { + assert(gop.index < old_uavs_len); + } else { + assert(gop.index >= old_uavs_len); + } + } + try c.updateNewUavs(pt, old_uavs_len); - object.dg.ctype_pool.freeUnusedCapacity(gpa); - self.uavs.values()[i] = .{ - .fwd_decl = try self.addString(object.dg.fwd_decl.written()), - .code = try self.addString(object.code.written()), - .ctype_pool = object.dg.ctype_pool.move(), - }; + try c.type_pool.flushPending(pt, .{ .c = c }); } -pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.File.UpdateNavError!void { +pub fn updateNav( + c: *C, + pt: Zcu.PerThread, + nav_index: InternPool.Nav.Index, +) Allocator.Error!void { const tracy = trace(@src()); defer tracy.end(); - const gpa = self.base.comp.gpa; + const gpa = c.base.comp.gpa; const zcu = pt.zcu; const ip = &zcu.intern_pool; const nav = ip.getNav(nav_index); - const nav_init = switch (ip.indexToKey(nav.status.fully_resolved.val)) { + switch (ip.indexToKey(nav.status.fully_resolved.val)) { .func => return, - .@"extern" => .none, - .variable => |variable| variable.init, - else => nav.status.fully_resolved.val, + .@"extern" => {}, + else => { + const nav_ty: Type = .fromInterned(nav.typeOf(ip)); + if (!nav_ty.hasRuntimeBits(zcu)) { + if (c.navs.fetchSwapRemove(nav_index)) |kv| { + var old_rendered = kv.value; + old_rendered.deinit(gpa); + } + return; + } + }, + } + + const rendered_decl: *RenderedDecl = rd: { + const gop = try c.navs.getOrPut(gpa, nav_index); + if (gop.found_existing) { + gop.value_ptr.clearRetainingCapacity(); + } else { + gop.value_ptr.* = .init; + } + break :rd gop.value_ptr; }; - if (nav_init != .none and !Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) return; + c.navs.lockPointers(); + defer c.navs.unlockPointers(); - const gop = try self.navs.getOrPut(gpa, nav_index); - errdefer _ = self.navs.pop(); - if (!gop.found_existing) gop.value_ptr.* = .{}; - const ctype_pool = &gop.value_ptr.ctype_pool; - try ctype_pool.init(gpa); - ctype_pool.clearRetainingCapacity(); + { + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); - var object: codegen.Object = .{ - .dg = .{ + var dg: codegen.DeclGen = .{ .gpa = gpa, + .arena = arena.allocator(), .pt = pt, .mod = zcu.navFileScope(nav_index).mod.?, .error_msg = null, - .pass = .{ .nav = nav_index }, + .owner_nav = nav_index.toOptional(), .is_naked_fn = false, .expected_block = null, - .fwd_decl = undefined, - .ctype_pool = ctype_pool.*, - .scratch = .initBuffer(self.scratch_buf), - .uavs = .empty, - }, - .code_header = undefined, - .code = undefined, - .indent_counter = 0, + .ctype_deps = .empty, + .uavs = rendered_decl.need_uavs.move(), + }; + + defer { + rendered_decl.need_uavs = dg.uavs.move(); + dg.ctype_deps.deinit(gpa); + } + + rendered_decl.fwd_decl = fwd_decl: { + var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes); + defer c.string_bytes = aw.toArrayList(); + const start = aw.written().len; + codegen.genDeclFwd(&dg, &aw.writer) catch |err| switch (err) { + error.AnalysisFail => switch (zcu.codegenFailMsg(nav_index, dg.error_msg.?)) { + error.CodegenFail => return, + error.OutOfMemory => |e| return e, + }, + error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, + }; + break :fwd_decl .{ + .start = @intCast(start), + .len = @intCast(aw.written().len - start), + }; + }; + + rendered_decl.code = code: { + var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes); + defer c.string_bytes = aw.toArrayList(); + const start = aw.written().len; + codegen.genDecl(&dg, &aw.writer) catch |err| switch (err) { + error.AnalysisFail => switch (zcu.codegenFailMsg(nav_index, dg.error_msg.?)) { + error.CodegenFail => return, + error.OutOfMemory => |e| return e, + }, + error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, + }; + break :code .{ + .start = @intCast(start), + .len = @intCast(aw.written().len - start), + }; + }; + + rendered_decl.ctype_deps = try c.addCTypeDependencies(pt, &dg.ctype_deps); + } + + const old_uavs_len = c.uavs.count(); + try c.uavs.ensureUnusedCapacity(gpa, rendered_decl.need_uavs.count()); + for (rendered_decl.need_uavs.keys()) |val| { + const gop = c.uavs.getOrPutAssumeCapacity(val); + if (gop.found_existing) { + assert(gop.index < old_uavs_len); + } else { + assert(gop.index >= old_uavs_len); + } + } + try c.updateNewUavs(pt, old_uavs_len); + + try c.type_pool.flushPending(pt, .{ .c = c }); +} + +/// Unlike `updateNav` and `updateFunc`, this does *not* add newly-discovered UAVs to `c.uavs`. The +/// caller is instead responsible for doing that (by iterating `rendered_decl.need_uavs`). However, +/// this function *does* still add newly-discovered *types* to `c.type_pool`. +/// +/// This function does not accept an alignment for the UAV, because the alignment needed on a UAV is +/// not known until `flush` (since we need to have seen all uses of the UAV first). Instead, `flush` +/// will prefix the UAV definition with an appropriate alignment annotation if necessary. +fn updateUav( + c: *C, + pt: Zcu.PerThread, + val: Value, + rendered_decl: *RenderedDecl, +) Allocator.Error!void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = c.base.comp.gpa; + + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); + + var dg: codegen.DeclGen = .{ + .gpa = gpa, + .arena = arena.allocator(), + .pt = pt, + .mod = pt.zcu.root_mod, + .error_msg = null, + .owner_nav = .none, + .is_naked_fn = false, + .expected_block = null, + .ctype_deps = .empty, + .uavs = .empty, }; - object.dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf); - object.code = .initOwnedSlice(gpa, self.code_buf); defer { - object.dg.uavs.deinit(gpa); - ctype_pool.* = object.dg.ctype_pool.move(); - ctype_pool.freeUnusedCapacity(gpa); - - self.fwd_decl_buf = object.dg.fwd_decl.toArrayList().allocatedSlice(); - self.code_buf = object.code.toArrayList().allocatedSlice(); - self.scratch_buf = object.dg.scratch.allocatedSlice(); + rendered_decl.need_uavs = dg.uavs.move(); + dg.ctype_deps.deinit(gpa); } - codegen.genDecl(&object) catch |err| switch (err) { - error.AnalysisFail => switch (zcu.codegenFailMsg(nav_index, object.dg.error_msg.?)) { - error.CodegenFail => return, - error.OutOfMemory => |e| return e, - }, - error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, + rendered_decl.fwd_decl = fwd_decl: { + var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes); + defer c.string_bytes = aw.toArrayList(); + const start = aw.written().len; + codegen.genDeclValueFwd(&dg, &aw.writer, .{ + .name = .{ .constant = val }, + .@"const" = true, + .@"threadlocal" = false, + .init_val = val, + }) catch |err| switch (err) { + error.AnalysisFail => { + @panic("TODO: CBE error.AnalysisFail on uav"); + }, + error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, + }; + break :fwd_decl .{ + .start = @intCast(start), + .len = @intCast(aw.written().len - start), + }; + }; + + rendered_decl.code = code: { + var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes); + defer c.string_bytes = aw.toArrayList(); + const start = aw.written().len; + codegen.genDeclValue(&dg, &aw.writer, .{ + .name = .{ .constant = val }, + .@"const" = true, + .@"threadlocal" = false, + .init_val = val, + }) catch |err| switch (err) { + error.AnalysisFail => { + @panic("TODO: CBE error.AnalysisFail on uav"); + }, + error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, + }; + break :code .{ + .start = @intCast(start), + .len = @intCast(aw.written().len - start), + }; }; - gop.value_ptr.fwd_decl = try self.addString(object.dg.fwd_decl.written()); - gop.value_ptr.code = try self.addString(object.code.written()); - try self.addUavsFromCodegen(&object.dg.uavs); + + rendered_decl.ctype_deps = try c.addCTypeDependencies(pt, &dg.ctype_deps); } -pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { - // The C backend does not have the ability to fix line numbers without re-generating - // the entire Decl. - _ = self; +pub fn updateLineNumber(c: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) error{}!void { + // The C backend does not currently emit "#line" directives. Even if it did, it would not be + // capable of updating those line numbers without re-generating the entire declaration. + _ = c; _ = pt; _ = ti_id; } -fn abiDefines(w: *std.Io.Writer, target: *const std.Target) !void { - switch (target.abi) { - .msvc, .itanium => try w.writeAll("#define ZIG_TARGET_ABI_MSVC\n"), - else => {}, - } - try w.print("#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n", .{ - target.cMaxIntAlignment(), - }); -} - -pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { - _ = arena; // Has the same lifetime as the call to Compilation.update. - +pub fn flush(c: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { const tracy = trace(@src()); defer tracy.end(); const sub_prog_node = prog_node.start("Flush Module", 0); defer sub_prog_node.end(); - const comp = self.base.comp; + const comp = c.base.comp; const diags = &comp.link_diags; const gpa = comp.gpa; const io = comp.io; - const zcu = self.base.comp.zcu.?; + const zcu = c.base.comp.zcu.?; const ip = &zcu.intern_pool; + const target = zcu.getTarget(); const pt: Zcu.PerThread = .activate(zcu, tid); defer pt.deactivate(); + // If it's somehow not made it into the pool, we need to generate the type `[:0]const u8` for + // error names. + const slice_const_u8_sentinel_0_pool_index = try c.type_pool.get( + pt, + .{ .c = c }, + .slice_const_u8_sentinel_0_type, + ); + try c.type_pool.flushPending(pt, .{ .c = c }); + + // Find the set of referenced NAVs; these are the ones we'll emit. It is important in this + // backend that we only emit referenced NAVs, because other ones may contain code from past + // incremental updates which is invalid C (due to e.g. types changing). Machine code backends + // don't have this problem because there are, of course, no type checking performed when you + // *execute* a binary! + var need_navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty; + defer need_navs.deinit(gpa); + { + const unit_references = try zcu.resolveReferences(); + for (c.navs.keys()) |nav| { + const nav_val = ip.getNav(nav).status.fully_resolved.val; + const check_unit: ?InternPool.AnalUnit = switch (ip.indexToKey(nav_val)) { + else => .wrap(.{ .nav_val = nav }), + .func => .wrap(.{ .func = nav_val }), + // TODO: this is a hack to deal with the fact that there's currently no good way to + // know which `extern`s are alive. This can and will break in certain patterns of + // incremental update. We kind of need to think a bit more about how the frontend + // actually represents `extern`, it's a bit awkward right now. + .@"extern" => null, + }; + if (check_unit) |u| { + if (!unit_references.contains(u)) continue; + } + try need_navs.putNoClobber(gpa, nav, {}); + } + } + + // Using our knowledge of which NAVs are referenced, we now need to discover the set of UAVs and + // C types which are referenced (and hence must be emitted). As above, this is necessary to make + // sure we only emit valid C code. + // + // At the same time, we will discover the set of lazy functions which are referenced. + + var need_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment) = .empty; + defer need_uavs.deinit(gpa); + + var need_types: std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, void) = .empty; + defer need_types.deinit(gpa); + var need_errunion_types: std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, void) = .empty; + defer need_errunion_types.deinit(gpa); + var need_aligned_types: std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, u64) = .empty; + defer need_aligned_types.deinit(gpa); + + var need_tag_name_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty; + defer need_tag_name_funcs.deinit(gpa); + + var need_never_tail_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty; + defer need_never_tail_funcs.deinit(gpa); + + var need_never_inline_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty; + defer need_never_inline_funcs.deinit(gpa); + + // As mentioned above, we need this type for error names. + try need_types.put(gpa, slice_const_u8_sentinel_0_pool_index, {}); + + // Every exported NAV should have been discovered via `zcu.resolveReferences`... + for (c.exported_navs.keys()) |nav| assert(need_navs.contains(nav)); + // ...but we *do* need to add exported UAVs to the set. + try need_uavs.ensureUnusedCapacity(gpa, c.exported_uavs.count()); + for (c.exported_uavs.keys()) |uav| { + const gop = need_uavs.getOrPutAssumeCapacity(uav); + if (!gop.found_existing) gop.value_ptr.* = .none; + } + + // For every referenced NAV, some UAVs, C types, and lazy functions may be referenced. + for (need_navs.keys()) |nav| { + const rendered = c.navs.getPtr(nav).?; + try mergeNeededCTypes( + c, + &need_types, + &need_errunion_types, + &need_aligned_types, + &rendered.ctype_deps, + ); + try mergeNeededUavs(zcu, &need_uavs, &rendered.need_uavs); + + try need_tag_name_funcs.ensureUnusedCapacity(gpa, rendered.need_tag_name_funcs.count()); + for (rendered.need_tag_name_funcs.keys()) |enum_type| { + need_tag_name_funcs.putAssumeCapacity(enum_type, {}); + } + + try need_never_tail_funcs.ensureUnusedCapacity(gpa, rendered.need_never_tail_funcs.count()); + for (rendered.need_never_tail_funcs.keys()) |fn_nav| { + need_never_tail_funcs.putAssumeCapacity(fn_nav, {}); + } + + try need_never_inline_funcs.ensureUnusedCapacity(gpa, rendered.need_never_inline_funcs.count()); + for (rendered.need_never_inline_funcs.keys()) |fn_nav| { + need_never_inline_funcs.putAssumeCapacity(fn_nav, {}); + } + } + + // UAVs may reference other UAVs or C types. { - var i: usize = 0; - while (i < self.uavs.count()) : (i += 1) { - try self.updateUav(pt, i); + var index: usize = 0; + while (need_uavs.count() > index) : (index += 1) { + const val = need_uavs.keys()[index]; + const rendered = c.uavs.getPtr(val).?; + try mergeNeededCTypes( + c, + &need_types, + &need_errunion_types, + &need_aligned_types, + &rendered.ctype_deps, + ); + try mergeNeededUavs(zcu, &need_uavs, &rendered.need_uavs); } } - // This code path happens exclusively with -ofmt=c. The flush logic for - // emit-h is in `flushEmitH` below. + // Finally, C types may reference other C types. + { + var index: usize = 0; + var errunion_index: usize = 0; + var aligned_index: usize = 0; + while (true) { + if (index < need_types.count()) { + const pool_index = need_types.keys()[index]; + const rendered = &c.types.items[@intFromEnum(pool_index)]; + try mergeNeededCTypes( + c, + &need_types, + &need_errunion_types, + &need_aligned_types, + &rendered.definition_deps, // we're tasked with emitting the *definition* of this type + ); + index += 1; + continue; + } - var f: Flush = .{ - .ctype_pool = .empty, - .ctype_global_from_decl_map = .empty, - .ctypes = .empty, + if (errunion_index < need_errunion_types.count()) { + const payload_pool_index = need_errunion_types.keys()[errunion_index]; + const rendered = &c.types.items[@intFromEnum(payload_pool_index)]; + try mergeNeededCTypes( + c, + &need_types, + &need_errunion_types, + &need_aligned_types, + &rendered.deps, // the error union type requires emitting this type's *name* + ); + errunion_index += 1; + continue; + } - .lazy_ctype_pool = .empty, - .lazy_fns = .empty, - .lazy_fwd_decl = .empty, - .lazy_code = .empty, + if (aligned_index < need_aligned_types.count()) { + const pool_index = need_aligned_types.keys()[aligned_index]; + const rendered = &c.types.items[@intFromEnum(pool_index)]; + try mergeNeededCTypes( + c, + &need_types, + &need_errunion_types, + &need_aligned_types, + &rendered.deps, // an aligned typedef requires emitting this type's *name* + ); + aligned_index += 1; + continue; + } - .all_buffers = .empty, - .file_size = 0, - }; + break; + } + } + + // Now that we know which types are required, generate aligned typedefs. One buffer per aligned + // type, with *all* aligned typedefs for that type. + const aligned_type_strings = try arena.alloc([]const u8, need_aligned_types.count()); + { + var aw: std.Io.Writer.Allocating = .init(gpa); + defer aw.deinit(); + var unused_deps: codegen.CType.Dependencies = .empty; + defer unused_deps.deinit(gpa); + for ( + need_aligned_types.keys(), + need_aligned_types.values(), + aligned_type_strings, + ) |pool_index, align_mask, *str_out| { + const ty: Type = .fromInterned(pool_index.val(&c.type_pool)); + const has_layout = c.types.items[@intFromEnum(pool_index)].errunion_definition.len > 0; + for (0..@bitSizeOf(@TypeOf(align_mask))) |bit_index| { + switch (@as(u1, @truncate(align_mask >> @intCast(bit_index)))) { + 0 => continue, + 1 => {}, + } + codegen.CType.render_defs.defineAligned( + ty, + .fromLog2Units(@intCast(bit_index)), + has_layout, + &unused_deps, + arena, + &aw.writer, + pt, + ) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + error.OutOfMemory => |e| return e, + }; + } + str_out.* = try arena.dupe(u8, aw.written()); + aw.clearRetainingCapacity(); + } + } + + // We have discovered the full set of NAVs, UAVs, and types we need to emit, and will now begin + // to build the output buffer. Our strategy is to emit the C source in this order: + // + // * ABI defines and `#include "zig.h"` + // * Big-int type definitions + // * Other CType definitions (traversing the dependency graph to sort topologically) + // * Global assembly + // * UAV exports + // * NAV exports + // * UAV forward declarations + // * NAV forward declarations + // * Lazy declarations (error names; @tagName functions; never_tail/never_inline wrappers) + // * UAV definitions + // * NAV definitions + // + // Most of these sections are order-independent within themselves, with the exception of the + // type definitions, which must be ordered to avoid a struct/union from embedding a type which + // is currently incomplete. + // + // When emitting UAV forward declarations, if the UAV requires alignment, we must prefix it with + // an alignment annotation. We couldn't emit the alignment into the UAV's `RenderedDecl` because + // we couldn't have known the required alignment until now! + + var f: Flush = .{ .all_buffers = .empty, .file_size = 0 }; defer f.deinit(gpa); - var abi_defines_aw: std.Io.Writer.Allocating = .init(gpa); - defer abi_defines_aw.deinit(); - abiDefines(&abi_defines_aw.writer, zcu.getTarget()) catch |err| switch (err) { - error.WriteFailed => return error.OutOfMemory, - }; + // We know exactly what we'll be emitting, so can reserve capacity for all of our buffers! + + try f.all_buffers.ensureUnusedCapacity(gpa, 3 + // ABI defines and `#include "zig.h"` + 1 + // Big-int type definitions + need_types.count() + // `RenderedType.fwd_decl` (worst-case) + need_types.count() + // `RenderedType.definition` + need_errunion_types.count() + // `RenderedType.errunion_fwd_decl` (worst-case) + need_errunion_types.count() + // `RenderedType.errunion_definition` + need_aligned_types.count() + // `aligned_type_strings` + 1 + // Global assembly + c.exported_uavs.count() + // UAV export block + c.exported_navs.count() + // NAV export block + need_uavs.count() + // UAV forward declarations + need_navs.count() + // NAV forward declarations + 1 + // Lazy declarations + need_uavs.count() * 3 + // UAV definitions ("static ", "zig_align(4)", "<definition body>") + need_navs.count() * 2); // NAV definitions ("static ", "<definition body>") + + // ABI defines and `#include "zig.h"` + switch (target.abi) { + .msvc, .itanium => f.appendBufAssumeCapacity("#define ZIG_TARGET_ABI_MSVC\n"), + else => {}, + } + f.appendBufAssumeCapacity(try std.fmt.allocPrint( + arena, + "#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n", + .{target.cMaxIntAlignment()}, + )); + f.appendBufAssumeCapacity( + \\#include "zig.h" + \\ + ); - // Covers defines, zig.h, ctypes, asm, lazy fwd. - try f.all_buffers.ensureUnusedCapacity(gpa, 5); + // Big-int type definitions + var bigint_aw: std.Io.Writer.Allocating = .init(gpa); + defer bigint_aw.deinit(); + for (c.bigint_types.keys()) |bigint| { + codegen.CType.render_defs.defineBigInt(bigint, &bigint_aw.writer, zcu) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + }; + } + f.appendBufAssumeCapacity(bigint_aw.written()); - f.appendBufAssumeCapacity(abi_defines_aw.written()); - f.appendBufAssumeCapacity(zig_h); + // CType definitions + { + var ft: FlushTypes = .{ + .c = c, + .f = &f, + .aligned_types = &need_aligned_types, + .aligned_type_strings = aligned_type_strings, + .status = .empty, + .errunion_status = .empty, + .aligned_status = .empty, + }; + defer { + ft.status.deinit(gpa); + ft.errunion_status.deinit(gpa); + ft.aligned_status.deinit(gpa); + } + try ft.status.ensureUnusedCapacity(gpa, need_types.count()); + try ft.errunion_status.ensureUnusedCapacity(gpa, need_errunion_types.count()); + try ft.aligned_status.ensureUnusedCapacity(gpa, need_aligned_types.count()); - const ctypes_index = f.all_buffers.items.len; - f.all_buffers.items.len += 1; + for (need_types.keys()) |pool_index| { + ft.doType(pool_index); + } + for (need_errunion_types.keys()) |pool_index| { + ft.doErrunionType(pool_index); + } + for (need_aligned_types.keys()) |pool_index| { + ft.doAlignedTypeFwd(pool_index); + } + } + // Global assembly var asm_aw: std.Io.Writer.Allocating = .init(gpa); defer asm_aw.deinit(); codegen.genGlobalAsm(zcu, &asm_aw.writer) catch |err| switch (err) { @@ -424,462 +1059,472 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P }; f.appendBufAssumeCapacity(asm_aw.written()); - const lazy_index = f.all_buffers.items.len; - f.all_buffers.items.len += 1; + var export_names: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty; + defer export_names.deinit(gpa); + try export_names.ensureTotalCapacity(gpa, @intCast(zcu.single_exports.count())); + for (zcu.single_exports.values()) |export_index| { + export_names.putAssumeCapacity(export_index.ptr(zcu).opts.name, {}); + } + for (zcu.multi_exports.values()) |info| { + try export_names.ensureUnusedCapacity(gpa, info.len); + for (zcu.all_exports.items[info.index..][0..info.len]) |@"export"| { + export_names.putAssumeCapacity(@"export".opts.name, {}); + } + } - try f.lazy_ctype_pool.init(gpa); - try self.flushErrDecls(pt, &f); + // UAV export block + for (c.exported_uavs.values()) |code| { + f.appendBufAssumeCapacity(code.get(c)); + } - // Unlike other backends, the .c code we are emitting has order-dependent decls. - // `CType`s, forward decls, and non-functions first. + // NAV export block + for (c.exported_navs.values()) |code| { + f.appendBufAssumeCapacity(code.get(c)); + } - { - var export_names: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty; - defer export_names.deinit(gpa); - try export_names.ensureTotalCapacity(gpa, @intCast(zcu.single_exports.count())); - for (zcu.single_exports.values()) |export_index| { - export_names.putAssumeCapacity(export_index.ptr(zcu).opts.name, {}); - } - for (zcu.multi_exports.values()) |info| { - try export_names.ensureUnusedCapacity(gpa, info.len); - for (zcu.all_exports.items[info.index..][0..info.len]) |@"export"| { - export_names.putAssumeCapacity(@"export".opts.name, {}); - } + // UAV forward declarations + for (need_uavs.keys()) |val| { + if (c.exported_uavs.contains(val)) continue; // the export was the declaration + const fwd_decl = c.uavs.getPtr(val).?.fwd_decl; + f.appendBufAssumeCapacity(fwd_decl.get(c)); + } + + // NAV forward declarations + for (need_navs.keys()) |nav| { + if (c.exported_navs.contains(nav)) continue; // the export was the declaration + if (ip.getNav(nav).getExtern(ip)) |e| { + if (export_names.contains(e.name)) continue; } + const fwd_decl = c.navs.getPtr(nav).?.fwd_decl; + f.appendBufAssumeCapacity(fwd_decl.get(c)); + } - for (self.uavs.keys(), self.uavs.values()) |uav, *av_block| try self.flushAvBlock( - pt, - zcu.root_mod, - &f, - av_block, - self.exported_uavs.getPtr(uav), - export_names, - .none, + // Lazy declarations + var lazy_decls_aw: std.Io.Writer.Allocating = .init(gpa); + defer lazy_decls_aw.deinit(); + { + var lazy_dg: codegen.DeclGen = .{ + .gpa = gpa, + .arena = arena, + .pt = pt, + .mod = pt.zcu.root_mod, + .owner_nav = .none, + .is_naked_fn = false, + .expected_block = null, + .error_msg = null, + .ctype_deps = .empty, + .uavs = .empty, + }; + defer { + assert(lazy_dg.uavs.count() == 0); + lazy_dg.ctype_deps.deinit(gpa); + } + const slice_const_u8_sentinel_0_cty: codegen.CType = try .lower( + .slice_const_u8_sentinel_0, + &lazy_dg.ctype_deps, + arena, + zcu, ); - - for (self.navs.keys(), self.navs.values()) |nav, *av_block| try self.flushAvBlock( - pt, - zcu.navFileScope(nav).mod.?, - &f, - av_block, - self.exported_navs.getPtr(nav), - export_names, - if (ip.getNav(nav).getExtern(ip) != null) - ip.getNav(nav).name.toOptional() - else - .none, + const slice_const_u8_sentinel_0_name = try std.fmt.allocPrint( + arena, + "{f}", + .{slice_const_u8_sentinel_0_cty.fmtTypeName(zcu)}, ); + codegen.genErrDecls(zcu, &lazy_decls_aw.writer, slice_const_u8_sentinel_0_name) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + }; + for (need_tag_name_funcs.keys()) |enum_ty_ip| { + const enum_ty: Type = .fromInterned(enum_ty_ip); + const enum_cty: codegen.CType = try .lower( + enum_ty, + &lazy_dg.ctype_deps, + arena, + zcu, + ); + codegen.genTagNameFn( + zcu, + &lazy_decls_aw.writer, + slice_const_u8_sentinel_0_name, + enum_ty, + try std.fmt.allocPrint(arena, "{f}", .{enum_cty.fmtTypeName(zcu)}), + ) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + }; + } + for (need_never_tail_funcs.keys()) |fn_nav| { + codegen.genLazyCallModifierFn(&lazy_dg, fn_nav, .never_tail, &lazy_decls_aw.writer) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + error.OutOfMemory => |e| return e, + error.AnalysisFail => unreachable, + }; + } + for (need_never_inline_funcs.keys()) |fn_nav| { + codegen.genLazyCallModifierFn(&lazy_dg, fn_nav, .never_inline, &lazy_decls_aw.writer) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + error.OutOfMemory => |e| return e, + error.AnalysisFail => unreachable, + }; + } } - - { - // We need to flush lazy ctypes after flushing all decls but before flushing any decl ctypes. - // This ensures that every lazy CType.Index exactly matches the global CType.Index. - try f.ctype_pool.init(gpa); - try self.flushCTypes(zcu, &f, .flush, &f.lazy_ctype_pool); - - for (self.uavs.keys(), self.uavs.values()) |uav, av_block| { - try self.flushCTypes(zcu, &f, .{ .uav = uav }, &av_block.ctype_pool); + f.appendBufAssumeCapacity(lazy_decls_aw.written()); + + // UAV definitions + for (need_uavs.keys(), need_uavs.values()) |val, overalign| { + const code = c.uavs.getPtr(val).?.code; + if (code.len == 0) continue; + if (!c.exported_uavs.contains(val)) { + f.appendBufAssumeCapacity("static "); } - - for (self.navs.keys(), self.navs.values()) |nav, av_block| { - try self.flushCTypes(zcu, &f, .{ .nav = nav }, &av_block.ctype_pool); + if (overalign != .none) { + // As long as `Alignment` isn't too big, it's reasonable to just generate all possible + // alignment annotations statically into a LUT, which avoids allocating strings on this + // path. + comptime assert(@bitSizeOf(Alignment) < 8); + const table_len = (1 << @bitSizeOf(Alignment)) - 1; + const table: [table_len][]const u8 = comptime table: { + @setEvalBranchQuota(16_000); + var table: [table_len][]const u8 = undefined; + for (&table, 0..) |*str, log2_align| { + const byte_align = Alignment.fromLog2Units(log2_align).toByteUnits().?; + str.* = std.fmt.comptimePrint("zig_align({d}) ", .{byte_align}); + } + break :table table; + }; + f.appendBufAssumeCapacity(table[overalign.toLog2Units()]); } + f.appendBufAssumeCapacity(code.get(c)); } - f.all_buffers.items[ctypes_index] = f.ctypes.items; - f.file_size += f.ctypes.items.len; - - f.all_buffers.items[lazy_index] = f.lazy_fwd_decl.items; - f.file_size += f.lazy_fwd_decl.items.len; - - // Now the code. - try f.all_buffers.ensureUnusedCapacity(gpa, 1 + (self.uavs.count() + self.navs.count()) * 2); - f.appendBufAssumeCapacity(f.lazy_code.items); - for (self.uavs.keys(), self.uavs.values()) |uav, av_block| f.appendCodeAssumeCapacity( - if (self.exported_uavs.contains(uav)) .default else switch (ip.indexToKey(uav)) { - .@"extern" => .zig_extern, - else => .static, - }, - self.getString(av_block.code), - ); - for (self.navs.keys(), self.navs.values()) |nav, av_block| f.appendCodeAssumeCapacity(storage: { - if (self.exported_navs.contains(nav)) break :storage .default; - if (ip.getNav(nav).getExtern(ip) != null) break :storage .zig_extern; - break :storage .static; - }, self.getString(av_block.code)); + // NAV definitions + for (need_navs.keys()) |nav| { + const code = c.navs.getPtr(nav).?.code; + if (code.len == 0) continue; + if (!c.exported_navs.contains(nav)) { + const is_extern = ip.getNav(nav).getExtern(ip) != null; + f.appendBufAssumeCapacity(if (is_extern) "zig_extern " else "static "); + } + f.appendBufAssumeCapacity(code.get(c)); + } - const file = self.base.file.?; + // We've collected all of our buffers; it's now time to actually write the file! + const file = c.base.file.?; file.setLength(io, f.file_size) catch |err| return diags.fail("failed to allocate file: {t}", .{err}); var fw = file.writer(io, &.{}); var w = &fw.interface; w.writeVecAll(f.all_buffers.items) catch |err| switch (err) { error.WriteFailed => return diags.fail("failed to write to '{f}': {s}", .{ - std.fmt.alt(self.base.emit, .formatEscapeChar), @errorName(fw.err.?), + std.fmt.alt(c.base.emit, .formatEscapeChar), @errorName(fw.err.?), }), }; } const Flush = struct { - ctype_pool: codegen.CType.Pool, - ctype_global_from_decl_map: std.ArrayList(codegen.CType), - ctypes: std.ArrayList(u8), - - lazy_ctype_pool: codegen.CType.Pool, - lazy_fns: LazyFns, - lazy_fwd_decl: std.ArrayList(u8), - lazy_code: std.ArrayList(u8), - /// We collect a list of buffers to write, and write them all at once with pwritev 😎 all_buffers: std.ArrayList([]const u8), /// Keeps track of the total bytes of `all_buffers`. file_size: u64, - const LazyFns = std.AutoHashMapUnmanaged(codegen.LazyFnKey, void); - fn appendBufAssumeCapacity(f: *Flush, buf: []const u8) void { if (buf.len == 0) return; f.all_buffers.appendAssumeCapacity(buf); f.file_size += buf.len; } - fn appendCodeAssumeCapacity(f: *Flush, storage: enum { default, zig_extern, static }, code: []const u8) void { - if (code.len == 0) return; - f.appendBufAssumeCapacity(switch (storage) { - .default => "\n", - .zig_extern => "\nzig_extern ", - .static => "\nstatic ", - }); - f.appendBufAssumeCapacity(code); - } - fn deinit(f: *Flush, gpa: Allocator) void { - f.ctype_pool.deinit(gpa); - assert(f.ctype_global_from_decl_map.items.len == 0); - f.ctype_global_from_decl_map.deinit(gpa); - f.ctypes.deinit(gpa); - f.lazy_ctype_pool.deinit(gpa); - f.lazy_fns.deinit(gpa); - f.lazy_fwd_decl.deinit(gpa); - f.lazy_code.deinit(gpa); f.all_buffers.deinit(gpa); } }; -const FlushDeclError = error{ - OutOfMemory, -}; - -fn flushCTypes( - self: *C, - zcu: *Zcu, - f: *Flush, - pass: codegen.DeclGen.Pass, - decl_ctype_pool: *const codegen.CType.Pool, -) FlushDeclError!void { - const gpa = self.base.comp.gpa; - const global_ctype_pool = &f.ctype_pool; - - const global_from_decl_map = &f.ctype_global_from_decl_map; - assert(global_from_decl_map.items.len == 0); - try global_from_decl_map.ensureTotalCapacity(gpa, decl_ctype_pool.items.len); - defer global_from_decl_map.clearRetainingCapacity(); - - var ctypes_aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &f.ctypes); - const ctypes_bw = &ctypes_aw.writer; - defer f.ctypes = ctypes_aw.toArrayList(); - - for (0..decl_ctype_pool.items.len) |decl_ctype_pool_index| { - const PoolAdapter = struct { - global_from_decl_map: []const codegen.CType, - pub fn eql(pool_adapter: @This(), decl_ctype: codegen.CType, global_ctype: codegen.CType) bool { - return if (decl_ctype.toPoolIndex()) |decl_pool_index| - decl_pool_index < pool_adapter.global_from_decl_map.len and - pool_adapter.global_from_decl_map[decl_pool_index].eql(global_ctype) - else - decl_ctype.index == global_ctype.index; - } - pub fn copy(pool_adapter: @This(), decl_ctype: codegen.CType) codegen.CType { - return if (decl_ctype.toPoolIndex()) |decl_pool_index| - pool_adapter.global_from_decl_map[decl_pool_index] - else - decl_ctype; - } - }; - const decl_ctype = codegen.CType.fromPoolIndex(decl_ctype_pool_index); - const global_ctype, const found_existing = try global_ctype_pool.getOrPutAdapted( - gpa, - decl_ctype_pool, - decl_ctype, - PoolAdapter{ .global_from_decl_map = global_from_decl_map.items }, - ); - global_from_decl_map.appendAssumeCapacity(global_ctype); - codegen.genTypeDecl( - zcu, - ctypes_bw, - global_ctype_pool, - global_ctype, - pass, - decl_ctype_pool, - decl_ctype, - found_existing, - ) catch |err| switch (err) { - error.WriteFailed => return error.OutOfMemory, - }; - } -} +pub fn updateExports( + c: *C, + pt: Zcu.PerThread, + exported: Zcu.Exported, + export_indices: []const Zcu.Export.Index, +) Allocator.Error!void { + const zcu = pt.zcu; + const gpa = zcu.gpa; -fn flushErrDecls(self: *C, pt: Zcu.PerThread, f: *Flush) FlushDeclError!void { - const gpa = self.base.comp.gpa; + var arena: std.heap.ArenaAllocator = .init(gpa); + defer arena.deinit(); - var object: codegen.Object = .{ - .dg = .{ - .gpa = gpa, - .pt = pt, - .mod = pt.zcu.root_mod, - .error_msg = null, - .pass = .flush, - .is_naked_fn = false, - .expected_block = null, - .fwd_decl = undefined, - .ctype_pool = f.lazy_ctype_pool, - .scratch = .initBuffer(self.scratch_buf), - .uavs = .empty, - }, - .code_header = undefined, - .code = undefined, - .indent_counter = 0, + var dg: codegen.DeclGen = .{ + .gpa = gpa, + .arena = arena.allocator(), + .pt = pt, + .mod = zcu.root_mod, + .owner_nav = .none, + .is_naked_fn = false, + .expected_block = null, + .error_msg = null, + .ctype_deps = .empty, + .uavs = .empty, }; - object.dg.fwd_decl = .fromArrayList(gpa, &f.lazy_fwd_decl); - object.code = .fromArrayList(gpa, &f.lazy_code); defer { - object.dg.uavs.deinit(gpa); - f.lazy_ctype_pool = object.dg.ctype_pool.move(); - f.lazy_ctype_pool.freeUnusedCapacity(gpa); - - f.lazy_fwd_decl = object.dg.fwd_decl.toArrayList(); - f.lazy_code = object.code.toArrayList(); - self.scratch_buf = object.dg.scratch.allocatedSlice(); + assert(dg.uavs.count() == 0); + dg.ctype_deps.deinit(gpa); } - codegen.genErrDecls(&object) catch |err| switch (err) { - error.AnalysisFail => unreachable, - error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, - }; - - try self.addUavsFromCodegen(&object.dg.uavs); -} - -fn flushLazyFn( - self: *C, - pt: Zcu.PerThread, - mod: *Module, - f: *Flush, - lazy_ctype_pool: *const codegen.CType.Pool, - lazy_fn: codegen.LazyFnMap.Entry, -) FlushDeclError!void { - const gpa = self.base.comp.gpa; - - var object: codegen.Object = .{ - .dg = .{ - .gpa = gpa, - .pt = pt, - .mod = mod, - .error_msg = null, - .pass = .flush, - .is_naked_fn = false, - .expected_block = null, - .fwd_decl = undefined, - .ctype_pool = f.lazy_ctype_pool, - .scratch = .initBuffer(self.scratch_buf), - .uavs = .empty, - }, - .code_header = undefined, - .code = undefined, - .indent_counter = 0, + const code: String = code: { + var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes); + defer c.string_bytes = aw.toArrayList(); + const start = aw.written().len; + codegen.genExports(&dg, &aw.writer, exported, export_indices) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + error.OutOfMemory => |e| return e, + }; + break :code .{ + .start = @intCast(start), + .len = @intCast(aw.written().len - start), + }; }; - object.dg.fwd_decl = .fromArrayList(gpa, &f.lazy_fwd_decl); - object.code = .fromArrayList(gpa, &f.lazy_code); - defer { - // If this assert trips just handle the anon_decl_deps the same as - // `updateFunc()` does. - assert(object.dg.uavs.count() == 0); - f.lazy_ctype_pool = object.dg.ctype_pool.move(); - f.lazy_ctype_pool.freeUnusedCapacity(gpa); - - f.lazy_fwd_decl = object.dg.fwd_decl.toArrayList(); - f.lazy_code = object.code.toArrayList(); - self.scratch_buf = object.dg.scratch.allocatedSlice(); + switch (exported) { + .nav => |nav| try c.exported_navs.put(gpa, nav, code), + .uav => |uav| try c.exported_uavs.put(gpa, uav, code), } - - codegen.genLazyFn(&object, lazy_ctype_pool, lazy_fn) catch |err| switch (err) { - error.AnalysisFail => unreachable, - error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, - }; } -fn flushLazyFns( +pub fn deleteExport( self: *C, - pt: Zcu.PerThread, - mod: *Module, - f: *Flush, - lazy_ctype_pool: *const codegen.CType.Pool, - lazy_fns: codegen.LazyFnMap, -) FlushDeclError!void { - const gpa = self.base.comp.gpa; - try f.lazy_fns.ensureUnusedCapacity(gpa, @intCast(lazy_fns.count())); - - var it = lazy_fns.iterator(); - while (it.next()) |entry| { - const gop = f.lazy_fns.getOrPutAssumeCapacity(entry.key_ptr.*); - if (gop.found_existing) continue; - gop.value_ptr.* = {}; - try self.flushLazyFn(pt, mod, f, lazy_ctype_pool, entry); + exported: Zcu.Exported, + _: InternPool.NullTerminatedString, +) void { + switch (exported) { + .nav => |nav| _ = self.exported_navs.swapRemove(nav), + .uav => |uav| _ = self.exported_uavs.swapRemove(uav), } } -fn flushAvBlock( - self: *C, - pt: Zcu.PerThread, - mod: *Module, - f: *Flush, - av_block: *const AvBlock, - exported_block: ?*const ExportedBlock, - export_names: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void), - extern_name: InternPool.OptionalNullTerminatedString, -) FlushDeclError!void { - const gpa = self.base.comp.gpa; - try self.flushLazyFns(pt, mod, f, &av_block.ctype_pool, av_block.lazy_fns); - try f.all_buffers.ensureUnusedCapacity(gpa, 1); - // avoid emitting extern decls that are already exported - if (extern_name.unwrap()) |name| if (export_names.contains(name)) return; - f.appendBufAssumeCapacity(self.getString(if (exported_block) |exported| - exported.fwd_decl - else - av_block.fwd_decl)); -} +fn mergeNeededCTypes( + c: *C, + need_types: *std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, void), + need_errunion_types: *std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, void), + need_aligned_types: *std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, u64), + deps: *const CTypeDependencies, +) Allocator.Error!void { + const gpa = c.base.comp.gpa; -pub fn flushEmitH(zcu: *Zcu) !void { - const tracy = trace(@src()); - defer tracy.end(); + const resolved = deps.get(c); - if (true) return; // emit-h is regressed + try need_types.ensureUnusedCapacity(gpa, resolved.type.len + resolved.type_fwd.len); + try need_errunion_types.ensureUnusedCapacity(gpa, resolved.errunion_type.len + resolved.errunion_type_fwd.len); + try need_aligned_types.ensureUnusedCapacity(gpa, resolved.aligned_type_fwd.len); - const emit_h = zcu.emit_h orelse return; - const io = zcu.comp.io; + for (resolved.type) |index| need_types.putAssumeCapacity(index, {}); + for (resolved.type_fwd) |index| need_types.putAssumeCapacity(index, {}); - // We collect a list of buffers to write, and write them all at once with pwritev 😎 - const num_buffers = emit_h.decl_table.count() + 1; - var all_buffers = try std.array_list.Managed(std.posix.iovec_const).initCapacity(zcu.gpa, num_buffers); - defer all_buffers.deinit(); + for (resolved.errunion_type) |index| need_errunion_types.putAssumeCapacity(index, {}); + for (resolved.errunion_type_fwd) |index| need_errunion_types.putAssumeCapacity(index, {}); - var file_size: u64 = zig_h.len; - if (zig_h.len != 0) { - all_buffers.appendAssumeCapacity(.{ - .base = zig_h, - .len = zig_h.len, - }); + for (resolved.aligned_type_fwd, resolved.aligned_type_masks) |ty_index, align_mask| { + const gop = need_aligned_types.getOrPutAssumeCapacity(ty_index); + if (!gop.found_existing) gop.value_ptr.* = 0; + gop.value_ptr.* |= align_mask; } +} - for (emit_h.decl_table.keys()) |decl_index| { - const decl_emit_h = emit_h.declPtr(decl_index); - const buf = decl_emit_h.fwd_decl.items; - if (buf.len != 0) { - all_buffers.appendAssumeCapacity(.{ - .base = buf.ptr, - .len = buf.len, - }); - file_size += buf.len; +fn mergeNeededUavs( + zcu: *const Zcu, + global: *std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), + new: *const std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), +) Allocator.Error!void { + const gpa = zcu.comp.gpa; + + try global.ensureUnusedCapacity(gpa, new.count()); + for (new.keys(), new.values()) |uav_val, need_align| { + const gop = global.getOrPutAssumeCapacity(uav_val); + if (!gop.found_existing) gop.value_ptr.* = .none; + + if (need_align != .none) { + const cur_align = switch (gop.value_ptr.*) { + .none => Value.fromInterned(uav_val).typeOf(zcu).abiAlignment(zcu), + else => |a| a, + }; + if (need_align.compareStrict(.gt, cur_align)) { + gop.value_ptr.* = need_align; + } } } - - const directory = emit_h.loc.directory orelse zcu.comp.local_cache_directory; - const file = try directory.handle.createFile(io, emit_h.loc.basename, .{ - // We set the end position explicitly below; by not truncating the file, we possibly - // make it easier on the file system by doing 1 reallocation instead of two. - .truncate = false, - }); - defer file.close(io); - - try file.setLength(io, file_size); - try file.pwritevAll(all_buffers.items, 0); } -pub fn updateExports( - self: *C, +fn addCTypeDependencies( + c: *C, pt: Zcu.PerThread, - exported: Zcu.Exported, - export_indices: []const Zcu.Export.Index, -) !void { - const zcu = pt.zcu; - const gpa = zcu.gpa; - const mod, const pass: codegen.DeclGen.Pass, const decl_block, const exported_block = switch (exported) { - .nav => |nav| .{ - zcu.navFileScope(nav).mod.?, - .{ .nav = nav }, - self.navs.getPtr(nav).?, - (try self.exported_navs.getOrPut(gpa, nav)).value_ptr, - }, - .uav => |uav| .{ - zcu.root_mod, - .{ .uav = uav }, - self.uavs.getPtr(uav).?, - (try self.exported_uavs.getOrPut(gpa, uav)).value_ptr, - }, - }; - const ctype_pool = &decl_block.ctype_pool; - var dg: codegen.DeclGen = .{ - .gpa = gpa, - .pt = pt, - .mod = mod, - .error_msg = null, - .pass = pass, - .is_naked_fn = false, - .expected_block = null, - .fwd_decl = undefined, - .ctype_pool = decl_block.ctype_pool, - .scratch = .initBuffer(self.scratch_buf), - .uavs = .empty, - }; - dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf); - defer { - assert(dg.uavs.count() == 0); - ctype_pool.* = dg.ctype_pool.move(); - ctype_pool.freeUnusedCapacity(gpa); + deps: *const codegen.CType.Dependencies, +) Allocator.Error!CTypeDependencies { + const gpa = pt.zcu.comp.gpa; + + try c.bigint_types.ensureUnusedCapacity(gpa, deps.bigint.count()); + for (deps.bigint.keys()) |bigint| c.bigint_types.putAssumeCapacity(bigint, {}); + + const type_start = c.type_dependencies.items.len; + const errunion_type_start = type_start + deps.type.count(); + const type_fwd_start = errunion_type_start + deps.errunion_type.count(); + const errunion_type_fwd_start = type_fwd_start + deps.type_fwd.count(); + const aligned_type_fwd_start = errunion_type_fwd_start + deps.errunion_type_fwd.count(); + try c.type_dependencies.appendNTimes(gpa, undefined, deps.type.count() + + deps.errunion_type.count() + + deps.type_fwd.count() + + deps.errunion_type_fwd.count() + + deps.aligned_type_fwd.count()); + + const align_mask_start = c.align_dependency_masks.items.len; + try c.align_dependency_masks.appendSlice(gpa, deps.aligned_type_fwd.values()); + + for (deps.type.keys(), type_start..) |ty, i| { + const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty); + c.type_dependencies.items[i] = pool_index; + } - self.fwd_decl_buf = dg.fwd_decl.toArrayList().allocatedSlice(); - self.scratch_buf = dg.scratch.allocatedSlice(); + for (deps.errunion_type.keys(), errunion_type_start..) |ty, i| { + const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty); + c.type_dependencies.items[i] = pool_index; } - codegen.genExports(&dg, exported, export_indices) catch |err| switch (err) { - error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, + + for (deps.type_fwd.keys(), type_fwd_start..) |ty, i| { + const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty); + c.type_dependencies.items[i] = pool_index; + } + + for (deps.errunion_type_fwd.keys(), errunion_type_fwd_start..) |ty, i| { + const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty); + c.type_dependencies.items[i] = pool_index; + } + + for (deps.aligned_type_fwd.keys(), aligned_type_fwd_start..) |ty, i| { + const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty); + c.type_dependencies.items[i] = pool_index; + } + + return .{ + .len = @intCast(deps.type.count()), + .errunion_len = @intCast(deps.errunion_type.count()), + .fwd_len = @intCast(deps.type_fwd.count()), + .errunion_fwd_len = @intCast(deps.errunion_type_fwd.count()), + .aligned_fwd_len = @intCast(deps.aligned_type_fwd.count()), + .type_start = @intCast(type_start), + .align_mask_start = @intCast(align_mask_start), }; - exported_block.* = .{ .fwd_decl = try self.addString(dg.fwd_decl.written()) }; } -pub fn deleteExport( - self: *C, - exported: Zcu.Exported, - _: InternPool.NullTerminatedString, -) void { - switch (exported) { - .nav => |nav| _ = self.exported_navs.swapRemove(nav), - .uav => |uav| _ = self.exported_uavs.swapRemove(uav), +fn updateNewUavs(c: *C, pt: Zcu.PerThread, old_uavs_len: usize) Allocator.Error!void { + const gpa = pt.zcu.comp.gpa; + var index = old_uavs_len; + while (index < c.uavs.count()) : (index += 1) { + // `new_uavs` is UAVs discovered while lowering *this* UAV. + const new_uavs: []const InternPool.Index = new: { + c.uavs.lockPointers(); + defer c.uavs.unlockPointers(); + const val: Value = .fromInterned(c.uavs.keys()[index]); + const rendered_decl = &c.uavs.values()[index]; + rendered_decl.* = .init; + try c.updateUav(pt, val, rendered_decl); + break :new rendered_decl.need_uavs.keys(); + }; + try c.uavs.ensureUnusedCapacity(gpa, new_uavs.len); + for (new_uavs) |val| { + const gop = c.uavs.getOrPutAssumeCapacity(val); + if (!gop.found_existing) { + assert(gop.index > index); + } + } } } -fn addUavsFromCodegen(c: *C, uavs: *const std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment)) Allocator.Error!void { - const gpa = c.base.comp.gpa; - try c.uavs.ensureUnusedCapacity(gpa, uavs.count()); - try c.aligned_uavs.ensureUnusedCapacity(gpa, uavs.count()); - for (uavs.keys(), uavs.values()) |uav_val, uav_align| { - { - const gop = c.uavs.getOrPutAssumeCapacity(uav_val); - if (!gop.found_existing) gop.value_ptr.* = .{}; +const FlushTypes = struct { + c: *C, + f: *Flush, + + aligned_types: *const std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, u64), + aligned_type_strings: []const []const u8, + + status: std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, bool), + errunion_status: std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, bool), + aligned_status: std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, void), + + fn processDeps(ft: *FlushTypes, deps: *const CTypeDependencies) void { + const resolved = deps.get(ft.c); + for (resolved.type) |pool_index| ft.doType(pool_index); + for (resolved.type_fwd) |pool_index| ft.doTypeFwd(pool_index); + for (resolved.errunion_type) |pool_index| ft.doErrunionType(pool_index); + for (resolved.errunion_type_fwd) |pool_index| ft.doErrunionTypeFwd(pool_index); + for (resolved.aligned_type_fwd) |pool_index| ft.doAlignedTypeFwd(pool_index); + } + fn processDepsAsFwd(ft: *FlushTypes, deps: *const CTypeDependencies) void { + const resolved = deps.get(ft.c); + for (resolved.type) |pool_index| ft.doTypeFwd(pool_index); + for (resolved.type_fwd) |pool_index| ft.doTypeFwd(pool_index); + for (resolved.errunion_type) |pool_index| ft.doErrunionTypeFwd(pool_index); + for (resolved.errunion_type_fwd) |pool_index| ft.doErrunionTypeFwd(pool_index); + for (resolved.aligned_type_fwd) |pool_index| ft.doAlignedTypeFwd(pool_index); + } + + fn doAlignedTypeFwd(ft: *FlushTypes, pool_index: link.ConstPool.Index) void { + const c = ft.c; + if (ft.aligned_status.contains(pool_index)) return; + if (ft.aligned_types.getIndex(pool_index)) |i| { + const rendered = &c.types.items[@intFromEnum(pool_index)]; + ft.processDepsAsFwd(&rendered.deps); + ft.f.appendBufAssumeCapacity(ft.aligned_type_strings[i]); } - if (uav_align != .none) { - const gop = c.aligned_uavs.getOrPutAssumeCapacity(uav_val); - gop.value_ptr.* = if (gop.found_existing) max: { - break :max gop.value_ptr.*.maxStrict(uav_align); - } else uav_align; + ft.aligned_status.putAssumeCapacity(pool_index, {}); + } + fn doTypeFwd(ft: *FlushTypes, pool_index: link.ConstPool.Index) void { + const c = ft.c; + if (ft.status.contains(pool_index)) return; + const rendered = &c.types.items[@intFromEnum(pool_index)]; + if (rendered.fwd_decl.len > 0) { + ft.f.appendBufAssumeCapacity(rendered.fwd_decl.get(c)); + ft.status.putAssumeCapacityNoClobber(pool_index, false); + } else { + ft.processDepsAsFwd(&rendered.definition_deps); + const gop = ft.status.getOrPutAssumeCapacity(pool_index); + if (!gop.found_existing) { + gop.value_ptr.* = false; + ft.f.appendBufAssumeCapacity(rendered.definition.get(c)); + } } } -} + fn doType(ft: *FlushTypes, pool_index: link.ConstPool.Index) void { + const c = ft.c; + if (ft.status.get(pool_index)) |completed| { + if (completed) return; + } + const rendered = &c.types.items[@intFromEnum(pool_index)]; + ft.processDeps(&rendered.definition_deps); + if (rendered.fwd_decl.len == 0 and ft.status.contains(pool_index)) { + // `doTypeFwd` already rendered the defintion, we just had to complete the type by + // fully resolving its dependencies. + } else if (rendered.definition.len > 0) { + ft.f.appendBufAssumeCapacity(rendered.definition.get(c)); + } else if (!ft.status.contains(pool_index)) { + // The type will never be completed, but it must be forward declared to avoid it being + // declared in the wrong scope. + ft.f.appendBufAssumeCapacity(rendered.fwd_decl.get(c)); + } + ft.status.putAssumeCapacity(pool_index, true); + } + fn doErrunionTypeFwd(ft: *FlushTypes, pool_index: link.ConstPool.Index) void { + const c = ft.c; + const gop = ft.errunion_status.getOrPutAssumeCapacity(pool_index); + if (gop.found_existing) return; + const rendered = &c.types.items[@intFromEnum(pool_index)]; + ft.f.appendBufAssumeCapacity(rendered.errunion_fwd_decl.get(c)); + gop.value_ptr.* = false; + } + fn doErrunionType(ft: *FlushTypes, pool_index: link.ConstPool.Index) void { + const c = ft.c; + if (ft.errunion_status.get(pool_index)) |completed| { + if (completed) return; + } + const rendered = &c.types.items[@intFromEnum(pool_index)]; + ft.processDeps(&rendered.deps); + if (rendered.errunion_definition.len > 0) { + ft.f.appendBufAssumeCapacity(rendered.errunion_definition.get(c)); + } else { + // The error union type will never be completed, but forward declare it to avoid the + // type being first declared in a different scope. + ft.f.appendBufAssumeCapacity(rendered.errunion_fwd_decl.get(c)); + } + ft.errunion_status.putAssumeCapacity(pool_index, true); + } +}; diff --git a/src/link/Coff.zig b/src/link/Coff.zig @@ -1552,7 +1552,7 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde const sec_si = try coff.navSection(zcu, nav.status.fully_resolved); try coff.nodes.ensureUnusedCapacity(gpa, 1); const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{ - .alignment = pt.navAlignment(nav_index).toStdMem(), + .alignment = zcu.navAlignment(nav_index).toStdMem(), .moved = true, }); coff.nodes.appendAssumeCapacity(.{ .nav = nmi }); diff --git a/src/link/ConstPool.zig b/src/link/ConstPool.zig @@ -0,0 +1,288 @@ +/// Helper type for debug information implementations (such as `link.Dwarf`) to help them emit +/// information about comptime-known values (constants), including types. +/// +/// Every constant with associated debug information is assigned an `Index` by calling `get`. The +/// pool will track which container types do and do not have a resolved layout, as well as which +/// constants in the pool depend on which types, and call into the implementation to emit debug +/// information for a constant only when all information is available. +/// +/// Indices into the pool are dense, and constants are never removed from the pool, so the debug +/// info implementation can store information for each one with a simple `ArrayList`. +/// +/// To use `ConstPool`, the debug info implementation is required to: +/// * forward `updateContainerType` calls to its `ConstPool` +/// * expose some callback functions---see functions in `User` +/// * ensure that any `get` call is eventually followed by a `flushPending` call +const ConstPool = @This(); + +values: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), +pending: std.ArrayList(Index), +complete_containers: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), +container_deps: std.AutoArrayHashMapUnmanaged(InternPool.Index, ContainerDepEntry.Index), +container_dep_entries: std.ArrayList(ContainerDepEntry), + +pub const empty: ConstPool = .{ + .values = .empty, + .pending = .empty, + .complete_containers = .empty, + .container_deps = .empty, + .container_dep_entries = .empty, +}; + +pub fn deinit(pool: *ConstPool, gpa: Allocator) void { + pool.values.deinit(gpa); + pool.pending.deinit(gpa); + pool.complete_containers.deinit(gpa); + pool.container_deps.deinit(gpa); + pool.container_dep_entries.deinit(gpa); +} + +pub const Index = enum(u32) { + _, + pub fn val(i: Index, pool: *const ConstPool) InternPool.Index { + return pool.values.keys()[@intFromEnum(i)]; + } +}; + +pub const User = union(enum) { + dwarf: *@import("Dwarf.zig"), + c: *@import("C.zig"), + llvm: @import("../codegen/llvm.zig").Object.Ptr, + + /// Inform the debug info implementation that the new constant `val` was added to the pool at + /// the given index (which equals the current pool length) due to a `get` call. It is guaranteed + /// that there will eventually be a call to either `updateConst` or `updateConstIncomplete` + /// following the `addConst` call, to actually populate the constant's debug info. + fn addConst( + user: User, + pt: Zcu.PerThread, + index: Index, + val: InternPool.Index, + ) Allocator.Error!void { + switch (user) { + inline else => |impl| return impl.addConst(pt, index, val), + } + } + + /// Tell the debug info implementation to emit information for the constant `val`, which is in + /// the pool at the given index. `val` is "complete", which means: + /// * If it is a type, its layout is known. + /// * Otherwise, the layout of its type is known. + fn updateConst( + user: User, + pt: Zcu.PerThread, + index: Index, + val: InternPool.Index, + ) Allocator.Error!void { + switch (user) { + inline else => |impl| return impl.updateConst(pt, index, val), + } + } + + /// Tell the debug info implementation to emit information for the constant `val`, which is in + /// the pool at the given index. `val` is "incomplete", meaning the implementation cannot emit + /// full information for it (for instance, perhaps it is a struct type which was never actually + /// initialized so never had its layout resolved). Instead, the implementation must emit some + /// form of placeholder entry representing an incomplete/unknown constant. + fn updateConstIncomplete( + user: User, + pt: Zcu.PerThread, + index: Index, + val: InternPool.Index, + ) Allocator.Error!void { + switch (user) { + inline else => |impl| return impl.updateConstIncomplete(pt, index, val), + } + } +}; + +const ContainerDepEntry = extern struct { + next: ContainerDepEntry.Index.Optional, + depender: ConstPool.Index, + const Index = enum(u32) { + _, + const Optional = enum(u32) { + none = std.math.maxInt(u32), + _, + fn unwrap(o: Optional) ?ContainerDepEntry.Index { + return switch (o) { + .none => null, + else => @enumFromInt(@intFromEnum(o)), + }; + } + }; + fn toOptional(i: ContainerDepEntry.Index) Optional { + return @enumFromInt(@intFromEnum(i)); + } + fn ptr(i: ContainerDepEntry.Index, pool: *ConstPool) *ContainerDepEntry { + return &pool.container_dep_entries.items[@intFromEnum(i)]; + } + }; +}; + +/// Calls to `link.File.updateContainerType` must be forwarded to this function so that the debug +/// constant pool has up-to-date information about the resolution status of types. +pub fn updateContainerType( + pool: *ConstPool, + pt: Zcu.PerThread, + user: User, + container_ty: InternPool.Index, + success: bool, +) Allocator.Error!void { + if (success) { + const gpa = pt.zcu.comp.gpa; + try pool.complete_containers.put(gpa, container_ty, {}); + } else { + _ = pool.complete_containers.fetchSwapRemove(container_ty); + } + var opt_dep = pool.container_deps.get(container_ty); + while (opt_dep) |dep| : (opt_dep = dep.ptr(pool).next.unwrap()) { + try pool.update(pt, user, dep.ptr(pool).depender); + } +} + +/// After this is called, there may be a constant for which debug information (complete or not) has +/// not yet been emitted, so the user must call `flushPending` at some point after this call. +pub fn get(pool: *ConstPool, pt: Zcu.PerThread, user: User, val: InternPool.Index) Allocator.Error!ConstPool.Index { + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const gpa = zcu.comp.gpa; + const gop = try pool.values.getOrPut(gpa, val); + const index: ConstPool.Index = @enumFromInt(gop.index); + if (!gop.found_existing) { + const ty: Type = switch (ip.typeOf(val)) { + .type_type => if (ip.isUndef(val)) .type else .fromInterned(val), + else => |ty| .fromInterned(ty), + }; + try pool.registerTypeDeps(index, ty, zcu); + try pool.pending.append(gpa, index); + try user.addConst(pt, index, val); + } + return index; +} +pub fn flushPending(pool: *ConstPool, pt: Zcu.PerThread, user: User) Allocator.Error!void { + while (pool.pending.pop()) |pending_ty| { + try pool.update(pt, user, pending_ty); + } +} + +fn update(pool: *ConstPool, pt: Zcu.PerThread, user: User, index: ConstPool.Index) Allocator.Error!void { + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const val = index.val(pool); + const ty: Type = switch (ip.typeOf(val)) { + .type_type => if (ip.isUndef(val)) .type else .fromInterned(val), + else => |ty| .fromInterned(ty), + }; + if (pool.checkType(ty, zcu)) { + try user.updateConst(pt, index, val); + } else { + try user.updateConstIncomplete(pt, index, val); + } +} +fn checkType(pool: *const ConstPool, ty: Type, zcu: *const Zcu) bool { + if (ty.isGenericPoison()) return true; + return switch (ty.zigTypeTag(zcu)) { + .type, + .void, + .bool, + .noreturn, + .int, + .float, + .pointer, + .comptime_float, + .comptime_int, + .undefined, + .null, + .error_set, + .@"opaque", + .frame, + .@"anyframe", + .enum_literal, + => true, + + .array, .vector => pool.checkType(ty.childType(zcu), zcu), + .optional => pool.checkType(ty.optionalChild(zcu), zcu), + .error_union => pool.checkType(ty.errorUnionPayload(zcu), zcu), + .@"fn" => { + const ip = &zcu.intern_pool; + const func = ip.indexToKey(ty.toIntern()).func_type; + for (func.param_types.get(ip)) |param_ty_ip| { + if (!pool.checkType(.fromInterned(param_ty_ip), zcu)) return false; + } + return pool.checkType(.fromInterned(func.return_type), zcu); + }, + .@"struct" => if (ty.isTuple(zcu)) { + for (0..ty.structFieldCount(zcu)) |field_index| { + if (!pool.checkType(ty.fieldType(field_index, zcu), zcu)) return false; + } + return true; + } else { + return pool.complete_containers.contains(ty.toIntern()); + }, + .@"union", .@"enum" => { + return pool.complete_containers.contains(ty.toIntern()); + }, + }; +} +fn registerTypeDeps(pool: *ConstPool, root: Index, ty: Type, zcu: *const Zcu) Allocator.Error!void { + if (ty.isGenericPoison()) return; + switch (ty.zigTypeTag(zcu)) { + .type, + .void, + .bool, + .noreturn, + .int, + .float, + .pointer, + .comptime_float, + .comptime_int, + .undefined, + .null, + .error_set, + .@"opaque", + .frame, + .@"anyframe", + .enum_literal, + => {}, + + .array, .vector => try pool.registerTypeDeps(root, ty.childType(zcu), zcu), + .optional => try pool.registerTypeDeps(root, ty.optionalChild(zcu), zcu), + .error_union => try pool.registerTypeDeps(root, ty.errorUnionPayload(zcu), zcu), + .@"fn" => { + const ip = &zcu.intern_pool; + const func = ip.indexToKey(ty.toIntern()).func_type; + for (func.param_types.get(ip)) |param_ty_ip| { + try pool.registerTypeDeps(root, .fromInterned(param_ty_ip), zcu); + } + try pool.registerTypeDeps(root, .fromInterned(func.return_type), zcu); + }, + .@"struct", .@"union", .@"enum" => if (ty.isTuple(zcu)) { + for (0..ty.structFieldCount(zcu)) |field_index| { + try pool.registerTypeDeps(root, ty.fieldType(field_index, zcu), zcu); + } + } else { + // `ty` is a container; register the dependency. + + const gpa = zcu.comp.gpa; + try pool.container_deps.ensureUnusedCapacity(gpa, 1); + try pool.container_dep_entries.ensureUnusedCapacity(gpa, 1); + errdefer comptime unreachable; + + const gop = pool.container_deps.getOrPutAssumeCapacity(ty.toIntern()); + const entry: ContainerDepEntry.Index = @enumFromInt(pool.container_dep_entries.items.len); + pool.container_dep_entries.appendAssumeCapacity(.{ + .next = if (gop.found_existing) gop.value_ptr.toOptional() else .none, + .depender = root, + }); + gop.value_ptr.* = entry; + }, + } +} + +const std = @import("std"); +const Allocator = std.mem.Allocator; + +const InternPool = @import("../InternPool.zig"); +const Type = @import("../Type.zig"); +const Zcu = @import("../Zcu.zig"); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig @@ -25,9 +25,11 @@ format: DW.Format, endian: std.builtin.Endian, address_size: AddressSize, +const_pool: link.ConstPool, + mods: std.AutoArrayHashMapUnmanaged(*Module, ModInfo), -types: std.AutoArrayHashMapUnmanaged(InternPool.Index, Entry.Index), -values: std.AutoArrayHashMapUnmanaged(InternPool.Index, Entry.Index), +/// Indices are `link.ConstPool.Index`. +values: std.ArrayList(struct { Unit.Index, Entry.Index }), navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, Entry.Index), decls: std.AutoArrayHashMapUnmanaged(InternPool.TrackedInst.Index, Entry.Index), @@ -1034,15 +1036,14 @@ const Entry = struct { }); const zcu = dwarf.bin_file.comp.zcu.?; const ip = &zcu.intern_pool; - for (dwarf.types.keys(), dwarf.types.values()) |ty, other_entry| { - const ty_unit: Unit.Index = if (Type.fromInterned(ty).typeDeclInst(zcu)) |inst_index| - dwarf.getUnit(zcu.fileByIndex(inst_index.resolveFile(ip)).mod.?) catch unreachable - else - .main; - if (sec.getUnit(ty_unit) == unit and unit.getEntry(other_entry) == entry) - log.err("missing Type({f}({d}))", .{ - Type.fromInterned(ty).fmt(.{ .tid = .main, .zcu = zcu }), - @intFromEnum(ty), + for (0.., dwarf.values.items) |raw_index, unit_and_entry| { + const index: link.ConstPool.Index = @enumFromInt(raw_index); + const val = index.val(&dwarf.const_pool); + const val_unit, const val_entry = unit_and_entry; + if (sec.getUnit(val_unit) == unit and unit.getEntry(val_entry) == entry) + log.err("missing Value({f}({d}))", .{ + Value.fromInterned(val).fmtValue(.{ .tid = .main, .zcu = zcu }), + @intFromEnum(val), }); } for (dwarf.navs.keys(), dwarf.navs.values()) |nav, other_entry| { @@ -1520,7 +1521,6 @@ pub const WipNav = struct { debug_info: Writer.Allocating, debug_line: Writer.Allocating, debug_loclists: Writer.Allocating, - pending_lazy: PendingLazy, pub fn deinit(wip_nav: *WipNav) void { const gpa = wip_nav.dwarf.gpa; @@ -1529,8 +1529,6 @@ pub const WipNav = struct { wip_nav.debug_info.deinit(); wip_nav.debug_line.deinit(); wip_nav.debug_loclists.deinit(); - wip_nav.pending_lazy.types.deinit(gpa); - wip_nav.pending_lazy.values.deinit(gpa); } pub fn genDebugFrame(wip_nav: *WipNav, loc: u32, cfa: Cfa) UpdateError!void { @@ -1603,7 +1601,7 @@ pub const WipNav = struct { const zcu = pt.zcu; const ty = val.typeOf(zcu); const has_runtime_bits = ty.hasRuntimeBits(zcu); - const has_comptime_state = ty.comptimeOnly(zcu) and try ty.onePossibleValue(pt) == null; + const has_comptime_state = ty.comptimeOnly(zcu); try wip_nav.abbrevCode(if (has_runtime_bits and has_comptime_state) switch (tag) { .comptime_arg => if (opt_name) |_| .comptime_arg_runtime_bits_comptime_state else .unnamed_comptime_arg_runtime_bits_comptime_state, .local_const => if (opt_name) |_| .local_const_runtime_bits_comptime_state else unreachable, @@ -1945,6 +1943,12 @@ pub const WipNav = struct { try wip_nav.infoSectionOffset(.debug_str, StringSection.unit, try wip_nav.dwarf.debug_str.addString(wip_nav.dwarf, str), 0); } + fn strpFmt(wip_nav: *WipNav, comptime fmt: []const u8, args: anytype) (UpdateError || Writer.Error)!void { + const str = try std.fmt.allocPrint(wip_nav.dwarf.gpa, fmt, args); + defer wip_nav.dwarf.gpa.free(str); + return wip_nav.strp(str); + } + const ExprLocCounter = struct { dw: Writer.Discarding, section_offset_bytes: u32, @@ -2054,74 +2058,16 @@ pub const WipNav = struct { try dfw.splatByteAll(0, @intFromEnum(wip_nav.dwarf.address_size)); } - fn getNavEntry( - wip_nav: *WipNav, - nav_index: InternPool.Nav.Index, - ) UpdateError!struct { Unit.Index, Entry.Index } { - const zcu = wip_nav.pt.zcu; - const ip = &zcu.intern_pool; - const nav = ip.getNav(nav_index); - const unit = try wip_nav.dwarf.getUnit(zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?); - const gop = try wip_nav.dwarf.navs.getOrPut(wip_nav.dwarf.gpa, nav_index); - if (gop.found_existing) return .{ unit, gop.value_ptr.* }; - const entry = try wip_nav.dwarf.addCommonEntry(unit); - gop.value_ptr.* = entry; - return .{ unit, entry }; - } - fn refNav( wip_nav: *WipNav, nav_index: InternPool.Nav.Index, ) (UpdateError || Writer.Error)!void { - const unit, const entry = try wip_nav.getNavEntry(nav_index); + const unit, const entry = try wip_nav.dwarf.getNavEntry(nav_index); try wip_nav.infoSectionOffset(.debug_info, unit, entry, 0); } - fn getTypeEntry(wip_nav: *WipNav, ty: Type) UpdateError!struct { Unit.Index, Entry.Index } { - const zcu = wip_nav.pt.zcu; - const ip = &zcu.intern_pool; - const maybe_inst_index = ty.typeDeclInst(zcu); - const unit = if (maybe_inst_index) |inst_index| switch (switch (ip.indexToKey(ty.toIntern())) { - else => unreachable, - .struct_type => ip.loadStructType(ty.toIntern()).name_nav, - .union_type => ip.loadUnionType(ty.toIntern()).name_nav, - .enum_type => ip.loadEnumType(ty.toIntern()).name_nav, - .opaque_type => ip.loadOpaqueType(ty.toIntern()).name_nav, - }) { - .none => try wip_nav.dwarf.getUnit(zcu.fileByIndex(inst_index.resolveFile(ip)).mod.?), - else => |name_nav| return wip_nav.getNavEntry(name_nav.unwrap().?), - } else .main; - const gop = try wip_nav.dwarf.types.getOrPut(wip_nav.dwarf.gpa, ty.toIntern()); - if (gop.found_existing) return .{ unit, gop.value_ptr.* }; - const entry = try wip_nav.dwarf.addCommonEntry(unit); - gop.value_ptr.* = entry; - if (maybe_inst_index == null) try wip_nav.pending_lazy.types.append(wip_nav.dwarf.gpa, ty.toIntern()); - return .{ unit, entry }; - } - fn refType(wip_nav: *WipNav, ty: Type) (UpdateError || Writer.Error)!void { - const unit, const entry = try wip_nav.getTypeEntry(ty); - try wip_nav.infoSectionOffset(.debug_info, unit, entry, 0); - } - - fn getValueEntry(wip_nav: *WipNav, value: Value) UpdateError!struct { Unit.Index, Entry.Index } { - const zcu = wip_nav.pt.zcu; - const ip = &zcu.intern_pool; - const ty = value.typeOf(zcu); - if (std.debug.runtime_safety) assert(ty.comptimeOnly(zcu) and try ty.onePossibleValue(wip_nav.pt) == null); - if (ty.toIntern() == .type_type) return wip_nav.getTypeEntry(value.toType()); - if (ip.isFunctionType(ty.toIntern()) and !value.isUndef(zcu)) return wip_nav.getNavEntry(switch (ip.indexToKey(value.toIntern())) { - else => unreachable, - .func => |func| func.owner_nav, - .@"extern" => |@"extern"| @"extern".owner_nav, - }); - const gop = try wip_nav.dwarf.values.getOrPut(wip_nav.dwarf.gpa, value.toIntern()); - const unit: Unit.Index = .main; - if (gop.found_existing) return .{ unit, gop.value_ptr.* }; - const entry = try wip_nav.dwarf.addCommonEntry(unit); - gop.value_ptr.* = entry; - try wip_nav.pending_lazy.values.append(wip_nav.dwarf.gpa, value.toIntern()); - return .{ unit, entry }; + return wip_nav.refValue(ty.toValue()); } fn refValue(wip_nav: *WipNav, value: Value) (UpdateError || Writer.Error)!void { @@ -2129,6 +2075,15 @@ pub const WipNav = struct { try wip_nav.infoSectionOffset(.debug_info, unit, entry, 0); } + fn getValueEntry(wip_nav: *WipNav, value: Value) UpdateError!struct { Unit.Index, Entry.Index } { + if (value.typeOf(wip_nav.pt.zcu).toIntern() != .type_type) { + assert(value.typeOf(wip_nav.pt.zcu).comptimeOnly(wip_nav.pt.zcu)); + } + const dwarf = wip_nav.dwarf; + const index = try dwarf.const_pool.get(wip_nav.pt, .{ .dwarf = dwarf }, value.toIntern()); + return dwarf.values.items[@intFromEnum(index)]; + } + fn refForward(wip_nav: *WipNav) (Allocator.Error || Writer.Error)!u32 { const dwarf = wip_nav.dwarf; const diw = &wip_nav.debug_info.writer; @@ -2156,7 +2111,7 @@ pub const WipNav = struct { ) (UpdateError || Writer.Error)!void { const ty = val.typeOf(wip_nav.pt.zcu); const diw = &wip_nav.debug_info.writer; - const size = if (ty.hasRuntimeBits(wip_nav.pt.zcu)) ty.abiSize(wip_nav.pt.zcu) else 0; + const size = ty.abiSize(wip_nav.pt.zcu); try diw.writeUleb128(size); if (size == 0) return; const old_end = wip_nav.debug_info.writer.end; @@ -2243,8 +2198,8 @@ pub const WipNav = struct { const zcu = wip_nav.pt.zcu; const ip = &zcu.intern_pool; var big_int_space: Value.BigIntSpace = undefined; - try wip_nav.bigIntConstValue(abbrev_code, .fromInterned(loaded_enum.tag_ty), if (loaded_enum.values.len > 0) - Value.fromInterned(loaded_enum.values.get(ip)[field_index]).toBigInt(&big_int_space, zcu) + try wip_nav.bigIntConstValue(abbrev_code, .fromInterned(loaded_enum.int_tag_type), if (loaded_enum.field_values.len > 0) + Value.fromInterned(loaded_enum.field_values.get(ip)[field_index]).toBigInt(&big_int_space, zcu) else std.math.big.int.Mutable.init(&big_int_space.limbs, field_index).toConst()); } @@ -2297,6 +2252,12 @@ pub const WipNav = struct { .generic_decl_const, .generic_decl_func, => true, + + // This comes from a decl which was previously generated as an incomplete value + // (I think that must mean either a function or an extern which previously had + // incomplete types). + .undefined_comptime_value => false, + else => |t| std.debug.panic("bad decl abbrev code: {t}", .{t}), }; if (parent_type.getCaptures(zcu).len == 0) { @@ -2331,22 +2292,6 @@ pub const WipNav = struct { try wip_nav.refType(parent_type.?); try wip_nav.infoSectionOffset(.debug_info, wip_nav.unit, generic_decl_entry, 0); } - - const PendingLazy = struct { - types: std.ArrayList(InternPool.Index), - values: std.ArrayList(InternPool.Index), - - const empty: PendingLazy = .{ .types = .empty, .values = .empty }; - }; - - fn updateLazy(wip_nav: *WipNav, src_loc: Zcu.LazySrcLoc) (UpdateError || Writer.Error)!void { - while (true) if (wip_nav.pending_lazy.types.pop()) |pending_ty| - try wip_nav.dwarf.updateLazyType(wip_nav.pt, src_loc, pending_ty, &wip_nav.pending_lazy) - else if (wip_nav.pending_lazy.values.pop()) |pending_val| - try wip_nav.dwarf.updateLazyValue(wip_nav.pt, src_loc, pending_val, &wip_nav.pending_lazy) - else - break; - } }; /// When allocating, the ideal_capacity is calculated by @@ -2372,8 +2317,9 @@ pub fn init(lf: *link.File, format: DW.Format) Dwarf { }, .endian = target.cpu.arch.endian(), + .const_pool = .empty, + .mods = .empty, - .types = .empty, .values = .empty, .navs = .empty, .decls = .empty, @@ -2544,9 +2490,9 @@ pub fn initMetadata(dwarf: *Dwarf) UpdateError!void { pub fn deinit(dwarf: *Dwarf) void { const gpa = dwarf.gpa; + dwarf.const_pool.deinit(gpa); for (dwarf.mods.values()) |*mod_info| mod_info.deinit(gpa); dwarf.mods.deinit(gpa); - dwarf.types.deinit(gpa); dwarf.values.deinit(gpa); dwarf.navs.deinit(gpa); dwarf.decls.deinit(gpa); @@ -2562,6 +2508,21 @@ pub fn deinit(dwarf: *Dwarf) void { dwarf.* = undefined; } +fn getNavEntry( + dwarf: *Dwarf, + nav_index: InternPool.Nav.Index, +) UpdateError!struct { Unit.Index, Entry.Index } { + const zcu = dwarf.bin_file.comp.zcu.?; + const ip = &zcu.intern_pool; + const nav = ip.getNav(nav_index); + const unit = try dwarf.getUnit(zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?); + const gop = try dwarf.navs.getOrPut(dwarf.gpa, nav_index); + if (gop.found_existing) return .{ unit, gop.value_ptr.* }; + const entry = try dwarf.addCommonEntry(unit); + gop.value_ptr.* = entry; + return .{ unit, entry }; +} + fn getUnit(dwarf: *Dwarf, mod: *Module) !Unit.Index { const mod_gop = try dwarf.mods.getOrPut(dwarf.gpa, mod); const unit: Unit.Index = @enumFromInt(mod_gop.index); @@ -2622,6 +2583,10 @@ fn getModInfo(dwarf: *Dwarf, unit: Unit.Index) *ModInfo { return &dwarf.mods.values()[@intFromEnum(unit)]; } +fn getUnitModule(dwarf: *Dwarf, unit: Unit.Index) *Module { + return dwarf.mods.keys()[@intFromEnum(unit)]; +} + pub fn initWipNav( dwarf: *Dwarf, pt: Zcu.PerThread, @@ -2683,7 +2648,6 @@ fn initWipNavInner( .debug_info = .init(dwarf.gpa), .debug_line = .init(dwarf.gpa), .debug_loclists = .init(dwarf.gpa), - .pending_lazy = .empty, }; errdefer wip_nav.deinit(); @@ -2705,7 +2669,7 @@ fn initWipNavInner( try wip_nav.refType(.fromInterned(if (maybe_func_type) |func_type| func_type.return_type else @"extern".ty)); if (maybe_func_type) |func_type| { try wip_nav.infoAddrSym(sym_index, 0); - try diw.writeByte(@intFromBool(ip.isNoReturn(func_type.return_type))); + try diw.writeByte(@intFromBool(Type.fromInterned(func_type.return_type).isNoReturn(zcu))); if (func_type.param_types.len > 0 or func_type.is_var_args) { for (func_type.param_types.get(ip)) |param_type| { try wip_nav.abbrevCode(.extern_param); @@ -2733,7 +2697,7 @@ fn initWipNavInner( try wip_nav.strp(@"extern".name.toSlice(ip)); try wip_nav.refType(.fromInterned(func_type.return_type)); try wip_nav.infoAddrSym(sym_index, 0); - try diw.writeByte(@intFromBool(ip.isNoReturn(func_type.return_type))); + try diw.writeByte(@intFromBool(Type.fromInterned(func_type.return_type).isNoReturn(zcu))); if (func_type.param_types.len > 0 or func_type.is_var_args) { for (func_type.param_types.get(ip)) |param_type| { try wip_nav.abbrevCode(.extern_param); @@ -2818,7 +2782,7 @@ fn initWipNavInner( else => |a| a.maxStrict(target_info.minFunctionAlignment(target)), }.toByteUnits().?); try diw.writeByte(@intFromBool(decl.linkage != .normal)); - try diw.writeByte(@intFromBool(ip.isNoReturn(func_type.return_type))); + try diw.writeByte(@intFromBool(Type.fromInterned(func_type.return_type).isNoReturn(zcu))); const dlw = &wip_nav.debug_line.writer; try dlw.writeByte(DW.LNS.extended_op); @@ -3050,7 +3014,7 @@ fn finishWipNavWriterError( } try dwarf.debug_loclists.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_loclists.written()); - try wip_nav.updateLazy(zcu.navSrcLoc(nav_index)); + try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); } pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{ OutOfMemory, CodegenFail }!void { @@ -3087,34 +3051,12 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo return; } - var wip_nav: WipNav = .{ - .dwarf = dwarf, - .pt = pt, - .unit = try dwarf.getUnit(file.mod.?), - .entry = undefined, - .any_children = false, - .func = .none, - .func_sym_index = undefined, - .func_high_pc = undefined, - .blocks = undefined, - .cfi = undefined, - .debug_frame = .init(dwarf.gpa), - .debug_info = .init(dwarf.gpa), - .debug_line = .init(dwarf.gpa), - .debug_loclists = .init(dwarf.gpa), - .pending_lazy = .empty, - }; - defer wip_nav.deinit(); - - const nav_gop = try dwarf.navs.getOrPut(dwarf.gpa, nav_index); - errdefer _ = if (!nav_gop.found_existing) dwarf.navs.pop(); - const tag: union(enum) { - done, - decl_alias, - decl_var, - decl_const, - decl_func_alias: InternPool.Nav.Index, + alias, + @"var", + @"const", + func: Type, + func_alias: InternPool.Nav.Index, } = switch (ip.indexToKey(nav_val.toIntern())) { .int_type, .ptr_type, @@ -3128,242 +3070,49 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo .func_type, .error_set_type, .inferred_error_set_type, - => .decl_alias, + => .alias, + .struct_type => tag: { const loaded_struct = ip.loadStructType(nav_val.toIntern()); - if (loaded_struct.zir_index.resolveFile(ip) != inst_info.file) break :tag .decl_alias; - - const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern()); - if (type_gop.found_existing) { - if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias; - assert(!nav_gop.found_existing); - nav_gop.value_ptr.* = type_gop.value_ptr.*; - } else { - if (nav_gop.found_existing) - dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() - else - nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); - type_gop.value_ptr.* = nav_gop.value_ptr.*; - } - wip_nav.entry = nav_gop.value_ptr.*; - - const diw = &wip_nav.debug_info.writer; - - switch (loaded_struct.layout) { - .auto, .@"extern" => { - try wip_nav.declCommon(if (loaded_struct.field_types.len == 0) .{ - .decl = .decl_namespace_struct, - .generic_decl = .generic_decl_const, - .decl_instance = .decl_instance_namespace_struct, - } else .{ - .decl = .decl_struct, - .generic_decl = .generic_decl_const, - .decl_instance = .decl_instance_struct, - }, &nav, inst_info.file, &decl); - if (loaded_struct.field_types.len == 0) try diw.writeByte(@intFromBool(false)) else { - try diw.writeUleb128(nav_val.toType().abiSize(zcu)); - try diw.writeUleb128(nav_val.toType().abiAlignment(zcu).toByteUnits().?); - for (0..loaded_struct.field_types.len) |field_index| { - const is_comptime = loaded_struct.fieldIsComptime(ip, field_index); - const field_init = loaded_struct.fieldInit(ip, field_index); - assert(!(is_comptime and field_init == .none)); - const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - const has_runtime_bits, const has_comptime_state = switch (field_init) { - .none => .{ false, false }, - else => .{ - field_type.hasRuntimeBits(zcu), - field_type.comptimeOnly(zcu) and try field_type.onePossibleValue(pt) == null, - }, - }; - try wip_nav.abbrevCode(if (is_comptime) - if (has_comptime_state) - .struct_field_comptime_comptime_state - else if (has_runtime_bits) - .struct_field_comptime_runtime_bits - else - .struct_field_comptime - else if (field_init != .none) - if (has_comptime_state) - .struct_field_default_comptime_state - else if (has_runtime_bits) - .struct_field_default_runtime_bits - else - .struct_field - else - .struct_field); - try wip_nav.strp(loaded_struct.fieldName(ip, field_index).toSlice(ip)); - try wip_nav.refType(field_type); - if (!is_comptime) { - try diw.writeUleb128(loaded_struct.offsets.get(ip)[field_index]); - try diw.writeUleb128(loaded_struct.fieldAlign(ip, field_index).toByteUnits() orelse - field_type.abiAlignment(zcu).toByteUnits().?); - } - if (has_comptime_state) - try wip_nav.refValue(.fromInterned(field_init)) - else if (has_runtime_bits) - try wip_nav.blockValue(nav_src_loc, .fromInterned(field_init)); - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - } - }, - .@"packed" => { - try wip_nav.declCommon(.{ - .decl = .decl_packed_struct, - .generic_decl = .generic_decl_const, - .decl_instance = .decl_instance_packed_struct, - }, &nav, inst_info.file, &decl); - try wip_nav.refType(.fromInterned(loaded_struct.backingIntTypeUnordered(ip))); - var field_bit_offset: u16 = 0; - for (0..loaded_struct.field_types.len) |field_index| { - try wip_nav.abbrevCode(.packed_struct_field); - try wip_nav.strp(loaded_struct.fieldName(ip, field_index).toSlice(ip)); - const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - try wip_nav.refType(field_type); - try diw.writeUleb128(field_bit_offset); - field_bit_offset += @intCast(field_type.bitSize(zcu)); - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - }, + if (nav_index.toOptional() == loaded_struct.name_nav) { + // This Nav's entry is populated by the type, not the actual Nav. + _ = try dwarf.const_pool.get(pt, .{ .dwarf = dwarf }, nav_val.toIntern()); + try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); + return; } - break :tag .done; + break :tag .alias; }, .enum_type => tag: { const loaded_enum = ip.loadEnumType(nav_val.toIntern()); - const type_zir_index = loaded_enum.zir_index.unwrap() orelse break :tag .decl_alias; - if (type_zir_index.resolveFile(ip) != inst_info.file) break :tag .decl_alias; - - const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern()); - if (type_gop.found_existing) { - if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias; - assert(!nav_gop.found_existing); - nav_gop.value_ptr.* = type_gop.value_ptr.*; - } else { - if (nav_gop.found_existing) - dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() - else - nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); - type_gop.value_ptr.* = nav_gop.value_ptr.*; - } - wip_nav.entry = nav_gop.value_ptr.*; - const diw = &wip_nav.debug_info.writer; - try wip_nav.declCommon(if (loaded_enum.names.len > 0) .{ - .decl = .decl_enum, - .generic_decl = .generic_decl_const, - .decl_instance = .decl_instance_enum, - } else .{ - .decl = .decl_empty_enum, - .generic_decl = .generic_decl_const, - .decl_instance = .decl_instance_empty_enum, - }, &nav, inst_info.file, &decl); - try wip_nav.refType(.fromInterned(loaded_enum.tag_ty)); - for (0..loaded_enum.names.len) |field_index| { - try wip_nav.enumConstValue(loaded_enum, .{ - .sdata = .signed_enum_field, - .udata = .unsigned_enum_field, - .block = .big_enum_field, - }, field_index); - try wip_nav.strp(loaded_enum.names.get(ip)[field_index].toSlice(ip)); + if (nav_index.toOptional() == loaded_enum.name_nav) { + // This Nav's entry is populated by the type, not the actual Nav. + _ = try dwarf.const_pool.get(pt, .{ .dwarf = dwarf }, nav_val.toIntern()); + try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); + return; } - if (loaded_enum.names.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - break :tag .done; + break :tag .alias; }, .union_type => tag: { const loaded_union = ip.loadUnionType(nav_val.toIntern()); - if (loaded_union.zir_index.resolveFile(ip) != inst_info.file) break :tag .decl_alias; - - const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern()); - if (type_gop.found_existing) { - if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias; - assert(!nav_gop.found_existing); - nav_gop.value_ptr.* = type_gop.value_ptr.*; - } else { - if (nav_gop.found_existing) - dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() - else - nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); - type_gop.value_ptr.* = nav_gop.value_ptr.*; - } - wip_nav.entry = nav_gop.value_ptr.*; - const diw = &wip_nav.debug_info.writer; - try wip_nav.declCommon(.{ - .decl = .decl_union, - .generic_decl = .generic_decl_const, - .decl_instance = .decl_instance_union, - }, &nav, inst_info.file, &decl); - const union_layout = Type.getUnionLayout(loaded_union, zcu); - try diw.writeUleb128(union_layout.abi_size); - try diw.writeUleb128(union_layout.abi_align.toByteUnits().?); - const loaded_tag = loaded_union.loadTagType(ip); - if (loaded_union.hasTag(ip)) { - try wip_nav.abbrevCode(.tagged_union); - try wip_nav.infoSectionOffset( - .debug_info, - wip_nav.unit, - wip_nav.entry, - @intCast(diw.end + dwarf.sectionOffsetBytes()), - ); - { - try wip_nav.abbrevCode(.generated_field); - try wip_nav.strp("tag"); - try wip_nav.refType(.fromInterned(loaded_union.enum_tag_ty)); - try diw.writeUleb128(union_layout.tagOffset()); - - for (0..loaded_union.field_types.len) |field_index| { - try wip_nav.enumConstValue(loaded_tag, .{ - .sdata = .signed_tagged_union_field, - .udata = .unsigned_tagged_union_field, - .block = .big_tagged_union_field, - }, field_index); - { - try wip_nav.abbrevCode(.struct_field); - try wip_nav.strp(loaded_tag.names.get(ip)[field_index].toSlice(ip)); - const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); - try wip_nav.refType(field_type); - try diw.writeUleb128(union_layout.payloadOffset()); - try diw.writeUleb128(loaded_union.fieldAlign(ip, field_index).toByteUnits() orelse - if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?); - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - } - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - } else for (0..loaded_union.field_types.len) |field_index| { - try wip_nav.abbrevCode(.untagged_union_field); - try wip_nav.strp(loaded_tag.names.get(ip)[field_index].toSlice(ip)); - const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); - try wip_nav.refType(field_type); - try diw.writeUleb128(loaded_union.fieldAlign(ip, field_index).toByteUnits() orelse - if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?); + if (nav_index.toOptional() == loaded_union.name_nav) { + // This Nav's entry is populated by the type, not the actual Nav. + _ = try dwarf.const_pool.get(pt, .{ .dwarf = dwarf }, nav_val.toIntern()); + try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); + return; } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - break :tag .done; + break :tag .alias; }, .opaque_type => tag: { const loaded_opaque = ip.loadOpaqueType(nav_val.toIntern()); - if (loaded_opaque.zir_index.resolveFile(ip) != inst_info.file) break :tag .decl_alias; - - const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern()); - if (type_gop.found_existing) { - if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias; - assert(!nav_gop.found_existing); - nav_gop.value_ptr.* = type_gop.value_ptr.*; - } else { - if (nav_gop.found_existing) - dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() - else - nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); - type_gop.value_ptr.* = nav_gop.value_ptr.*; + if (nav_index.toOptional() == loaded_opaque.name_nav) { + // This Nav's entry is populated by the type, not the actual Nav. + _ = try dwarf.const_pool.get(pt, .{ .dwarf = dwarf }, nav_val.toIntern()); + try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); + return; } - wip_nav.entry = nav_gop.value_ptr.*; - const diw = &wip_nav.debug_info.writer; - try wip_nav.declCommon(.{ - .decl = .decl_namespace_struct, - .generic_decl = .generic_decl_const, - .decl_instance = .decl_instance_namespace_struct, - }, &nav, inst_info.file, &decl); - try diw.writeByte(@intFromBool(true)); - break :tag .done; + break :tag .alias; }, + .undef, .simple_value, .int, @@ -3371,70 +3120,76 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo .error_union, .enum_literal, .enum_tag, - .empty_enum_value, .float, .ptr, .slice, .opt, .aggregate, .un, - => .decl_const, - .variable => .decl_var, + .bitpack, + => .@"const", + + .variable => .@"var", + .@"extern" => unreachable, - .func => |func| tag: { - if (func.owner_nav != nav_index) break :tag .{ .decl_func_alias = func.owner_nav }; - if (nav_gop.found_existing) switch (try dwarf.debug_info.declAbbrevCode(wip_nav.unit, nav_gop.value_ptr.*)) { - .null => {}, - else => unreachable, - .decl_nullary_func, .decl_func, .decl_instance_nullary_func, .decl_instance_func => return, - .decl_nullary_func_generic, - .decl_func_generic, - .decl_instance_nullary_func_generic, - .decl_instance_func_generic, - => dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear(), - } else nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); - wip_nav.entry = nav_gop.value_ptr.*; - const func_type = ip.indexToKey(func.ty).func_type; - const is_nullary = !func_type.is_var_args and for (0..func_type.param_types.len) |param_index| { - if (!func_type.paramIsComptime(std.math.cast(u5, param_index) orelse break false)) break false; - } else true; - const diw = &wip_nav.debug_info.writer; - try wip_nav.declCommon(if (is_nullary) .{ - .decl = .decl_nullary_func_generic, - .generic_decl = .generic_decl_func, - .decl_instance = .decl_instance_nullary_func_generic, - } else .{ - .decl = .decl_func_generic, - .generic_decl = .generic_decl_func, - .decl_instance = .decl_instance_func_generic, - }, &nav, inst_info.file, &decl); - try wip_nav.refType(.fromInterned(func_type.return_type)); - if (!is_nullary) { - for (0..func_type.param_types.len) |param_index| { - if (std.math.cast(u5, param_index)) |small_param_index| - if (func_type.paramIsComptime(small_param_index)) continue; - try wip_nav.abbrevCode(.func_type_param); - try wip_nav.refType(.fromInterned(func_type.param_types.get(ip)[param_index])); - } - if (func_type.is_var_args) try wip_nav.abbrevCode(.is_var_args); - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - } - break :tag .done; + .func => |func| tag: { + if (func.owner_nav != nav_index) break :tag .{ .func_alias = func.owner_nav }; + break :tag .{ .func = .fromInterned(func.ty) }; }, + // memoization, not types .memoized_call => unreachable, }; - if (tag != .done) { - if (nav_gop.found_existing) - dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() - else - nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); - wip_nav.entry = nav_gop.value_ptr.*; + + const unit = try dwarf.getUnit(file.mod.?); + + const nav_gop = try dwarf.navs.getOrPut(dwarf.gpa, nav_index); + errdefer _ = if (!nav_gop.found_existing) dwarf.navs.pop(); + + if (nav_gop.found_existing) { + if (tag == .func) switch (try dwarf.debug_info.declAbbrevCode(unit, nav_gop.value_ptr.*)) { + else => unreachable, + + .decl_nullary_func, + .decl_func, + .decl_instance_nullary_func, + .decl_instance_func, + => return, + + .null, + .decl_nullary_func_generic, + .decl_func_generic, + .decl_instance_nullary_func_generic, + .decl_instance_func_generic, + => {}, + }; + dwarf.debug_info.section.getUnit(unit).getEntry(nav_gop.value_ptr.*).clear(); + } else { + nav_gop.value_ptr.* = try dwarf.addCommonEntry(unit); } + + var wip_nav: WipNav = .{ + .dwarf = dwarf, + .pt = pt, + .unit = unit, + .entry = nav_gop.value_ptr.*, + .any_children = false, + .func = .none, + .func_sym_index = undefined, + .func_high_pc = undefined, + .blocks = undefined, + .cfi = undefined, + .debug_frame = .init(dwarf.gpa), + .debug_info = .init(dwarf.gpa), + .debug_line = .init(dwarf.gpa), + .debug_loclists = .init(dwarf.gpa), + }; + defer wip_nav.deinit(); + const diw = &wip_nav.debug_info.writer; + switch (tag) { - .done => {}, - .decl_alias => { + .alias => { try wip_nav.declCommon(.{ .decl = .decl_alias, .generic_decl = .generic_decl_const, @@ -3442,8 +3197,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo }, &nav, inst_info.file, &decl); try wip_nav.refType(nav_val.toType()); }, - .decl_var => { - const diw = &wip_nav.debug_info.writer; + .@"var" => { try wip_nav.declCommon(.{ .decl = .decl_var, .generic_decl = .generic_decl_var, @@ -3460,11 +3214,10 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo nav_ty.abiAlignment(zcu).toByteUnits().?); try diw.writeByte(@intFromBool(decl.linkage != .normal)); }, - .decl_const => { - const diw = &wip_nav.debug_info.writer; + .@"const" => { const nav_ty = nav_val.typeOf(zcu); const has_runtime_bits = nav_ty.hasRuntimeBits(zcu); - const has_comptime_state = nav_ty.comptimeOnly(zcu) and try nav_ty.onePossibleValue(pt) == null; + const has_comptime_state = nav_ty.comptimeOnly(zcu); try wip_nav.declCommon(if (has_runtime_bits and has_comptime_state) .{ .decl = .decl_const_runtime_bits_comptime_state, .generic_decl = .generic_decl_const, @@ -3496,40 +3249,129 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo try wip_nav.abbrevCode(.is_const); try wip_nav.refType(nav_ty); }, - .decl_func_alias => |owner_nav| { - try wip_nav.declCommon(.{ - .decl = .decl_alias, - .generic_decl = .generic_decl_const, - .decl_instance = .decl_instance_alias, + .func => |func_ty| { + const func_type = ip.indexToKey(func_ty.toIntern()).func_type; + const is_nullary = !func_type.is_var_args and for (0..func_type.param_types.len) |param_index| { + if (!func_type.paramIsComptime(std.math.cast(u5, param_index) orelse break false)) break false; + } else true; + try wip_nav.declCommon(if (is_nullary) .{ + .decl = .decl_nullary_func_generic, + .generic_decl = .generic_decl_func, + .decl_instance = .decl_instance_nullary_func_generic, + } else .{ + .decl = .decl_func_generic, + .generic_decl = .generic_decl_func, + .decl_instance = .decl_instance_func_generic, }, &nav, inst_info.file, &decl); - try wip_nav.refNav(owner_nav); - }, - } - try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written()); - try wip_nav.updateLazy(nav_src_loc); + try wip_nav.refType(.fromInterned(func_type.return_type)); + if (!is_nullary) { + for (0..func_type.param_types.len) |param_index| { + if (std.math.cast(u5, param_index)) |small_param_index| + if (func_type.paramIsComptime(small_param_index)) continue; + try wip_nav.abbrevCode(.func_type_param); + try wip_nav.refType(.fromInterned(func_type.param_types.get(ip)[param_index])); + } + if (func_type.is_var_args) try wip_nav.abbrevCode(.is_var_args); + try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + } + }, + .func_alias => |owner_nav| { + try wip_nav.declCommon(.{ + .decl = .decl_alias, + .generic_decl = .generic_decl_const, + .decl_instance = .decl_instance_alias, + }, &nav, inst_info.file, &decl); + try wip_nav.refNav(owner_nav); + }, + } + try dwarf.debug_info.section.replaceEntry(unit, wip_nav.entry, dwarf, wip_nav.debug_info.written()); + try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); } -fn updateLazyType( +pub fn updateContainerType( dwarf: *Dwarf, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, - type_index: InternPool.Index, - pending_lazy: *WipNav.PendingLazy, -) (UpdateError || Writer.Error)!void { + ty: InternPool.Index, + success: bool, +) !void { + try dwarf.const_pool.updateContainerType(pt, .{ .dwarf = dwarf }, ty, success); +} +/// Should only be called by the `link.ConstPool` implementation. +pub fn addConst(dwarf: *Dwarf, pt: Zcu.PerThread, index: link.ConstPool.Index, val: InternPool.Index) Allocator.Error!void { + addConstInner(dwarf, pt, index, val) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + else => |e| std.debug.panic("DWARF TODO: '{t}' while registering constant\n", .{e}), + }; +} +fn addConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, index: link.ConstPool.Index, val: InternPool.Index) !void { const zcu = pt.zcu; const ip = &zcu.intern_pool; - assert(ip.typeOf(type_index) == .type_type); - const ty: Type = .fromInterned(type_index); - switch (type_index) { - .generic_poison_type => log.debug("updateLazyType({s})", .{"anytype"}), - else => log.debug("updateLazyType({f})", .{ty.fmt(pt)}), + + const unit: Unit.Index, const entry: Entry.Index = switch (ip.indexToKey(val)) { + else => .{ .main, try dwarf.addCommonEntry(.main) }, + .func => |func| try dwarf.getNavEntry(func.owner_nav), + .@"extern" => |@"extern"| try dwarf.getNavEntry(@"extern".owner_nav), + .struct_type, .union_type, .enum_type, .opaque_type => |_, tag| entry: { + const name_nav = switch (tag) { + .struct_type => ip.loadStructType(val).name_nav, + .union_type => ip.loadUnionType(val).name_nav, + .enum_type => ip.loadEnumType(val).name_nav, + .opaque_type => ip.loadOpaqueType(val).name_nav, + else => unreachable, + }; + if (name_nav.unwrap()) |nav| { + break :entry try dwarf.getNavEntry(nav); + } else { + const zir_index = Type.fromInterned(val).typeDeclInstAllowGeneratedTag(zcu).?; + const unit = try dwarf.getUnit(zcu.fileByIndex(zir_index.resolveFile(ip)).mod.?); + break :entry .{ unit, try dwarf.addCommonEntry(unit) }; + } + }, + }; + + assert(@intFromEnum(index) == dwarf.values.items.len); + try dwarf.values.append(dwarf.gpa, .{ unit, entry }); +} +/// Should only be called by the `link.ConstPool` implementation. +/// +/// Emits a "dummy" DIE for the given comptime-only value (which may be a type). For types, this is +/// an opaque type. Otherwise, it is an undefined value of the value's type. +pub fn updateConstIncomplete(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.ConstPool.Index, value_index: InternPool.Index) Allocator.Error!void { + updateConstIncompleteInner(dwarf, pt, debug_const_index, value_index) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + else => |e| std.debug.panic("DWARF TODO: '{t}' while updating incomplete constant\n", .{e}), + }; +} +fn updateConstIncompleteInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.ConstPool.Index, value_index: InternPool.Index) !void { + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + + const val: Value = .fromInterned(value_index); + + switch (value_index) { + .generic_poison_type => log.debug("updateValueIncomplete(anytype)", .{}), + else => log.debug("updateValueIncomplete(@as({f}, {f}))", .{ + val.typeOf(zcu).fmt(pt), + val.fmtValue(pt), + }), } + const unit, const entry = dwarf.values.items[@intFromEnum(debug_const_index)]; + + for ([_]*Section{ + &dwarf.debug_aranges.section, + &dwarf.debug_aranges.section, + &dwarf.debug_info.section, + &dwarf.debug_line.section, + &dwarf.debug_loclists.section, + &dwarf.debug_rnglists.section, + }) |sec| sec.getUnit(unit).getEntry(entry).clear(); + var wip_nav: WipNav = .{ .dwarf = dwarf, .pt = pt, - .unit = .main, - .entry = dwarf.types.get(type_index).?, + .unit = unit, + .entry = entry, .any_children = false, .func = .none, .func_sym_index = undefined, @@ -3540,43 +3382,216 @@ fn updateLazyType( .debug_info = .init(dwarf.gpa), .debug_line = .init(dwarf.gpa), .debug_loclists = .init(dwarf.gpa), - .pending_lazy = pending_lazy.*, }; - defer { - pending_lazy.* = wip_nav.pending_lazy; - wip_nav.pending_lazy = .empty; - wip_nav.deinit(); + defer wip_nav.deinit(); + + switch (ip.indexToKey(value_index)) { + // Container types still need to be valid namespaces. + .struct_type => { + const loaded_struct = ip.loadStructType(value_index); + const root_of_file: ?Zcu.File.Index = if (loaded_struct.zir_index.resolveFull(ip)) |r| f: { + if (r.inst != .main_struct_inst) break :f null; + break :f r.file; + } else null; + if (root_of_file) |file_index| { + assert(loaded_struct.name_nav == .none); + const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file_index); + try wip_nav.abbrevCode(.empty_file); + try wip_nav.debug_info.writer.writeUleb128(file_gop.index); + try wip_nav.strp(loaded_struct.name.toSlice(ip)); + } else { + try dwarf.emitIncompleteContainerType( + &wip_nav, + loaded_struct.zir_index, + loaded_struct.name, + loaded_struct.name_nav, + ); + } + }, + .union_type => { + const loaded_union = ip.loadUnionType(value_index); + try dwarf.emitIncompleteContainerType( + &wip_nav, + loaded_union.zir_index, + loaded_union.name, + loaded_union.name_nav, + ); + }, + .enum_type => { + const loaded_enum = ip.loadEnumType(value_index); + if (loaded_enum.zir_index.unwrap()) |zir_index| { + try dwarf.emitIncompleteContainerType( + &wip_nav, + zir_index, + loaded_enum.name, + loaded_enum.name_nav, + ); + } else { + try wip_nav.abbrevCode(.generated_empty_struct_type); + try wip_nav.strp(loaded_enum.name.toSlice(ip)); + try wip_nav.debug_info.writer.writeByte(@intFromBool(true)); + } + }, + .opaque_type => { + const loaded_opaque = ip.loadOpaqueType(value_index); + try dwarf.emitIncompleteContainerType( + &wip_nav, + loaded_opaque.zir_index, + loaded_opaque.name, + loaded_opaque.name_nav, + ); + }, + // Not a container type, so just emit a dummy entry. If `val` happens to be a type, we'll + // emit it as if it were an opaque type so that we can name it. + else => |val_key| switch (val_key.typeOf()) { + .type_type => { + try wip_nav.abbrevCode(.generated_empty_struct_type); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); + try wip_nav.debug_info.writer.writeByte(@intFromBool(true)); + }, + else => |ty| { + try wip_nav.abbrevCode(.undefined_comptime_value); + try wip_nav.refType(.fromInterned(ty)); + }, + }, } - const diw = &wip_nav.debug_info.writer; - const name = switch (type_index) { - .generic_poison_type => "", - else => try std.fmt.allocPrint(dwarf.gpa, "{f}", .{ty.fmt(pt)}), + try dwarf.debug_info.section.replaceEntry(unit, entry, dwarf, wip_nav.debug_info.written()); + try dwarf.debug_loclists.section.replaceEntry(unit, entry, dwarf, wip_nav.debug_loclists.written()); +} +fn emitIncompleteContainerType( + dwarf: *Dwarf, + wip_nav: *WipNav, + zir_index: InternPool.TrackedInst.Index, + name: InternPool.NullTerminatedString, + name_nav: InternPool.Nav.Index.Optional, +) !void { + const zcu = wip_nav.pt.zcu; + const ip = &zcu.intern_pool; + const file = zir_index.resolveFile(ip); + if (name_nav.unwrap()) |nav_index| { + const nav = ip.getNav(nav_index); + const decl_inst = nav.srcInst(ip).resolve(ip).?; + const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); + try wip_nav.declCommon(.{ + .decl = .decl_namespace_struct, + .generic_decl = .generic_decl_const, + .decl_instance = .decl_instance_namespace_struct, + }, &nav, file, &decl); + try wip_nav.debug_info.writer.writeByte(@intFromBool(true)); + } else { + const diw = &wip_nav.debug_info.writer; + const file_gop = try dwarf.getModInfo(wip_nav.unit).files.getOrPut(dwarf.gpa, file); + try wip_nav.abbrevCode(.empty_struct_type); + try diw.writeUleb128(file_gop.index); + try wip_nav.strp(name.toSlice(ip)); + try diw.writeByte(@intFromBool(true)); + } +} +/// Should only be called by the `link.ConstPool` implementation. +/// +/// Emits a DIE for the given comptime-only value (which may be a type). +pub fn updateConst(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.ConstPool.Index, value_index: InternPool.Index) Allocator.Error!void { + updateConstInner(dwarf, pt, debug_const_index, value_index) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + else => |e| std.debug.panic("DWARF TODO: '{t}' while updating constant\n", .{e}), }; - defer dwarf.gpa.free(name); +} +fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.ConstPool.Index, value_index: InternPool.Index) !void { + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + + const val: Value = .fromInterned(value_index); + + if (val.typeOf(zcu).toIntern() == .type_type and !val.isUndef(zcu)) { + val.toType().assertHasLayout(zcu); + } else { + val.typeOf(zcu).assertHasLayout(zcu); + } + + if (value_index == .anyerror_type) return; // handled in `flush` instead + + const value_ip_key = ip.indexToKey(value_index); + switch (value_ip_key) { + .func => return, // populated by the Nav instead (`updateComptimeNav` or `initWipNav`) + .@"extern" => return, // populated by the Nav instead (`initWipNav`) + else => {}, + } + + switch (value_index) { + .generic_poison_type => log.debug("updateValue(anytype)", .{}), + else => log.debug("updateValue(@as({f}, {f}))", .{ + val.typeOf(zcu).fmt(pt), + val.fmtValue(pt), + }), + } + + const unit, const entry = dwarf.values.items[@intFromEnum(debug_const_index)]; + + for ([_]*Section{ + &dwarf.debug_aranges.section, + &dwarf.debug_info.section, + &dwarf.debug_line.section, + &dwarf.debug_loclists.section, + &dwarf.debug_rnglists.section, + }) |sec| sec.getUnit(unit).getEntry(entry).clear(); + + var wip_nav: WipNav = .{ + .dwarf = dwarf, + .pt = pt, + .unit = unit, + .entry = entry, + .any_children = false, + .func = .none, + .func_sym_index = undefined, + .func_high_pc = undefined, + .blocks = undefined, + .cfi = undefined, + .debug_frame = .init(dwarf.gpa), + .debug_info = .init(dwarf.gpa), + .debug_line = .init(dwarf.gpa), + .debug_loclists = .init(dwarf.gpa), + }; + defer wip_nav.deinit(); + + // TODO: we really shouldn't need source locations at this point in the pipeline: we've lost + // that information by now. If the linker fundamentally cannot lower certain values, that needs + // to be caught in the frontend; if it can only hit transient failures, they should be reported + // without trying to tie them to a bogus source location. + const src_loc: Zcu.LazySrcLoc = .{ + .base_node_inst = inst: { + const mod_root_file_index = zcu.module_roots.get(zcu.std_mod).?.unwrap().?; + const mod_root_type_index = zcu.fileRootType(mod_root_file_index); + break :inst ip.loadStructType(mod_root_type_index).zir_index; + }, + .offset = .{ .byte_abs = 0 }, + }; + + const diw = &wip_nav.debug_info.writer; + var big_int_space: Value.BigIntSpace = undefined; + switch (value_ip_key) { + .func => unreachable, // handled above + .@"extern" => unreachable, // handled above - switch (ip.indexToKey(type_index)) { - .undef => { - try wip_nav.abbrevCode(.undefined_comptime_value); - try wip_nav.refType(.type); - }, .int_type => |int_type| { try wip_nav.abbrevCode(.numeric_type); - try wip_nav.strp(name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); try diw.writeByte(switch (int_type.signedness) { inline .signed, .unsigned => |signedness| @field(DW.ATE, @tagName(signedness)), }); try diw.writeUleb128(int_type.bits); - try diw.writeUleb128(ty.abiSize(zcu)); - try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + try diw.writeUleb128(val.toType().abiSize(zcu)); + try diw.writeUleb128(val.toType().abiAlignment(zcu).toByteUnits().?); }, .ptr_type => |ptr_type| switch (ptr_type.flags.size) { .one, .many, .c => { const ptr_child_type: Type = .fromInterned(ptr_type.child); - try wip_nav.abbrevCode(if (ptr_type.sentinel == .none) .ptr_type else .ptr_sentinel_type); - try wip_nav.strp(name); + try wip_nav.abbrevCode(switch (ptr_type.flags.alignment) { + .none => if (ptr_type.sentinel == .none) .ptr_type else .ptr_sentinel_type, + else => if (ptr_type.sentinel == .none) .ptr_aligned_type else .ptr_aligned_sentinel_type, + }); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); if (ptr_type.sentinel != .none) try wip_nav.blockValue(src_loc, .fromInterned(ptr_type.sentinel)); - try diw.writeUleb128(ptr_type.flags.alignment.toByteUnits() orelse - ptr_child_type.abiAlignment(zcu).toByteUnits().?); + if (ptr_type.flags.alignment.toByteUnits()) |a| try diw.writeUleb128(a); try diw.writeByte(@intFromEnum(ptr_type.flags.address_space)); if (ptr_type.flags.is_const or ptr_type.flags.is_volatile) try wip_nav.infoSectionOffset( .debug_info, @@ -3600,12 +3615,12 @@ fn updateLazyType( }, .slice => { try wip_nav.abbrevCode(.generated_struct_type); - try wip_nav.strp(name); - try diw.writeUleb128(ty.abiSize(zcu)); - try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); + try diw.writeUleb128(val.toType().abiSize(zcu)); + try diw.writeUleb128(val.toType().abiAlignment(zcu).toByteUnits().?); try wip_nav.abbrevCode(.generated_field); try wip_nav.strp("ptr"); - const ptr_field_type = ty.slicePtrFieldType(zcu); + const ptr_field_type = val.toType().slicePtrFieldType(zcu); try wip_nav.refType(ptr_field_type); try diw.writeUleb128(0); try wip_nav.abbrevCode(.generated_field); @@ -3619,7 +3634,7 @@ fn updateLazyType( .array_type => |array_type| { const array_child_type: Type = .fromInterned(array_type.child); try wip_nav.abbrevCode(if (array_type.sentinel == .none) .array_type else .array_sentinel_type); - try wip_nav.strp(name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); if (array_type.sentinel != .none) try wip_nav.blockValue(src_loc, .fromInterned(array_type.sentinel)); try wip_nav.refType(array_child_type); try wip_nav.abbrevCode(.array_len); @@ -3629,7 +3644,7 @@ fn updateLazyType( }, .vector_type => |vector_type| { try wip_nav.abbrevCode(.vector_type); - try wip_nav.strp(name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); try wip_nav.refType(.fromInterned(vector_type.child)); try wip_nav.abbrevCode(.array_len); try wip_nav.refType(.usize); @@ -3640,9 +3655,9 @@ fn updateLazyType( const opt_child_type: Type = .fromInterned(opt_child_type_index); const opt_repr = optRepr(opt_child_type, zcu); try wip_nav.abbrevCode(.generated_union_type); - try wip_nav.strp(name); - try diw.writeUleb128(ty.abiSize(zcu)); - try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); + try diw.writeUleb128(val.toType().abiSize(zcu)); + try diw.writeUleb128(val.toType().abiAlignment(zcu).toByteUnits().?); switch (opt_repr) { .opv_null => { try wip_nav.abbrevCode(.generated_field); @@ -3720,12 +3735,12 @@ fn updateLazyType( }; try wip_nav.abbrevCode(.generated_union_type); - try wip_nav.strp(name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); if (error_union_type.error_set_type != .generic_poison_type and error_union_type.payload_type != .generic_poison_type) { - try diw.writeUleb128(ty.abiSize(zcu)); - try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + try diw.writeUleb128(val.toType().abiSize(zcu)); + try diw.writeUleb128(val.toType().abiAlignment(zcu).toByteUnits().?); } else { try diw.writeUleb128(0); try diw.writeUleb128(1); @@ -3791,20 +3806,24 @@ fn updateLazyType( .bool, => { try wip_nav.abbrevCode(.numeric_type); - try wip_nav.strp(name); - try diw.writeByte(if (type_index == .bool_type) + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); + try diw.writeByte(if (value_index == .bool_type) DW.ATE.boolean - else if (ty.isRuntimeFloat()) + else if (val.toType().isRuntimeFloat()) DW.ATE.float - else if (ty.isSignedInt(zcu)) + else if (val.toType().isSignedInt(zcu)) DW.ATE.signed - else if (ty.isUnsignedInt(zcu)) + else if (val.toType().isUnsignedInt(zcu)) DW.ATE.unsigned else unreachable); - try diw.writeUleb128(ty.bitSize(zcu)); - try diw.writeUleb128(ty.abiSize(zcu)); - try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + try diw.writeUleb128(val.toType().bitSize(zcu)); + try diw.writeUleb128(val.toType().abiSize(zcu)); + try diw.writeUleb128(val.toType().abiAlignment(zcu).toByteUnits().?); + }, + .generic_poison => { + try wip_nav.abbrevCode(.void_type); + try wip_nav.strp("anytype"); }, .anyopaque, .void, @@ -3815,37 +3834,29 @@ fn updateLazyType( .null, .undefined, .enum_literal, - .generic_poison, => { try wip_nav.abbrevCode(.void_type); - try wip_nav.strp(if (type_index == .generic_poison_type) "anytype" else name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); }, - .anyerror => return, // delay until flush + .anyerror => unreachable, // already did early return above .adhoc_inferred_error_set => unreachable, }, - .struct_type, - .union_type, - .opaque_type, - => unreachable, .tuple_type => |tuple_type| if (tuple_type.types.len == 0) { try wip_nav.abbrevCode(.generated_empty_struct_type); - try wip_nav.strp(name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); try diw.writeByte(@intFromBool(false)); } else { try wip_nav.abbrevCode(.generated_struct_type); - try wip_nav.strp(name); - try diw.writeUleb128(ty.abiSize(zcu)); - try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); + try diw.writeUleb128(val.toType().abiSize(zcu)); + try diw.writeUleb128(val.toType().abiAlignment(zcu).toByteUnits().?); var field_byte_offset: u64 = 0; for (0..tuple_type.types.len) |field_index| { const comptime_value = tuple_type.values.get(ip)[field_index]; const field_type: Type = .fromInterned(tuple_type.types.get(ip)[field_index]); const has_runtime_bits, const has_comptime_state = switch (comptime_value) { .none => .{ false, false }, - else => .{ - field_type.hasRuntimeBits(zcu), - field_type.comptimeOnly(zcu) and try field_type.onePossibleValue(pt) == null, - }, + else => .{ field_type.hasRuntimeBits(zcu), field_type.comptimeOnly(zcu) }, }; try wip_nav.abbrevCode(if (has_comptime_state) .struct_field_comptime_comptime_state @@ -3875,25 +3886,284 @@ fn updateLazyType( } try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); }, + .struct_type => { + const loaded_struct = ip.loadStructType(value_index); + const ty = val.toType(); + const file = loaded_struct.zir_index.resolveFile(ip); + switch (loaded_struct.layout) { + .auto, .@"extern" => { + const struct_is_file: bool = if (loaded_struct.zir_index.resolve(ip)) |inst| f: { + break :f inst == .main_struct_inst; + } else false; + if (loaded_struct.name_nav.unwrap()) |nav_index| { + assert(!struct_is_file); + const nav = ip.getNav(nav_index); + const decl_inst = nav.srcInst(ip).resolve(ip).?; + const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); + try wip_nav.declCommon(if (loaded_struct.field_types.len == 0) .{ + .decl = .decl_namespace_struct, + .generic_decl = .generic_decl_const, + .decl_instance = .decl_instance_namespace_struct, + } else .{ + .decl = .decl_struct, + .generic_decl = .generic_decl_const, + .decl_instance = .decl_instance_struct, + }, &nav, file, &decl); + } else { + const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file); + try wip_nav.abbrevCode(switch (loaded_struct.field_types.len) { + 0 => if (struct_is_file) .empty_file else .empty_struct_type, + else => if (struct_is_file) .file else .struct_type, + }); + try diw.writeUleb128(file_gop.index); + try wip_nav.strp(loaded_struct.name.toSlice(ip)); + } + if (loaded_struct.field_types.len == 0) { + if (!struct_is_file) try diw.writeByte(@intFromBool(false)); + } else { + try diw.writeUleb128(ty.abiSize(zcu)); + try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); + for (0..loaded_struct.field_types.len) |field_index| { + const is_comptime = loaded_struct.field_is_comptime_bits.get(ip, field_index); + // TODO: we currently don't emit information about default values for + // non-`comptime` fields, because these default values are resolved at a + // separate time in the compiler frontend. To emit this information, the + // frontend needs to tell us when the default values are available: like + // how `Zcu.PerThread.ensureTypeLayoutUpToDate` enqueues a link task to + // indicate completion of the type's layout, a task should be enqueued + // by `Zcu.PerThread.ensureStructDefaultsUpToDate`, and upon receiving + // it we should patch the correct default field values in. + const field_init: InternPool.Index = if (is_comptime) loaded_struct.field_defaults.getOrNone(ip, field_index) else .none; + assert(!(is_comptime and field_init == .none)); + const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); + const has_runtime_bits, const has_comptime_state = switch (field_init) { + .none => .{ false, false }, + else => .{ + field_type.hasRuntimeBits(zcu), + field_type.comptimeOnly(zcu), + }, + }; + try wip_nav.abbrevCode(if (is_comptime) + if (has_comptime_state) + .struct_field_comptime_comptime_state + else if (has_runtime_bits) + .struct_field_comptime_runtime_bits + else + .struct_field_comptime + else if (field_init != .none) + if (has_comptime_state) + .struct_field_default_comptime_state + else if (has_runtime_bits) + .struct_field_default_runtime_bits + else + .struct_field + else + .struct_field); + try wip_nav.strp(loaded_struct.field_names.get(ip)[field_index].toSlice(ip)); + try wip_nav.refType(field_type); + if (!is_comptime) { + try diw.writeUleb128(loaded_struct.field_offsets.get(ip)[field_index]); + try diw.writeUleb128(loaded_struct.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse + field_type.abiAlignment(zcu).toByteUnits().?); + } + if (has_comptime_state) + try wip_nav.refValue(.fromInterned(field_init)) + else if (has_runtime_bits) + try wip_nav.blockValue(ty.srcLoc(zcu), .fromInterned(field_init)); + } + try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + } + }, + .@"packed" => { + const need_terminator: bool = if (loaded_struct.name_nav.unwrap()) |nav_index| t: { + const nav = ip.getNav(nav_index); + const decl_inst = nav.srcInst(ip).resolve(ip).?; + const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); + try wip_nav.declCommon(.{ + .decl = .decl_packed_struct, + .generic_decl = .generic_decl_const, + .decl_instance = .decl_instance_packed_struct, + }, &nav, file, &decl); + break :t true; + } else t: { + const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file); + try wip_nav.abbrevCode(if (loaded_struct.field_types.len > 0) .packed_struct_type else .empty_packed_struct_type); + try diw.writeUleb128(file_gop.index); + try wip_nav.strp(loaded_struct.name.toSlice(ip)); + break :t loaded_struct.field_types.len > 0; + }; + try wip_nav.refType(.fromInterned(loaded_struct.packed_backing_int_type)); + var field_bit_offset: u16 = 0; + for (0..loaded_struct.field_types.len) |field_index| { + try wip_nav.abbrevCode(.packed_struct_field); + try wip_nav.strp(loaded_struct.field_names.get(ip)[field_index].toSlice(ip)); + const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); + try wip_nav.refType(field_type); + try diw.writeUleb128(field_bit_offset); + field_bit_offset += @intCast(field_type.bitSize(zcu)); + } + if (need_terminator) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + }, + } + }, + .union_type => { + const loaded_union = ip.loadUnionType(value_index); + const file = loaded_union.zir_index.resolveFile(ip); + switch (loaded_union.layout) { + .auto, .@"extern" => { + const need_terminator: bool = if (loaded_union.name_nav.unwrap()) |nav_index| t: { + const nav = ip.getNav(nav_index); + const decl_inst = nav.srcInst(ip).resolve(ip).?; + const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); + try wip_nav.declCommon(.{ + .decl = .decl_union, + .generic_decl = .generic_decl_const, + .decl_instance = .decl_instance_union, + }, &nav, file, &decl); + break :t true; + } else t: { + const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file); + try wip_nav.abbrevCode(if (loaded_union.field_types.len > 0) .union_type else .empty_union_type); + try diw.writeUleb128(file_gop.index); + try wip_nav.strp(loaded_union.name.toSlice(ip)); + break :t loaded_union.field_types.len > 0; + }; + const union_layout = Type.getUnionLayout(loaded_union, zcu); + try diw.writeUleb128(union_layout.abi_size); + try diw.writeUleb128(union_layout.abi_align.toByteUnits().?); + const loaded_tag = ip.loadEnumType(loaded_union.enum_tag_type); + if (loaded_union.has_runtime_tag) { + try wip_nav.abbrevCode(.tagged_union); + try wip_nav.infoSectionOffset( + .debug_info, + wip_nav.unit, + wip_nav.entry, + @intCast(diw.end + dwarf.sectionOffsetBytes()), + ); + { + try wip_nav.abbrevCode(.generated_field); + try wip_nav.strp("tag"); + try wip_nav.refType(.fromInterned(loaded_union.enum_tag_type)); + try diw.writeUleb128(union_layout.tagOffset()); + + for (0..loaded_union.field_types.len) |field_index| { + try wip_nav.enumConstValue(loaded_tag, .{ + .sdata = .signed_tagged_union_field, + .udata = .unsigned_tagged_union_field, + .block = .big_tagged_union_field, + }, field_index); + { + try wip_nav.abbrevCode(.struct_field); + try wip_nav.strp(loaded_tag.field_names.get(ip)[field_index].toSlice(ip)); + const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); + try wip_nav.refType(field_type); + try diw.writeUleb128(union_layout.payloadOffset()); + try diw.writeUleb128(loaded_union.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse + if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?); + } + try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + } + } + try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + } else for (0..loaded_union.field_types.len) |field_index| { + try wip_nav.abbrevCode(.untagged_union_field); + try wip_nav.strp(loaded_tag.field_names.get(ip)[field_index].toSlice(ip)); + const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); + try wip_nav.refType(field_type); + try diw.writeUleb128(loaded_union.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse + if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?); + } + if (need_terminator) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + }, + .@"packed" => { + // TODO: debug info for packed unions + try wip_nav.abbrevCode(.numeric_type); + try wip_nav.strp(loaded_union.name.toSlice(ip)); + const backing_int_ty: Type = .fromInterned(loaded_union.packed_backing_int_type); + const int_info = backing_int_ty.intInfo(zcu); + try diw.writeByte(switch (int_info.signedness) { + inline .signed, .unsigned => |signedness| @field(DW.ATE, @tagName(signedness)), + }); + try diw.writeUleb128(int_info.bits); + try diw.writeUleb128(backing_int_ty.abiSize(zcu)); + try diw.writeUleb128(backing_int_ty.abiAlignment(zcu).toByteUnits().?); + }, + } + }, .enum_type => { - const loaded_enum = ip.loadEnumType(type_index); - try wip_nav.abbrevCode(if (loaded_enum.names.len == 0) .generated_empty_enum_type else .generated_enum_type); - try wip_nav.strp(name); - try wip_nav.refType(.fromInterned(loaded_enum.tag_ty)); - for (0..loaded_enum.names.len) |field_index| { - try wip_nav.enumConstValue(loaded_enum, .{ - .sdata = .signed_enum_field, - .udata = .unsigned_enum_field, - .block = .big_enum_field, - }, field_index); - try wip_nav.strp(loaded_enum.names.get(ip)[field_index].toSlice(ip)); + const loaded_enum = ip.loadEnumType(value_index); + if (loaded_enum.zir_index.unwrap()) |zir_index| { + assert(loaded_enum.owner_union == .none); + const file = zir_index.resolveFile(ip); + if (loaded_enum.name_nav.unwrap()) |nav_index| { + const nav = ip.getNav(nav_index); + const decl_inst = nav.srcInst(ip).resolve(ip).?; + const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); + try wip_nav.declCommon(if (loaded_enum.field_names.len > 0) .{ + .decl = .decl_enum, + .generic_decl = .generic_decl_const, + .decl_instance = .decl_instance_enum, + } else .{ + .decl = .decl_empty_enum, + .generic_decl = .generic_decl_const, + .decl_instance = .decl_instance_empty_enum, + }, &nav, file, &decl); + } else { + const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file); + try wip_nav.abbrevCode(if (loaded_enum.field_names.len > 0) .enum_type else .empty_enum_type); + try diw.writeUleb128(file_gop.index); + try wip_nav.strp(loaded_enum.name.toSlice(ip)); + } + try wip_nav.refType(.fromInterned(loaded_enum.int_tag_type)); + for (0..loaded_enum.field_names.len) |field_index| { + try wip_nav.enumConstValue(loaded_enum, .{ + .sdata = .signed_enum_field, + .udata = .unsigned_enum_field, + .block = .big_enum_field, + }, field_index); + try wip_nav.strp(loaded_enum.field_names.get(ip)[field_index].toSlice(ip)); + } + if (loaded_enum.field_names.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + } else { + assert(loaded_enum.owner_union != .none); + try wip_nav.abbrevCode(if (loaded_enum.field_names.len == 0) .generated_empty_enum_type else .generated_enum_type); + try wip_nav.strp(loaded_enum.name.toSlice(ip)); + try wip_nav.refType(.fromInterned(loaded_enum.int_tag_type)); + for (0..loaded_enum.field_names.len) |field_index| { + try wip_nav.enumConstValue(loaded_enum, .{ + .sdata = .signed_enum_field, + .udata = .unsigned_enum_field, + .block = .big_enum_field, + }, field_index); + try wip_nav.strp(loaded_enum.field_names.get(ip)[field_index].toSlice(ip)); + } + if (loaded_enum.field_names.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + } + }, + .opaque_type => { + const loaded_opaque = ip.loadOpaqueType(value_index); + const file = loaded_opaque.zir_index.resolveFile(ip); + if (loaded_opaque.name_nav.unwrap()) |nav_index| { + const nav = ip.getNav(nav_index); + const decl_inst = nav.srcInst(ip).resolve(ip).?; + const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); + try wip_nav.declCommon(.{ + .decl = .decl_namespace_struct, + .generic_decl = .generic_decl_const, + .decl_instance = .decl_instance_namespace_struct, + }, &nav, file, &decl); + } else { + const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file); + try wip_nav.abbrevCode(.empty_struct_type); + try diw.writeUleb128(file_gop.index); + try wip_nav.strp(loaded_opaque.name.toSlice(ip)); } - if (loaded_enum.names.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + try diw.writeByte(@intFromBool(true)); }, .func_type => |func_type| { const is_nullary = func_type.param_types.len == 0 and !func_type.is_var_args; try wip_nav.abbrevCode(if (is_nullary) .nullary_func_type else .func_type); - try wip_nav.strp(name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); const cc: DW.CC = cc: { if (zcu.getTarget().cCallingConvention()) |cc| { if (@as(std.builtin.CallingConvention.Tag, cc) == func_type.cc) { @@ -3975,7 +4245,7 @@ fn updateLazyType( }, .error_set_type => |error_set_type| { try wip_nav.abbrevCode(if (error_set_type.names.len == 0) .generated_empty_enum_type else .generated_enum_type); - try wip_nav.strp(name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); try wip_nav.refType(.fromInterned(try pt.intern(.{ .int_type = .{ .signedness = .unsigned, .bits = zcu.errorSetBits(), @@ -3990,100 +4260,28 @@ fn updateLazyType( }, .inferred_error_set_type => |func| { try wip_nav.abbrevCode(.inferred_error_set_type); - try wip_nav.strp(name); + try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); try wip_nav.refType(.fromInterned(switch (ip.funcIesResolvedUnordered(func)) { .none => .anyerror_type, else => |ies| ies, })); }, - // values, not types - .simple_value, - .variable, - .@"extern", - .func, - .int, - .err, - .error_union, - .enum_literal, - .enum_tag, - .empty_enum_value, - .float, - .ptr, - .slice, - .opt, - .aggregate, - .un, - // memoization, not types - .memoized_call, - => unreachable, - } - try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written()); -} - -fn updateLazyValue( - dwarf: *Dwarf, - pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, - value_index: InternPool.Index, - pending_lazy: *WipNav.PendingLazy, -) (UpdateError || Writer.Error)!void { - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - assert(ip.typeOf(value_index) != .type_type); - log.debug("updateLazyValue(@as({f}, {f}))", .{ - Value.fromInterned(value_index).typeOf(zcu).fmt(pt), - Value.fromInterned(value_index).fmtValue(pt), - }); - var wip_nav: WipNav = .{ - .dwarf = dwarf, - .pt = pt, - .unit = .main, - .entry = dwarf.values.get(value_index).?, - .any_children = false, - .func = .none, - .func_sym_index = undefined, - .func_high_pc = undefined, - .blocks = undefined, - .cfi = undefined, - .debug_frame = .init(dwarf.gpa), - .debug_info = .init(dwarf.gpa), - .debug_line = .init(dwarf.gpa), - .debug_loclists = .init(dwarf.gpa), - .pending_lazy = pending_lazy.*, - }; - defer { - pending_lazy.* = wip_nav.pending_lazy; - wip_nav.pending_lazy = .empty; - wip_nav.deinit(); - } - const diw = &wip_nav.debug_info.writer; - var big_int_space: Value.BigIntSpace = undefined; - switch (ip.indexToKey(value_index)) { - .int_type, - .ptr_type, - .array_type, - .vector_type, - .opt_type, - .anyframe_type, - .error_union_type, - .simple_type, - .struct_type, - .tuple_type, - .union_type, - .opaque_type, - .enum_type, - .func_type, - .error_set_type, - .inferred_error_set_type, - => unreachable, // already handled .undef => |ty| { try wip_nav.abbrevCode(.undefined_comptime_value); try wip_nav.refType(.fromInterned(ty)); }, - .simple_value => unreachable, // opv state - .variable, .@"extern" => unreachable, // not a value - .func => unreachable, // already handled + .simple_value => |simple_value| switch (simple_value) { + .void => unreachable, // opv state + .true, .false => unreachable, // runtime bits + .@"unreachable" => unreachable, // not a value + .null => { + // TODO: proper representation for this + try wip_nav.abbrevCode(.undefined_comptime_value); + try wip_nav.refType(.null); + }, + }, + .variable => unreachable, // not a value .int => |int| { try wip_nav.bigIntConstValue(.{ .sdata = .sdata_comptime_value, @@ -4092,6 +4290,15 @@ fn updateLazyValue( }, .fromInterned(int.ty), Value.fromInterned(value_index).toBigInt(&big_int_space, zcu)); try wip_nav.refType(.fromInterned(int.ty)); }, + .bitpack => |bitpack| { + const backing_int_val: Value = .fromInterned(bitpack.backing_int_val); + try wip_nav.bigIntConstValue(.{ + .sdata = .sdata_comptime_value, + .udata = .udata_comptime_value, + .block = .block_comptime_value, + }, backing_int_val.typeOf(zcu), backing_int_val.toBigInt(&big_int_space, zcu)); + try wip_nav.refType(.fromInterned(bitpack.ty)); + }, .err => |err| { try wip_nav.abbrevCode(.udata_comptime_value); try wip_nav.refType(.fromInterned(err.ty)); @@ -4117,7 +4324,7 @@ fn updateLazyValue( .payload => |payload_val| { const payload_type: Type = .fromInterned(ip.typeOf(payload_val)); const has_runtime_bits = payload_type.hasRuntimeBits(zcu); - const has_comptime_state = payload_type.comptimeOnly(zcu) and try payload_type.onePossibleValue(pt) == null; + const has_comptime_state = payload_type.comptimeOnly(zcu); try wip_nav.abbrevCode(if (has_comptime_state) .comptime_value_field_comptime_state else if (has_runtime_bits) @@ -4153,7 +4360,6 @@ fn updateLazyValue( }, .fromInterned(int.ty), Value.fromInterned(value_index).toBigInt(&big_int_space, zcu)); try wip_nav.refType(.fromInterned(enum_tag.ty)); }, - .empty_enum_value => unreachable, .float => |float| { switch (float.storage) { .f16 => |f16_val| { @@ -4194,11 +4400,11 @@ fn updateLazyValue( var byte_offset = ptr.byte_offset; const base_unit, const base_entry = while (true) { const base_ptr, const access: Access = base_ptr_access: switch (base_addr) { - .nav => |nav_index| break try wip_nav.getNavEntry(nav_index), + .nav => |nav_index| break try dwarf.getNavEntry(nav_index), .comptime_alloc, .comptime_field => unreachable, .uav => |uav| { const uav_ty: Type = .fromInterned(ip.typeOf(uav.val)); - if (try uav_ty.onePossibleValue(pt)) |_| { + if (uav_ty.classify(zcu) == .one_possible_value) { try wip_nav.abbrevCode(if (zero_bit_accesses.items.len > 0) .aggregate_udata_comptime_value else @@ -4311,22 +4517,12 @@ fn updateLazyValue( switch (optRepr(opt_child_type, zcu)) { .opv_null => try diw.writeUleb128(0), .unpacked => try wip_nav.blockValue(src_loc, .makeBool(opt.val != .none)), - .error_set => try wip_nav.blockValue(src_loc, .fromInterned(value_index)), - .pointer => if (opt_child_type.comptimeOnly(zcu)) { - var buf: [8]u8 = undefined; - const bytes = buf[0..@divExact(zcu.getTarget().ptrBitWidth(), 8)]; - dwarf.writeInt(bytes, switch (opt.val) { - .none => 0, - else => opt_child_type.ptrAlignment(zcu).toByteUnits().?, - }); - try diw.writeUleb128(bytes.len); - try diw.writeAll(bytes); - } else try wip_nav.blockValue(src_loc, .fromInterned(value_index)), + .error_set, .pointer => try wip_nav.blockValue(src_loc, .fromInterned(value_index)), } } if (opt.val != .none) child_field: { const has_runtime_bits = opt_child_type.hasRuntimeBits(zcu); - const has_comptime_state = opt_child_type.comptimeOnly(zcu) and try opt_child_type.onePossibleValue(pt) == null; + const has_comptime_state = opt_child_type.comptimeOnly(zcu); try wip_nav.abbrevCode(if (has_comptime_state) .comptime_value_field_comptime_state else if (has_runtime_bits) @@ -4349,17 +4545,17 @@ fn updateLazyValue( const loaded_struct_type = ip.loadStructType(aggregate.ty); assert(loaded_struct_type.layout == .auto); for (0..loaded_struct_type.field_types.len) |field_index| { - if (loaded_struct_type.fieldIsComptime(ip, field_index)) continue; + if (loaded_struct_type.field_is_comptime_bits.get(ip, field_index)) continue; const field_type: Type = .fromInterned(loaded_struct_type.field_types.get(ip)[field_index]); const has_runtime_bits = field_type.hasRuntimeBits(zcu); - const has_comptime_state = field_type.comptimeOnly(zcu) and try field_type.onePossibleValue(pt) == null; + const has_comptime_state = field_type.comptimeOnly(zcu); try wip_nav.abbrevCode(if (has_comptime_state) .comptime_value_field_comptime_state else if (has_runtime_bits) .comptime_value_field_runtime_bits else continue); - try wip_nav.strp(loaded_struct_type.fieldName(ip, field_index).toSlice(ip)); + try wip_nav.strp(loaded_struct_type.field_names.get(ip)[field_index].toSlice(ip)); const field_value: Value = .fromInterned(switch (aggregate.storage) { .bytes => unreachable, .elems => |elems| elems[field_index], @@ -4375,7 +4571,7 @@ fn updateLazyValue( if (tuple_type.values.get(ip)[field_index] != .none) continue; const field_type: Type = .fromInterned(tuple_type.types.get(ip)[field_index]); const has_runtime_bits = field_type.hasRuntimeBits(zcu); - const has_comptime_state = field_type.comptimeOnly(zcu) and try field_type.onePossibleValue(pt) == null; + const has_comptime_state = field_type.comptimeOnly(zcu); try wip_nav.abbrevCode(if (has_comptime_state) .comptime_value_field_comptime_state else if (has_runtime_bits) @@ -4400,7 +4596,7 @@ fn updateLazyValue( inline .array_type, .vector_type => |sequence_type| { const child_type: Type = .fromInterned(sequence_type.child); const has_runtime_bits = child_type.hasRuntimeBits(zcu); - const has_comptime_state = child_type.comptimeOnly(zcu) and try child_type.onePossibleValue(pt) == null; + const has_comptime_state = child_type.comptimeOnly(zcu); for (switch (aggregate.storage) { .bytes => unreachable, .elems => |elems| elems, @@ -4427,12 +4623,12 @@ fn updateLazyValue( try wip_nav.refType(.fromInterned(un.ty)); field: { const loaded_union_type = ip.loadUnionType(un.ty); - assert(loaded_union_type.flagsUnordered(ip).layout == .auto); + assert(loaded_union_type.layout == .auto); const field_index = zcu.unionTagFieldIndex(loaded_union_type, Value.fromInterned(un.tag)).?; const field_ty: Type = .fromInterned(loaded_union_type.field_types.get(ip)[field_index]); - const field_name = loaded_union_type.loadTagType(ip).names.get(ip)[field_index]; + const field_name = ip.loadEnumType(loaded_union_type.enum_tag_type).field_names.get(ip)[field_index]; const has_runtime_bits = field_ty.hasRuntimeBits(zcu); - const has_comptime_state = field_ty.comptimeOnly(zcu) and try field_ty.onePossibleValue(pt) == null; + const has_comptime_state = field_ty.comptimeOnly(zcu); try wip_nav.abbrevCode(if (has_comptime_state) .comptime_value_field_comptime_state else if (has_runtime_bits) @@ -4449,7 +4645,8 @@ fn updateLazyValue( }, .memoized_call => unreachable, // not a value } - try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written()); + try dwarf.debug_info.section.replaceEntry(unit, entry, dwarf, wip_nav.debug_info.written()); + try dwarf.debug_loclists.section.replaceEntry(unit, entry, dwarf, wip_nav.debug_loclists.written()); } fn optRepr(opt_child_type: Type, zcu: *const Zcu) enum { unpacked, opv_null, error_set, pointer } { @@ -4464,312 +4661,6 @@ fn optRepr(opt_child_type: Type, zcu: *const Zcu) enum { unpacked, opv_null, err }; } -pub fn updateContainerType( - dwarf: *Dwarf, - pt: Zcu.PerThread, - type_index: InternPool.Index, -) UpdateError!void { - return dwarf.updateContainerTypeWriterError(pt, type_index) catch |err| switch (err) { - error.WriteFailed => error.OutOfMemory, - else => |e| e, - }; -} -fn updateContainerTypeWriterError( - dwarf: *Dwarf, - pt: Zcu.PerThread, - type_index: InternPool.Index, -) (UpdateError || Writer.Error)!void { - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const ty: Type = .fromInterned(type_index); - const ty_src_loc = ty.srcLoc(zcu); - log.debug("updateContainerType({f})", .{ty.fmt(pt)}); - - const inst_info = ty.typeDeclInst(zcu).?.resolveFull(ip).?; - const file = zcu.fileByIndex(inst_info.file); - const unit = try dwarf.getUnit(file.mod.?); - const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, inst_info.file); - if (inst_info.inst == .main_struct_inst) { - const type_gop = try dwarf.types.getOrPut(dwarf.gpa, type_index); - if (!type_gop.found_existing) type_gop.value_ptr.* = try dwarf.addCommonEntry(unit); - var wip_nav: WipNav = .{ - .dwarf = dwarf, - .pt = pt, - .unit = unit, - .entry = type_gop.value_ptr.*, - .any_children = false, - .func = .none, - .func_sym_index = undefined, - .func_high_pc = undefined, - .blocks = undefined, - .cfi = undefined, - .debug_frame = .init(dwarf.gpa), - .debug_info = .init(dwarf.gpa), - .debug_line = .init(dwarf.gpa), - .debug_loclists = .init(dwarf.gpa), - .pending_lazy = .empty, - }; - defer wip_nav.deinit(); - - const loaded_struct = ip.loadStructType(type_index); - - const diw = &wip_nav.debug_info.writer; - try wip_nav.abbrevCode(if (loaded_struct.field_types.len == 0) .empty_file else .file); - try diw.writeUleb128(file_gop.index); - try wip_nav.strp(loaded_struct.name.toSlice(ip)); - if (loaded_struct.field_types.len > 0) { - try diw.writeUleb128(ty.abiSize(zcu)); - try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); - for (0..loaded_struct.field_types.len) |field_index| { - const is_comptime = loaded_struct.fieldIsComptime(ip, field_index); - const field_init = loaded_struct.fieldInit(ip, field_index); - assert(!(is_comptime and field_init == .none)); - const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - const has_runtime_bits, const has_comptime_state = switch (field_init) { - .none => .{ false, false }, - else => .{ - field_type.hasRuntimeBits(zcu), - field_type.comptimeOnly(zcu) and try field_type.onePossibleValue(pt) == null, - }, - }; - try wip_nav.abbrevCode(if (is_comptime) - if (has_comptime_state) - .struct_field_comptime_comptime_state - else if (has_runtime_bits) - .struct_field_comptime_runtime_bits - else - .struct_field_comptime - else if (field_init != .none) - if (has_comptime_state) - .struct_field_default_comptime_state - else if (has_runtime_bits) - .struct_field_default_runtime_bits - else - .struct_field - else - .struct_field); - try wip_nav.strp(loaded_struct.fieldName(ip, field_index).toSlice(ip)); - try wip_nav.refType(field_type); - if (!is_comptime) { - try diw.writeUleb128(loaded_struct.offsets.get(ip)[field_index]); - try diw.writeUleb128(loaded_struct.fieldAlign(ip, field_index).toByteUnits() orelse - field_type.abiAlignment(zcu).toByteUnits().?); - } - if (has_comptime_state) - try wip_nav.refValue(.fromInterned(field_init)) - else if (has_runtime_bits) - try wip_nav.blockValue(ty_src_loc, .fromInterned(field_init)); - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - } - - try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written()); - try wip_nav.updateLazy(ty_src_loc); - } else { - { - // Note that changes to ZIR instruction tracking only need to update this code - // if a newly-tracked instruction can be a type's owner `zir_index`. - comptime assert(Zir.inst_tracking_version == 0); - - const decl_inst = file.zir.?.instructions.get(@intFromEnum(inst_info.inst)); - const name_strat: Zir.Inst.NameStrategy = switch (decl_inst.tag) { - .struct_init, .struct_init_ref, .struct_init_anon => .anon, - .extended => switch (decl_inst.data.extended.opcode) { - .struct_decl => @as(Zir.Inst.StructDecl.Small, @bitCast(decl_inst.data.extended.small)).name_strategy, - .enum_decl => @as(Zir.Inst.EnumDecl.Small, @bitCast(decl_inst.data.extended.small)).name_strategy, - .union_decl => @as(Zir.Inst.UnionDecl.Small, @bitCast(decl_inst.data.extended.small)).name_strategy, - .opaque_decl => @as(Zir.Inst.OpaqueDecl.Small, @bitCast(decl_inst.data.extended.small)).name_strategy, - - .reify_enum, - .reify_struct, - .reify_union, - => @enumFromInt(decl_inst.data.extended.small), - - else => unreachable, - }, - else => unreachable, - }; - if (name_strat == .parent) return; - } - - const type_gop = try dwarf.types.getOrPut(dwarf.gpa, type_index); - if (!type_gop.found_existing) type_gop.value_ptr.* = try dwarf.addCommonEntry(unit); - var wip_nav: WipNav = .{ - .dwarf = dwarf, - .pt = pt, - .unit = unit, - .entry = type_gop.value_ptr.*, - .any_children = false, - .func = .none, - .func_sym_index = undefined, - .func_high_pc = undefined, - .blocks = undefined, - .cfi = undefined, - .debug_frame = .init(dwarf.gpa), - .debug_info = .init(dwarf.gpa), - .debug_line = .init(dwarf.gpa), - .debug_loclists = .init(dwarf.gpa), - .pending_lazy = .empty, - }; - defer wip_nav.deinit(); - const diw = &wip_nav.debug_info.writer; - const name = try std.fmt.allocPrint(dwarf.gpa, "{f}", .{ty.fmt(pt)}); - defer dwarf.gpa.free(name); - - switch (ip.indexToKey(type_index)) { - .struct_type => { - const loaded_struct = ip.loadStructType(type_index); - switch (loaded_struct.layout) { - .auto, .@"extern" => { - try wip_nav.abbrevCode(if (loaded_struct.field_types.len == 0) .empty_struct_type else .struct_type); - try diw.writeUleb128(file_gop.index); - try wip_nav.strp(name); - if (loaded_struct.field_types.len == 0) try diw.writeByte(@intFromBool(false)) else { - try diw.writeUleb128(ty.abiSize(zcu)); - try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?); - for (0..loaded_struct.field_types.len) |field_index| { - const is_comptime = loaded_struct.fieldIsComptime(ip, field_index); - const field_init = loaded_struct.fieldInit(ip, field_index); - assert(!(is_comptime and field_init == .none)); - const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - const has_runtime_bits, const has_comptime_state = switch (field_init) { - .none => .{ false, false }, - else => .{ - field_type.hasRuntimeBits(zcu), - field_type.comptimeOnly(zcu) and try field_type.onePossibleValue(pt) == null, - }, - }; - try wip_nav.abbrevCode(if (is_comptime) - if (has_comptime_state) - .struct_field_comptime_comptime_state - else if (has_runtime_bits) - .struct_field_comptime_runtime_bits - else - .struct_field_comptime - else if (field_init != .none) - if (has_comptime_state) - .struct_field_default_comptime_state - else if (has_runtime_bits) - .struct_field_default_runtime_bits - else - .struct_field - else - .struct_field); - try wip_nav.strp(loaded_struct.fieldName(ip, field_index).toSlice(ip)); - try wip_nav.refType(field_type); - if (!is_comptime) { - try diw.writeUleb128(loaded_struct.offsets.get(ip)[field_index]); - try diw.writeUleb128(loaded_struct.fieldAlign(ip, field_index).toByteUnits() orelse - field_type.abiAlignment(zcu).toByteUnits().?); - } - if (has_comptime_state) - try wip_nav.refValue(.fromInterned(field_init)) - else if (has_runtime_bits) - try wip_nav.blockValue(ty_src_loc, .fromInterned(field_init)); - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - } - }, - .@"packed" => { - try wip_nav.abbrevCode(if (loaded_struct.field_types.len > 0) .packed_struct_type else .empty_packed_struct_type); - try diw.writeUleb128(file_gop.index); - try wip_nav.strp(name); - try wip_nav.refType(.fromInterned(loaded_struct.backingIntTypeUnordered(ip))); - var field_bit_offset: u16 = 0; - for (0..loaded_struct.field_types.len) |field_index| { - try wip_nav.abbrevCode(.packed_struct_field); - try wip_nav.strp(loaded_struct.fieldName(ip, field_index).toSlice(ip)); - const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); - try wip_nav.refType(field_type); - try diw.writeUleb128(field_bit_offset); - field_bit_offset += @intCast(field_type.bitSize(zcu)); - } - if (loaded_struct.field_types.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - }, - } - }, - .enum_type => { - const loaded_enum = ip.loadEnumType(type_index); - try wip_nav.abbrevCode(if (loaded_enum.names.len > 0) .enum_type else .empty_enum_type); - try diw.writeUleb128(file_gop.index); - try wip_nav.strp(name); - try wip_nav.refType(.fromInterned(loaded_enum.tag_ty)); - for (0..loaded_enum.names.len) |field_index| { - try wip_nav.enumConstValue(loaded_enum, .{ - .sdata = .signed_enum_field, - .udata = .unsigned_enum_field, - .block = .big_enum_field, - }, field_index); - try wip_nav.strp(loaded_enum.names.get(ip)[field_index].toSlice(ip)); - } - if (loaded_enum.names.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - }, - .union_type => { - const loaded_union = ip.loadUnionType(type_index); - try wip_nav.abbrevCode(if (loaded_union.field_types.len > 0) .union_type else .empty_union_type); - try diw.writeUleb128(file_gop.index); - try wip_nav.strp(name); - const union_layout = Type.getUnionLayout(loaded_union, zcu); - try diw.writeUleb128(union_layout.abi_size); - try diw.writeUleb128(union_layout.abi_align.toByteUnits().?); - const loaded_tag = loaded_union.loadTagType(ip); - if (loaded_union.hasTag(ip)) { - try wip_nav.abbrevCode(.tagged_union); - try wip_nav.infoSectionOffset( - .debug_info, - wip_nav.unit, - wip_nav.entry, - @intCast(diw.end + dwarf.sectionOffsetBytes()), - ); - { - try wip_nav.abbrevCode(.generated_field); - try wip_nav.strp("tag"); - try wip_nav.refType(.fromInterned(loaded_union.enum_tag_ty)); - try diw.writeUleb128(union_layout.tagOffset()); - - for (0..loaded_union.field_types.len) |field_index| { - try wip_nav.enumConstValue(loaded_tag, .{ - .sdata = .signed_tagged_union_field, - .udata = .unsigned_tagged_union_field, - .block = .big_tagged_union_field, - }, field_index); - { - try wip_nav.abbrevCode(.struct_field); - try wip_nav.strp(loaded_tag.names.get(ip)[field_index].toSlice(ip)); - const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); - try wip_nav.refType(field_type); - try diw.writeUleb128(union_layout.payloadOffset()); - try diw.writeUleb128(loaded_union.fieldAlign(ip, field_index).toByteUnits() orelse - if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?); - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - } - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - } else for (0..loaded_union.field_types.len) |field_index| { - try wip_nav.abbrevCode(.untagged_union_field); - try wip_nav.strp(loaded_tag.names.get(ip)[field_index].toSlice(ip)); - const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); - try wip_nav.refType(field_type); - try diw.writeUleb128(loaded_union.fieldAlign(ip, field_index).toByteUnits() orelse - if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?); - } - if (loaded_union.field_types.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - }, - .opaque_type => { - try wip_nav.abbrevCode(.empty_struct_type); - try diw.writeUleb128(file_gop.index); - try wip_nav.strp(name); - try diw.writeByte(@intFromBool(true)); - }, - else => unreachable, - } - try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written()); - try dwarf.debug_loclists.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_loclists.written()); - try wip_nav.updateLazy(ty_src_loc); - } -} - pub fn updateLineNumber(dwarf: *Dwarf, zcu: *Zcu, zir_index: InternPool.TrackedInst.Index) UpdateError!void { const comp = dwarf.bin_file.comp; const io = comp.io; @@ -4832,14 +4723,15 @@ fn flushWriterError(dwarf: *Dwarf, pt: Zcu.PerThread) (FlushError || Writer.Erro const comp = dwarf.bin_file.comp; const io = comp.io; + // Update `anyerror` based on the finished global error set. { - const type_gop = try dwarf.types.getOrPut(dwarf.gpa, .anyerror_type); - if (!type_gop.found_existing) type_gop.value_ptr.* = try dwarf.addCommonEntry(.main); + const index = try dwarf.const_pool.get(pt, .{ .dwarf = dwarf }, .anyerror_type); + const unit, const entry = dwarf.values.items[@intFromEnum(index)]; var wip_nav: WipNav = .{ .dwarf = dwarf, .pt = pt, - .unit = .main, - .entry = type_gop.value_ptr.*, + .unit = unit, + .entry = entry, .any_children = false, .func = .none, .func_sym_index = undefined, @@ -4850,7 +4742,6 @@ fn flushWriterError(dwarf: *Dwarf, pt: Zcu.PerThread) (FlushError || Writer.Erro .debug_info = .init(dwarf.gpa), .debug_line = .init(dwarf.gpa), .debug_loclists = .init(dwarf.gpa), - .pending_lazy = .empty, }; defer wip_nav.deinit(); const diw = &wip_nav.debug_info.writer; @@ -4868,7 +4759,7 @@ fn flushWriterError(dwarf: *Dwarf, pt: Zcu.PerThread) (FlushError || Writer.Erro } if (global_error_set_names.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written()); - try wip_nav.updateLazy(.unneeded); + try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); } for (dwarf.mods.keys(), dwarf.mods.values()) |mod, *mod_info| { @@ -5316,6 +5207,8 @@ const AbbrevCode = enum { inferred_error_set_type, ptr_type, ptr_sentinel_type, + ptr_aligned_type, + ptr_aligned_sentinel_type, is_const, is_volatile, array_type, @@ -5952,7 +5845,6 @@ const AbbrevCode = enum { .tag = .pointer_type, .attrs = &.{ .{ .name, .strp }, - .{ .alignment, .udata }, .{ .address_class, .data1 }, .{ .type, .ref_addr }, }, @@ -5962,6 +5854,24 @@ const AbbrevCode = enum { .attrs = &.{ .{ .name, .strp }, .{ .ZIG_sentinel, .block }, + .{ .address_class, .data1 }, + .{ .type, .ref_addr }, + }, + }, + .ptr_aligned_type = .{ + .tag = .pointer_type, + .attrs = &.{ + .{ .name, .strp }, + .{ .alignment, .udata }, + .{ .address_class, .data1 }, + .{ .type, .ref_addr }, + }, + }, + .ptr_aligned_sentinel_type = .{ + .tag = .pointer_type, + .attrs = &.{ + .{ .name, .strp }, + .{ .ZIG_sentinel, .block }, .{ .alignment, .udata }, .{ .address_class, .data1 }, .{ .type, .ref_addr }, diff --git a/src/link/Elf.zig b/src/link/Elf.zig @@ -1711,23 +1711,13 @@ pub fn updateContainerType( self: *Elf, pt: Zcu.PerThread, ty: InternPool.Index, + success: bool, ) link.File.UpdateContainerTypeError!void { if (build_options.skip_non_native and builtin.object_format != .elf) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - const zcu = pt.zcu; - const gpa = zcu.gpa; - return self.zigObjectPtr().?.updateContainerType(pt, ty) catch |err| switch (err) { + return self.zigObjectPtr().?.updateContainerType(pt, ty, success) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, - else => |e| { - try zcu.failed_types.putNoClobber(gpa, ty, try Zcu.ErrorMsg.create( - gpa, - zcu.typeSrcLoc(ty), - "failed to update container type: {s}", - .{@errorName(e)}, - )); - return error.TypeFailureReported; - }, }; } diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig @@ -775,7 +775,7 @@ pub fn checkDuplicates(self: *Object, dupes: anytype, elf_file: *Elf) error{OutO const gop = try dupes.getOrPut(self.symbols_resolver.items[i]); if (!gop.found_existing) { - gop.value_ptr.* = .{}; + gop.value_ptr.* = .empty; } try gop.value_ptr.append(elf_file.base.comp.gpa, self.index); } diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig @@ -84,7 +84,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void { const ptr_size = elf_file.ptrWidthBytes(); try self.atoms.append(gpa, .{ .extra_index = try self.addAtomExtra(gpa, .{}) }); // null input section - try self.relocs.append(gpa, .{}); // null relocs section + try self.relocs.append(gpa, .empty); // null relocs section try self.strtab.buffer.append(gpa, 0); { @@ -546,7 +546,7 @@ fn newAtom(self: *ZigObject, allocator: Allocator, name_off: u32) !Atom.Index { atom_ptr.name_offset = name_off; const relocs_index: u32 = @intCast(self.relocs.items.len); - self.relocs.addOneAssumeCapacity().* = .{}; + self.relocs.addOneAssumeCapacity().* = .empty; atom_ptr.relocs_section_index = relocs_index; return index; @@ -730,7 +730,7 @@ pub fn checkDuplicates(self: *ZigObject, dupes: anytype, elf_file: *Elf) error{O const gop = try dupes.getOrPut(self.symbols_resolver.items[i]); if (!gop.found_existing) { - gop.value_ptr.* = .{}; + gop.value_ptr.* = .empty; } try gop.value_ptr.append(elf_file.base.comp.gpa, self.index); } @@ -1479,7 +1479,7 @@ fn updateTlv( log.debug("updateTlv {f}({d})", .{ nav.fqn.fmt(ip), nav_index }); - const required_alignment = pt.navAlignment(nav_index); + const required_alignment = zcu.navAlignment(nav_index); const sym = self.symbol(sym_index); const esym = &self.symtab.items(.elf_sym)[sym.esym_index]; @@ -1719,11 +1719,12 @@ pub fn updateContainerType( self: *ZigObject, pt: Zcu.PerThread, ty: InternPool.Index, + success: bool, ) !void { const tracy = trace(@src()); defer tracy.end(); - if (self.dwarf) |*dwarf| try dwarf.updateContainerType(pt, ty); + if (self.dwarf) |*dwarf| try dwarf.updateContainerType(pt, ty, success); } fn updateLazySymbol( diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig @@ -2906,7 +2906,7 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) try elf.nodes.ensureUnusedCapacity(gpa, 1); const sec_si = elf.navSection(ip, nav.status.fully_resolved); const ni = try elf.mf.addLastChildNode(gpa, sec_si.node(elf), .{ - .alignment = pt.navAlignment(nav_index).toStdMem(), + .alignment = zcu.navAlignment(nav_index).toStdMem(), .moved = true, }); elf.nodes.appendAssumeCapacity(.{ .nav = nmi }); diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig @@ -561,7 +561,7 @@ fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool { defer macho_file.undefs_mutex.unlock(io); const gop = try macho_file.undefs.getOrPut(gpa, file.getGlobals()[rel.target]); if (!gop.found_existing) { - gop.value_ptr.* = .{ .refs = .{} }; + gop.value_ptr.* = .{ .refs = .empty }; } try gop.value_ptr.refs.append(gpa, .{ .index = self.atom_index, .file = self.file }); return true; diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig @@ -3,7 +3,7 @@ data: std.ArrayList(u8) = .empty, basename: []const u8, index: File.Index, -symtab: std.MultiArrayList(Nlist) = .{}, +symtab: std.MultiArrayList(Nlist) = .empty, strtab: StringTable = .{}, symbols: std.ArrayList(Symbol) = .empty, @@ -29,7 +29,7 @@ uavs: UavTable = .{}, tlv_initializers: TlvInitializerTable = .{}, /// A table of relocations. -relocs: RelocationTable = .{}, +relocs: RelocationTable = .empty, dwarf: ?Dwarf = null, @@ -150,7 +150,7 @@ fn newAtom(self: *ZigObject, allocator: Allocator, name: MachO.String, macho_fil atom.name = name; const relocs_index = @as(u32, @intCast(self.relocs.items.len)); - self.relocs.addOneAssumeCapacity().* = .{}; + self.relocs.addOneAssumeCapacity().* = .empty; atom.addExtra(.{ .rel_index = relocs_index, .rel_count = 0 }, macho_file); return index; @@ -925,7 +925,7 @@ pub fn updateNav( const sect_index = try self.getNavOutputSection(macho_file, zcu, nav_index, code); if (isThreadlocal(macho_file, nav_index)) - try self.updateTlv(macho_file, pt, nav_index, sym_index, sect_index, code) + try self.updateTlv(macho_file, zcu, nav_index, sym_index, sect_index, code) else try self.updateNavCode(macho_file, pt, nav_index, sym_index, sect_index, code); @@ -1030,13 +1030,13 @@ fn updateNavCode( fn updateTlv( self: *ZigObject, macho_file: *MachO, - pt: Zcu.PerThread, + zcu: *Zcu, nav_index: InternPool.Nav.Index, sym_index: Symbol.Index, sect_index: u8, code: []const u8, ) !void { - const ip = &pt.zcu.intern_pool; + const ip = &zcu.intern_pool; const nav = ip.getNav(nav_index); log.debug("updateTlv {f} (0x{x})", .{ nav.fqn.fmt(ip), nav_index }); @@ -1045,7 +1045,7 @@ fn updateTlv( const init_sym_index = try self.createTlvInitializer( macho_file, nav.fqn.toSlice(ip), - pt.navAlignment(nav_index), + zcu.navAlignment(nav_index), sect_index, code, ); diff --git a/src/link/MachO/file.zig b/src/link/MachO/file.zig @@ -258,7 +258,7 @@ pub const File = union(enum) { const gop = try macho_file.dupes.getOrPut(gpa, file.getGlobals()[i]); if (!gop.found_existing) { - gop.value_ptr.* = .{}; + gop.value_ptr.* = .empty; } try gop.value_ptr.append(gpa, file.getIndex()); } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig @@ -78,7 +78,7 @@ export_table: bool, /// Output name of the file name: []const u8, /// List of relocatable files to be linked into the final binary. -objects: std.ArrayList(Object) = .{}, +objects: std.ArrayList(Object) = .empty, func_types: std.AutoArrayHashMapUnmanaged(FunctionType, void) = .empty, /// Provides a mapping of both imports and provided functions to symbol name. @@ -278,7 +278,7 @@ any_tls_relocs: bool = false, any_passive_inits: bool = false, /// All MIR instructions for all Zcu functions. -mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, +mir_instructions: std.MultiArrayList(Mir.Inst) = .empty, /// Corresponds to `mir_instructions`. mir_extra: std.ArrayList(u32) = .empty, /// All local types for all Zcu functions. @@ -4226,7 +4226,7 @@ fn convertZcuFnType( if (CodeGen.firstParamSRet(cc, return_type, zcu, target)) { try params_buffer.append(gpa, .i32); // memory address is always a 32-bit handle - } else if (return_type.hasRuntimeBitsIgnoreComptime(zcu)) { + } else if (return_type.hasRuntimeBits(zcu)) { if (cc == .wasm_mvp) { switch (abi.classifyType(return_type, zcu)) { .direct => |scalar_ty| { @@ -4245,7 +4245,7 @@ fn convertZcuFnType( // param types for (params) |param_type_ip| { const param_type = Zcu.Type.fromInterned(param_type_ip); - if (!param_type.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!param_type.hasRuntimeBits(zcu)) continue; switch (cc) { .wasm_mvp => { diff --git a/src/link/Wasm/Flush.zig b/src/link/Wasm/Flush.zig @@ -154,7 +154,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { .type_index = try wasm.internFunctionType(.auto, &.{int_tag_ty.ip_index}, .slice_const_u8_sentinel_0, target), .table_index = @intCast(wasm.tag_name_offs.items.len), } }; - const tag_names = ip.loadEnumType(data.ip_index).names; + const tag_names = ip.loadEnumType(data.ip_index).field_names; for (tag_names.get(ip)) |tag_name| { const slice = tag_name.toSlice(ip); try wasm.tag_name_offs.append(gpa, @intCast(wasm.tag_name_bytes.items.len)); @@ -1869,7 +1869,7 @@ fn emitTagNameFunction( const zcu = comp.zcu.?; const ip = &zcu.intern_pool; const enum_type = ip.loadEnumType(enum_type_ip); - const tag_values = enum_type.values.get(ip); + const tag_values = enum_type.field_values.get(ip); const slice_abi_size = 8; const encoded_alignment = @ctz(@as(u32, 4)); @@ -1908,7 +1908,7 @@ fn emitTagNameFunction( return; } - const int_info = Zcu.Type.intInfo(.fromInterned(enum_type.tag_ty), zcu); + const int_info = Zcu.Type.intInfo(.fromInterned(enum_type.int_tag_type), zcu); const outer_block_type: std.wasm.BlockType = switch (int_info.bits) { 0...32 => .i32, 33...64 => .i64, diff --git a/src/link/tapi/parse.zig b/src/link/tapi/parse.zig @@ -530,7 +530,7 @@ const Parser = struct { fn leaf_value(self: *Parser) ParseError!*Node { const node = try self.allocator.create(Node.Value); errdefer self.allocator.destroy(node); - node.* = .{ .string_value = .{} }; + node.* = .{ .string_value = .empty }; node.base.tree = self.tree; node.base.start = self.token_it.pos; errdefer node.string_value.deinit(self.allocator); diff --git a/src/main.zig b/src/main.zig @@ -979,7 +979,7 @@ fn buildOutputType( .dirs = undefined, .object_format = null, .dynamic_linker = null, - .modules = .{}, + .modules = .empty, .opts = .{ .is_test = switch (arg_mode) { .zig_test, .zig_test_obj => true, @@ -1006,18 +1006,18 @@ fn buildOutputType( .windows_libs = .empty, .link_inputs = .empty, - .c_source_files = .{}, - .rc_source_files = .{}, + .c_source_files = .empty, + .rc_source_files = .empty, - .llvm_m_args = .{}, + .llvm_m_args = .empty, .sysroot = null, - .lib_directories = .{}, // populated by createModule() - .lib_dir_args = .{}, // populated from CLI arg parsing + .lib_directories = .empty, // populated by createModule() + .lib_dir_args = .empty, // populated from CLI arg parsing .libc_installation = null, .want_native_include_dirs = false, - .frameworks = .{}, - .framework_dirs = .{}, - .rpath_list = .{}, + .frameworks = .empty, + .framework_dirs = .empty, + .rpath_list = .empty, .each_lib_rpath = null, .libc_paths_file = EnvVar.ZIG_LIBC.get(environ_map), .native_system_include_paths = &.{}, diff --git a/src/mutable_value.zig b/src/mutable_value.zig @@ -18,7 +18,7 @@ pub const MutableValue = union(enum) { opt_payload: SubValue, /// An aggregate consisting of a single repeated value. repeated: SubValue, - /// An aggregate of `u8` consisting of "plain" bytes (no lazy or undefined elements). + /// An aggregate of `u8` consisting of "plain" bytes (no undefined elements). bytes: Bytes, /// An aggregate with arbitrary sub-values. aggregate: Aggregate, @@ -97,8 +97,8 @@ pub const MutableValue = union(enum) { /// * Non-error error unions use `eu_payload` /// * Non-null optionals use `eu_payload /// * Slices use `slice` - /// * Unions use `un` - /// * Aggregates use `repeated` or `bytes` or `aggregate` + /// * Unions use `un` (excluding packed unions) + /// * Aggregates use `repeated` or `bytes` or `aggregate` (excluding packed structs) /// If `!allow_bytes`, the `bytes` representation will not be used. /// If `!allow_repeated`, the `repeated` representation will not be used. pub fn unintern( @@ -209,6 +209,7 @@ pub const MutableValue = union(enum) { .undef => |ty_ip| switch (Type.fromInterned(ty_ip).zigTypeTag(zcu)) { .@"struct", .array, .vector => |type_tag| { const ty = Type.fromInterned(ty_ip); + if (type_tag == .@"struct" and ty.containerLayout(zcu) == .@"packed") return; const opt_sent = ty.sentinel(zcu); if (type_tag == .@"struct" or opt_sent != null or !allow_repeated) { const len_no_sent = ip.aggregateTypeLen(ty_ip); @@ -241,15 +242,18 @@ pub const MutableValue = union(enum) { } }; } }, - .@"union" => { - const payload = try arena.create(MutableValue); - const backing_ty = try Type.fromInterned(ty_ip).unionBackingType(pt); - payload.* = .{ .interned = try pt.intern(.{ .undef = backing_ty.toIntern() }) }; - mv.* = .{ .un = .{ - .ty = ty_ip, - .tag = .none, - .payload = payload, - } }; + .@"union" => switch (Type.fromInterned(ty_ip).containerLayout(zcu)) { + .auto, .@"packed" => {}, + .@"extern" => { + const payload = try arena.create(MutableValue); + const backing_ty = try Type.fromInterned(ty_ip).externUnionBackingType(pt); + payload.* = .{ .interned = try pt.intern(.{ .undef = backing_ty.toIntern() }) }; + mv.* = .{ .un = .{ + .ty = ty_ip, + .tag = .none, + .payload = payload, + } }; + }, }, .pointer => { const ptr_ty = ip.indexToKey(ty_ip).ptr_type; @@ -415,16 +419,7 @@ pub const MutableValue = union(enum) { } else if (!is_struct and is_trivial_int and Type.fromInterned(a.ty).childType(zcu).toIntern() == .u8_type) { // See if we can switch to `bytes` repr for (a.elems) |e| { - switch (e) { - else => break, - .interned => |ip_index| switch (ip.indexToKey(ip_index)) { - else => break, - .int => |int| switch (int.storage) { - .u64, .i64, .big_int => {}, - .lazy_align, .lazy_size => break, - }, - }, - } + if (!e.isTrivialInt(zcu)) break; } else { const bytes = try arena.alloc(u8, a.elems.len); for (a.elems, bytes) |elem_val, *b| { @@ -494,10 +489,7 @@ pub const MutableValue = union(enum) { else => false, .interned => |ip_index| switch (zcu.intern_pool.indexToKey(ip_index)) { else => false, - .int => |int| switch (int.storage) { - .u64, .i64, .big_int => true, - .lazy_align, .lazy_size => false, - }, + .int => true, }, }; } diff --git a/src/print_value.zig b/src/print_value.zig @@ -25,10 +25,7 @@ pub fn formatSema(ctx: FormatContext, writer: *Writer) Writer.Error!void { const sema = ctx.opt_sema.?; return print(ctx.val, writer, ctx.depth, ctx.pt, sema) catch |err| switch (err) { error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function - error.ComptimeBreak, error.ComptimeReturn => unreachable, - error.AnalysisFail => unreachable, // TODO: re-evaluate when we use `sema` more fully - error.Canceled => @panic("TODO"), // pls stop returning this error mlugg - else => |e| return e, + error.WriteFailed => |e| return e, }; } @@ -36,9 +33,7 @@ pub fn format(ctx: FormatContext, writer: *Writer) Writer.Error!void { std.debug.assert(ctx.opt_sema == null); return print(ctx.val, writer, ctx.depth, ctx.pt, null) catch |err| switch (err) { error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function - error.ComptimeBreak, error.ComptimeReturn, error.AnalysisFail => unreachable, - error.Canceled => @panic("TODO"), // pls stop returning this error mlugg - else => |e| return e, + error.WriteFailed => |e| return e, }; } @@ -48,7 +43,7 @@ pub fn print( level: u8, pt: Zcu.PerThread, opt_sema: ?*Sema, -) (Writer.Error || Zcu.CompileError)!void { +) (Writer.Error || Allocator.Error)!void { const zcu = pt.zcu; const ip = &zcu.intern_pool; switch (ip.indexToKey(val.toIntern())) { @@ -72,8 +67,12 @@ pub fn print( .undef => try writer.writeAll("undefined"), .simple_value => |simple_value| switch (simple_value) { .void => try writer.writeAll("{}"), - .empty_tuple => try writer.writeAll(".{}"), - else => try writer.writeAll(@tagName(simple_value)), + + .null, + .true, + .false, + .@"unreachable", + => try writer.writeAll(@tagName(simple_value)), }, .variable => try writer.writeAll("(variable)"), .@"extern" => |e| try writer.print("(extern '{f}')", .{e.name.fmt(ip)}), @@ -81,14 +80,6 @@ pub fn print( .int => |int| switch (int.storage) { inline .u64, .i64 => |x| try writer.print("{d}", .{x}), .big_int => |x| try writer.print("{d}", .{x}), - .lazy_align => |ty| if (opt_sema != null) { - const a = try Type.fromInterned(ty).abiAlignmentSema(pt); - try writer.print("{d}", .{a.toByteUnits() orelse 0}); - } else try writer.print("@alignOf({f})", .{Type.fromInterned(ty).fmt(pt)}), - .lazy_size => |ty| if (opt_sema != null) { - const s = try Type.fromInterned(ty).abiSizeSema(pt); - try writer.print("{d}", .{s}); - } else try writer.print("@sizeOf({f})", .{Type.fromInterned(ty).fmt(pt)}), }, .err => |err| try writer.print("error.{f}", .{ err.name.fmt(ip), @@ -104,8 +95,8 @@ pub fn print( }), .enum_tag => |enum_tag| { const enum_type = ip.loadEnumType(val.typeOf(zcu).toIntern()); - if (enum_type.tagValueIndex(ip, val.toIntern())) |tag_index| { - return writer.print(".{f}", .{enum_type.names.get(ip)[tag_index].fmt(ip)}); + if (enum_type.tagValueIndex(ip, enum_tag.int)) |tag_index| { + return writer.print(".{f}", .{enum_type.field_names.get(ip)[tag_index].fmt(ip)}); } if (level == 0) { return writer.writeAll("@enumFromInt(...)"); @@ -114,7 +105,6 @@ pub fn print( try print(Value.fromInterned(enum_tag.int), writer, level - 1, pt, opt_sema); try writer.writeAll(")"); }, - .empty_enum_value => try writer.writeAll("(empty enum value)"), .float => |float| switch (float.storage) { inline else => |x| try writer.print("{d}", .{@as(f64, @floatCast(x))}), }, @@ -123,7 +113,7 @@ pub fn print( if (slice.len == .zero_usize) { return writer.writeAll("&.{}"); } - try print(.fromInterned(slice.ptr), writer, level - 1, pt, opt_sema); + try print(.fromInterned(slice.ptr), writer, level, pt, opt_sema); } else { const print_contents = switch (ip.getBackingAddrTag(slice.ptr).?) { .field, .arr_elem, .eu_payload, .opt_payload => unreachable, @@ -167,7 +157,7 @@ pub fn print( return; } if (un.tag == .none) { - const backing_ty = try val.typeOf(zcu).unionBackingType(pt); + const backing_ty = try val.typeOf(zcu).externUnionBackingType(pt); try writer.print("@bitCast(@as({f}, ", .{backing_ty.fmt(pt)}); try print(Value.fromInterned(un.val), writer, level - 1, pt, opt_sema); try writer.writeAll("))"); @@ -179,6 +169,35 @@ pub fn print( try writer.writeAll(" }"); } }, + .bitpack => |bitpack| { + if (level == 0) { + return writer.writeAll(".{ ... }"); + } + const ty: Type = .fromInterned(bitpack.ty); + switch (ty.zigTypeTag(zcu)) { + .@"struct" => { + if (ty.structFieldCount(zcu) == 0) { + return writer.writeAll(".{}"); + } + try writer.writeAll(".{ "); + const max_len = @min(ty.structFieldCount(zcu), max_aggregate_items); + for (0..max_len) |i| { + if (i != 0) try writer.writeAll(", "); + const field_name = ty.structFieldName(@intCast(i), zcu).unwrap().?; + try writer.print(".{f} = ", .{field_name.fmt(ip)}); + try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema); + } + try writer.writeAll(" }"); + return; + }, + .@"union" => { + try writer.print("@bitCast(@as({f}, ", .{ty.bitpackBackingInt(zcu).fmt(pt)}); + try print(.fromInterned(bitpack.backing_int_val), writer, level - 1, pt, opt_sema); + try writer.writeAll("))"); + }, + else => unreachable, + } + }, .memoized_call => unreachable, } } @@ -191,7 +210,7 @@ fn printAggregate( level: u8, pt: Zcu.PerThread, opt_sema: ?*Sema, -) (Writer.Error || Zcu.CompileError)!void { +) (Writer.Error || Allocator.Error)!void { if (level == 0) { if (is_ref) try writer.writeByte('&'); return writer.writeAll(".{ ... }"); @@ -256,17 +275,26 @@ fn printAggregate( const len = ty.arrayLen(zcu); if (is_ref) try writer.writeByte('&'); - try writer.writeAll(".{ "); - - const max_len = @min(len, max_aggregate_items); - for (0..max_len) |i| { - if (i != 0) try writer.writeAll(", "); - try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema); - } - if (len > max_aggregate_items) { - try writer.writeAll(", ..."); + switch (len) { + 0 => try writer.writeAll(".{}"), + 1 => { + try writer.writeAll(".{"); + try print(try val.fieldValue(pt, 0), writer, level - 1, pt, opt_sema); + try writer.writeByte('}'); + }, + else => { + try writer.writeAll(".{ "); + const max_len = @min(len, max_aggregate_items); + for (0..max_len) |i| { + if (i != 0) try writer.writeAll(", "); + try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema); + } + if (len > max_aggregate_items) { + try writer.writeAll(", ..."); + } + try writer.writeAll(" }"); + }, } - return writer.writeAll(" }"); } fn printPtr( @@ -277,7 +305,7 @@ fn printPtr( level: u8, pt: Zcu.PerThread, opt_sema: ?*Sema, -) (Writer.Error || Zcu.CompileError)!void { +) (Writer.Error || Allocator.Error)!void { const ptr = switch (pt.zcu.intern_pool.indexToKey(ptr_val.toIntern())) { .undef => return writer.writeAll("undefined"), .ptr => |ptr| ptr, @@ -302,10 +330,7 @@ fn printPtr( var arena = std.heap.ArenaAllocator.init(pt.zcu.gpa); defer arena.deinit(); - const derivation = if (opt_sema) |sema| - try ptr_val.pointerDerivationAdvanced(arena.allocator(), pt, true, sema) - else - try ptr_val.pointerDerivationAdvanced(arena.allocator(), pt, false, null); + const derivation = try ptr_val.pointerDerivation(arena.allocator(), pt, opt_sema); _ = try printPtrDerivation(derivation, writer, pt, want_kind, .{ .print_val = .{ .level = level, @@ -442,18 +467,30 @@ pub fn printPtrDerivation( .uav_ptr => |uav| { const ty = Value.fromInterned(uav.val).typeOf(zcu); try writer.print("@as({f}, ", .{ty.fmt(pt)}); - try print(Value.fromInterned(uav.val), writer, x.level - 1, pt, x.opt_sema); + if (x.level == 0) { + try writer.writeAll("..."); + } else { + try print(Value.fromInterned(uav.val), writer, x.level - 1, pt, x.opt_sema); + } try writer.writeByte(')'); }, .comptime_alloc_ptr => |info| { try writer.print("@as({f}, ", .{info.val.typeOf(zcu).fmt(pt)}); - try print(info.val, writer, x.level - 1, pt, x.opt_sema); + if (x.level == 0) { + try writer.writeAll("..."); + } else { + try print(info.val, writer, x.level - 1, pt, x.opt_sema); + } try writer.writeByte(')'); }, .comptime_field_ptr => |val| { const ty = val.typeOf(zcu); try writer.print("@as({f}, ", .{ty.fmt(pt)}); - try print(val, writer, x.level - 1, pt, x.opt_sema); + if (x.level == 0) { + try writer.writeAll("..."); + } else { + try print(val, writer, x.level - 1, pt, x.opt_sema); + } try writer.writeByte(')'); }, else => unreachable, diff --git a/src/print_zir.zig b/src/print_zir.zig @@ -548,10 +548,10 @@ const Writer = struct { .shl_with_overflow, => try self.writeOverflowArithmetic(stream, extended), - .struct_decl => try self.writeStructDecl(stream, extended), - .union_decl => try self.writeUnionDecl(stream, extended), - .enum_decl => try self.writeEnumDecl(stream, extended), - .opaque_decl => try self.writeOpaqueDecl(stream, extended), + .struct_decl => try self.writeStructDecl(stream, inst), + .union_decl => try self.writeUnionDecl(stream, inst), + .enum_decl => try self.writeEnumDecl(stream, inst), + .opaque_decl => try self.writeOpaqueDecl(stream, inst), .tuple_decl => try self.writeTupleDecl(stream, extended), @@ -1427,187 +1427,57 @@ const Writer = struct { try self.writeSrcNode(stream, inst_data.src_node); } - fn writeStructDecl(self: *Writer, stream: *std.Io.Writer, extended: Zir.Inst.Extended.InstData) !void { - const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); - - const extra = self.code.extraData(Zir.Inst.StructDecl, extended.operand); + fn writeStructDecl(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void { + const struct_decl = self.code.getStructDecl(inst); const prev_parent_decl_node = self.parent_decl_node; - self.parent_decl_node = extra.data.src_node; + self.parent_decl_node = struct_decl.src_node; defer self.parent_decl_node = prev_parent_decl_node; - const fields_hash: std.zig.SrcHash = @bitCast([4]u32{ - extra.data.fields_hash_0, - extra.data.fields_hash_1, - extra.data.fields_hash_2, - extra.data.fields_hash_3, - }); - + const fields_hash = self.code.getAssociatedSrcHash(inst).?; try stream.print("hash({x}) ", .{&fields_hash}); - var extra_index: usize = extra.end; - - const captures_len = if (small.has_captures_len) blk: { - const captures_len = self.code.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - - const fields_len = if (small.has_fields_len) blk: { - const fields_len = self.code.extra[extra_index]; - extra_index += 1; - break :blk fields_len; - } else 0; - - const decls_len = if (small.has_decls_len) blk: { - const decls_len = self.code.extra[extra_index]; - extra_index += 1; - break :blk decls_len; - } else 0; + try stream.print("{s}, ", .{@tagName(struct_decl.name_strategy)}); - try self.writeFlag(stream, "known_non_opv, ", small.known_non_opv); - try self.writeFlag(stream, "known_comptime_only, ", small.known_comptime_only); - - try stream.print("{s}, ", .{@tagName(small.name_strategy)}); - - extra_index = try self.writeCaptures(stream, extra_index, captures_len); - try stream.writeAll(", "); - - if (small.has_backing_int) { - const backing_int_body_len = self.code.extra[extra_index]; - extra_index += 1; + if (struct_decl.backing_int_type_body) |backing_int_type_body| { + assert(struct_decl.layout == .@"packed"); try stream.writeAll("packed("); - if (backing_int_body_len == 0) { - const backing_int_ref: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]); - extra_index += 1; - try self.writeInstRef(stream, backing_int_ref); - } else { - const body = self.code.bodySlice(extra_index, backing_int_body_len); - extra_index += backing_int_body_len; - self.indent += 2; - try self.writeBracedDecl(stream, body); - self.indent -= 2; - } + try self.writeBracedDecl(stream, backing_int_type_body); try stream.writeAll("), "); } else { - try stream.print("{s}, ", .{@tagName(small.layout)}); + try stream.print("{s}, ", .{@tagName(struct_decl.layout)}); } - if (decls_len == 0) { - try stream.writeAll("{}, "); - } else { - try stream.writeAll("{\n"); - self.indent += 2; - try self.writeBody(stream, self.code.bodySlice(extra_index, decls_len)); - self.indent -= 2; - extra_index += decls_len; - try stream.splatByteAll(' ', self.indent); - try stream.writeAll("}, "); - } + try self.writeCaptures(stream, struct_decl.captures, struct_decl.capture_names); + try stream.writeAll(", "); + try self.writeBracedDecl(stream, struct_decl.decls); + try stream.writeAll(", "); - if (fields_len == 0) { - try stream.writeAll("{}, {}) "); + if (struct_decl.field_names.len == 0) { + try stream.writeAll("{}) "); } else { - const bits_per_field = 4; - const fields_per_u32 = 32 / bits_per_field; - const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; - const Field = struct { - type_len: u32 = 0, - align_len: u32 = 0, - init_len: u32 = 0, - type: Zir.Inst.Ref = .none, - name: Zir.NullTerminatedString, - is_comptime: bool, - }; - const fields = try self.arena.alloc(Field, fields_len); - { - var bit_bag_index: usize = extra_index; - extra_index += bit_bags_count; - var cur_bit_bag: u32 = undefined; - var field_i: u32 = 0; - while (field_i < fields_len) : (field_i += 1) { - if (field_i % fields_per_u32 == 0) { - cur_bit_bag = self.code.extra[bit_bag_index]; - bit_bag_index += 1; - } - const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - const has_default = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - const is_comptime = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - - const field_name_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]); - extra_index += 1; - - fields[field_i] = .{ - .is_comptime = is_comptime, - .name = field_name_index, - }; - - if (has_type_body) { - fields[field_i].type_len = self.code.extra[extra_index]; - } else { - fields[field_i].type = @enumFromInt(self.code.extra[extra_index]); - } - extra_index += 1; - - if (has_align) { - fields[field_i].align_len = self.code.extra[extra_index]; - extra_index += 1; - } - - if (has_default) { - fields[field_i].init_len = self.code.extra[extra_index]; - extra_index += 1; - } - } - } - try stream.writeAll("{\n"); self.indent += 2; - for (fields, 0..) |field, i| { + var it = struct_decl.iterateFields(); + while (it.next()) |field| { try stream.splatByteAll(' ', self.indent); try self.writeFlag(stream, "comptime ", field.is_comptime); - if (field.name != .empty) { - const field_name = self.code.nullTerminatedString(field.name); - try stream.print("{f}: ", .{std.zig.fmtIdP(field_name)}); - } else { - try stream.print("@\"{d}\": ", .{i}); - } - if (field.type != .none) { - try self.writeInstRef(stream, field.type); - } - - if (field.type_len > 0) { - const body = self.code.bodySlice(extra_index, field.type_len); - extra_index += body.len; - self.indent += 2; - try self.writeBracedDecl(stream, body); - self.indent -= 2; - } + const field_name = self.code.nullTerminatedString(field.name); + try stream.print("{f}: ", .{std.zig.fmtIdP(field_name)}); - if (field.align_len > 0) { - const body = self.code.bodySlice(extra_index, field.align_len); - extra_index += body.len; - self.indent += 2; + self.indent += 2; + try self.writeBracedDecl(stream, field.type_body); + if (field.align_body) |body| { try stream.writeAll(" align("); try self.writeBracedDecl(stream, body); - try stream.writeAll(")"); - self.indent -= 2; + try stream.writeByte(')'); } - - if (field.init_len > 0) { - const body = self.code.bodySlice(extra_index, field.init_len); - extra_index += body.len; - self.indent += 2; + if (field.default_body) |body| { try stream.writeAll(" = "); try self.writeBracedDecl(stream, body); - self.indent -= 2; } + self.indent -= 2; try stream.writeAll(",\n"); } @@ -1619,266 +1489,119 @@ const Writer = struct { try self.writeSrcNode(stream, .zero); } - fn writeUnionDecl(self: *Writer, stream: *std.Io.Writer, extended: Zir.Inst.Extended.InstData) !void { - const small = @as(Zir.Inst.UnionDecl.Small, @bitCast(extended.small)); - - const extra = self.code.extraData(Zir.Inst.UnionDecl, extended.operand); + fn writeUnionDecl(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void { + const union_decl = self.code.getUnionDecl(inst); const prev_parent_decl_node = self.parent_decl_node; - self.parent_decl_node = extra.data.src_node; + self.parent_decl_node = union_decl.src_node; defer self.parent_decl_node = prev_parent_decl_node; - const fields_hash: std.zig.SrcHash = @bitCast([4]u32{ - extra.data.fields_hash_0, - extra.data.fields_hash_1, - extra.data.fields_hash_2, - extra.data.fields_hash_3, - }); - + const fields_hash = self.code.getAssociatedSrcHash(inst).?; try stream.print("hash({x}) ", .{&fields_hash}); - var extra_index: usize = extra.end; - - const tag_type_ref = if (small.has_tag_type) blk: { - const tag_type_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index])); - extra_index += 1; - break :blk tag_type_ref; - } else .none; - - const captures_len = if (small.has_captures_len) blk: { - const captures_len = self.code.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - - const body_len = if (small.has_body_len) blk: { - const body_len = self.code.extra[extra_index]; - extra_index += 1; - break :blk body_len; - } else 0; - - const fields_len = if (small.has_fields_len) blk: { - const fields_len = self.code.extra[extra_index]; - extra_index += 1; - break :blk fields_len; - } else 0; - - const decls_len = if (small.has_decls_len) blk: { - const decls_len = self.code.extra[extra_index]; - extra_index += 1; - break :blk decls_len; - } else 0; + try stream.print("{s}, ", .{@tagName(union_decl.name_strategy)}); - try stream.print("{s}, {s}, ", .{ - @tagName(small.name_strategy), @tagName(small.layout), - }); - try self.writeFlag(stream, "autoenum, ", small.auto_enum_tag); + switch (union_decl.kind) { + .auto => try stream.writeAll("auto, "), + .@"extern" => try stream.writeAll("extern, "), + .@"packed" => try stream.writeAll("packed, "), + .packed_explicit => { + try stream.writeAll("packed("); + try self.writeBracedDecl(stream, union_decl.arg_type_body.?); + try stream.writeAll("), "); + }, + .tagged_explicit => { + try stream.writeAll("tagged("); + try self.writeBracedDecl(stream, union_decl.arg_type_body.?); + try stream.writeAll("), "); + }, + .tagged_enum => try stream.writeAll("tagged(enum), "), + .tagged_enum_explicit => { + try stream.writeAll("tagged(enum("); + try self.writeBracedDecl(stream, union_decl.arg_type_body.?); + try stream.writeAll(")), "); + }, + } - extra_index = try self.writeCaptures(stream, extra_index, captures_len); + try self.writeCaptures(stream, union_decl.captures, union_decl.capture_names); + try stream.writeAll(", "); + try self.writeBracedDecl(stream, union_decl.decls); try stream.writeAll(", "); - if (decls_len == 0) { - try stream.writeAll("{}"); + if (union_decl.field_names.len == 0) { + try stream.writeAll("}) "); } else { try stream.writeAll("{\n"); self.indent += 2; - try self.writeBody(stream, self.code.bodySlice(extra_index, decls_len)); - self.indent -= 2; - extra_index += decls_len; - try stream.splatByteAll(' ', self.indent); - try stream.writeAll("}"); - } - - if (tag_type_ref != .none) { - try stream.writeAll(", "); - try self.writeInstRef(stream, tag_type_ref); - } - - if (fields_len == 0) { - try stream.writeAll("}) "); - try self.writeSrcNode(stream, .zero); - return; - } - try stream.writeAll(", "); - const body = self.code.bodySlice(extra_index, body_len); - extra_index += body.len; + var it = union_decl.iterateFields(); + while (it.next()) |field| { + try stream.splatByteAll(' ', self.indent); + const field_name = self.code.nullTerminatedString(field.name); + try stream.print("{f}", .{std.zig.fmtIdP(field_name)}); - try self.writeBracedDecl(stream, body); - try stream.writeAll(", {\n"); + self.indent += 2; + if (field.type_body) |body| { + try stream.writeAll(": "); + try self.writeBracedDecl(stream, body); + } + if (field.align_body) |body| { + try stream.writeAll(" align("); + try self.writeBracedDecl(stream, body); + try stream.writeByte(')'); + } + if (field.value_body) |body| { + try stream.writeAll(" = "); + try self.writeBracedDecl(stream, body); + } + self.indent -= 2; - self.indent += 2; - const bits_per_field = 4; - const fields_per_u32 = 32 / bits_per_field; - const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; - const body_end = extra_index; - extra_index += bit_bags_count; - var bit_bag_index: usize = body_end; - var cur_bit_bag: u32 = undefined; - var field_i: u32 = 0; - while (field_i < fields_len) : (field_i += 1) { - if (field_i % fields_per_u32 == 0) { - cur_bit_bag = self.code.extra[bit_bag_index]; - bit_bag_index += 1; + try stream.writeAll(",\n"); } - const has_type = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - const has_value = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - const unused = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - - _ = unused; - - const field_name_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]); - const field_name = self.code.nullTerminatedString(field_name_index); - extra_index += 1; - + self.indent -= 2; try stream.splatByteAll(' ', self.indent); - try stream.print("{f}", .{std.zig.fmtIdP(field_name)}); - - if (has_type) { - const field_type = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index])); - extra_index += 1; - - try stream.writeAll(": "); - try self.writeInstRef(stream, field_type); - } - if (has_align) { - const align_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index])); - extra_index += 1; - - try stream.writeAll(" align("); - try self.writeInstRef(stream, align_ref); - try stream.writeAll(")"); - } - if (has_value) { - const default_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index])); - extra_index += 1; - - try stream.writeAll(" = "); - try self.writeInstRef(stream, default_ref); - } - try stream.writeAll(",\n"); + try stream.writeAll("}) "); } - - self.indent -= 2; - try stream.splatByteAll(' ', self.indent); - try stream.writeAll("}) "); try self.writeSrcNode(stream, .zero); } - fn writeEnumDecl(self: *Writer, stream: *std.Io.Writer, extended: Zir.Inst.Extended.InstData) !void { - const small = @as(Zir.Inst.EnumDecl.Small, @bitCast(extended.small)); - - const extra = self.code.extraData(Zir.Inst.EnumDecl, extended.operand); + fn writeEnumDecl(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void { + const enum_decl = self.code.getEnumDecl(inst); const prev_parent_decl_node = self.parent_decl_node; - self.parent_decl_node = extra.data.src_node; + self.parent_decl_node = enum_decl.src_node; defer self.parent_decl_node = prev_parent_decl_node; - const fields_hash: std.zig.SrcHash = @bitCast([4]u32{ - extra.data.fields_hash_0, - extra.data.fields_hash_1, - extra.data.fields_hash_2, - extra.data.fields_hash_3, - }); - + const fields_hash = self.code.getAssociatedSrcHash(inst).?; try stream.print("hash({x}) ", .{&fields_hash}); - var extra_index: usize = extra.end; - - const tag_type_ref = if (small.has_tag_type) blk: { - const tag_type_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index])); - extra_index += 1; - break :blk tag_type_ref; - } else .none; - - const captures_len = if (small.has_captures_len) blk: { - const captures_len = self.code.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - - const body_len = if (small.has_body_len) blk: { - const body_len = self.code.extra[extra_index]; - extra_index += 1; - break :blk body_len; - } else 0; - - const fields_len = if (small.has_fields_len) blk: { - const fields_len = self.code.extra[extra_index]; - extra_index += 1; - break :blk fields_len; - } else 0; - - const decls_len = if (small.has_decls_len) blk: { - const decls_len = self.code.extra[extra_index]; - extra_index += 1; - break :blk decls_len; - } else 0; - - try stream.print("{s}, ", .{@tagName(small.name_strategy)}); - try self.writeFlag(stream, "nonexhaustive, ", small.nonexhaustive); + try stream.print("{s}, ", .{@tagName(enum_decl.name_strategy)}); + try self.writeFlag(stream, "nonexhaustive, ", enum_decl.nonexhaustive); + if (enum_decl.tag_type_body) |tag_type_body| { + try stream.writeAll("tag("); + try self.writeBracedDecl(stream, tag_type_body); + try stream.writeAll("), "); + } - extra_index = try self.writeCaptures(stream, extra_index, captures_len); + try self.writeCaptures(stream, enum_decl.captures, enum_decl.capture_names); + try stream.writeAll(", "); + try self.writeBracedDecl(stream, enum_decl.decls); try stream.writeAll(", "); - if (decls_len == 0) { - try stream.writeAll("{}, "); + if (enum_decl.field_names.len == 0) { + try stream.writeAll("{}) "); } else { try stream.writeAll("{\n"); self.indent += 2; - try self.writeBody(stream, self.code.bodySlice(extra_index, decls_len)); - self.indent -= 2; - extra_index += decls_len; - try stream.splatByteAll(' ', self.indent); - try stream.writeAll("}, "); - } - - if (tag_type_ref != .none) { - try self.writeInstRef(stream, tag_type_ref); - try stream.writeAll(", "); - } - - const body = self.code.bodySlice(extra_index, body_len); - extra_index += body.len; - - try self.writeBracedDecl(stream, body); - if (fields_len == 0) { - try stream.writeAll(", {}) "); - } else { - try stream.writeAll(", {\n"); - - self.indent += 2; - const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; - const body_end = extra_index; - extra_index += bit_bags_count; - var bit_bag_index: usize = body_end; - var cur_bit_bag: u32 = undefined; - var field_i: u32 = 0; - while (field_i < fields_len) : (field_i += 1) { - if (field_i % 32 == 0) { - cur_bit_bag = self.code.extra[bit_bag_index]; - bit_bag_index += 1; - } - const has_tag_value = @as(u1, @truncate(cur_bit_bag)) != 0; - cur_bit_bag >>= 1; - - const field_name = self.code.nullTerminatedString(@enumFromInt(self.code.extra[extra_index])); - extra_index += 1; + var it = enum_decl.iterateFields(); + while (it.next()) |field| { try stream.splatByteAll(' ', self.indent); + const field_name = self.code.nullTerminatedString(field.name); try stream.print("{f}", .{std.zig.fmtIdP(field_name)}); - - if (has_tag_value) { - const tag_value_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index])); - extra_index += 1; - + if (field.value_body) |body| { try stream.writeAll(" = "); - try self.writeInstRef(stream, tag_value_ref); + try self.writeBracedDecl(stream, body); } try stream.writeAll(",\n"); } @@ -1889,47 +1612,18 @@ const Writer = struct { try self.writeSrcNode(stream, .zero); } - fn writeOpaqueDecl( - self: *Writer, - stream: *std.Io.Writer, - extended: Zir.Inst.Extended.InstData, - ) !void { - const small = @as(Zir.Inst.OpaqueDecl.Small, @bitCast(extended.small)); - const extra = self.code.extraData(Zir.Inst.OpaqueDecl, extended.operand); + fn writeOpaqueDecl(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void { + const opaque_decl = self.code.getOpaqueDecl(inst); const prev_parent_decl_node = self.parent_decl_node; - self.parent_decl_node = extra.data.src_node; + self.parent_decl_node = opaque_decl.src_node; defer self.parent_decl_node = prev_parent_decl_node; - var extra_index: usize = extra.end; - - const captures_len = if (small.has_captures_len) blk: { - const captures_len = self.code.extra[extra_index]; - extra_index += 1; - break :blk captures_len; - } else 0; - - const decls_len = if (small.has_decls_len) blk: { - const decls_len = self.code.extra[extra_index]; - extra_index += 1; - break :blk decls_len; - } else 0; - - try stream.print("{s}, ", .{@tagName(small.name_strategy)}); - - extra_index = try self.writeCaptures(stream, extra_index, captures_len); + try stream.print("{s}, ", .{@tagName(opaque_decl.name_strategy)}); + try self.writeCaptures(stream, opaque_decl.captures, opaque_decl.capture_names); try stream.writeAll(", "); - - if (decls_len == 0) { - try stream.writeAll("{}) "); - } else { - try stream.writeAll("{\n"); - self.indent += 2; - try self.writeBody(stream, self.code.bodySlice(extra_index, decls_len)); - self.indent -= 2; - try stream.splatByteAll(' ', self.indent); - try stream.writeAll("}) "); - } + try self.writeBracedDecl(stream, opaque_decl.decls); + try stream.writeAll(") "); try self.writeSrcNode(stream, .zero); } @@ -2588,14 +2282,11 @@ const Writer = struct { return stream.print("%{d}", .{@intFromEnum(inst)}); } - fn writeCaptures(self: *Writer, stream: *std.Io.Writer, extra_index: usize, captures_len: u32) !usize { - if (captures_len == 0) { - try stream.writeAll("{}"); - return extra_index; + fn writeCaptures(self: *Writer, stream: *std.Io.Writer, captures: []const Zir.Inst.Capture, capture_names: []const Zir.NullTerminatedString) !void { + if (captures.len == 0) { + assert(capture_names.len == 0); + return stream.writeAll("{}"); } - - const captures: []const Zir.Inst.Capture = @ptrCast(self.code.extra[extra_index..][0..captures_len]); - const capture_names: []const Zir.NullTerminatedString = @ptrCast(self.code.extra[extra_index + captures_len ..][0..captures_len]); for (captures, capture_names) |capture, name| { try stream.writeAll("{ "); if (name != .empty) { @@ -2604,8 +2295,6 @@ const Writer = struct { } try self.writeCapture(stream, capture); } - - return extra_index + 2 * captures_len; } fn writeCapture(self: *Writer, stream: *std.Io.Writer, capture: Zir.Inst.Capture) !void { diff --git a/stage1/zig.h b/stage1/zig.h @@ -151,6 +151,14 @@ #define zig_has_attribute(attribute) 0 #endif +#if __STDC_VERSION__ >= 201112L +#define zig_static_assert(cond, msg) _Static_assert(cond, msg) +#elif zig_has_attribute(unused) +#define zig_static_assert(cond, _) typedef char zig_expand_concat(zig_static_assert_fail_, __LINE__)[!!(cond)] __attribute__((unused)) +#else +#define zig_static_assert(cond, _) typedef char zig_expand_concat(zig_static_assert_fail_, __LINE__)[!!(cond)] +#endif + #if __STDC_VERSION__ >= 202311L #define zig_threadlocal thread_local #elif __STDC_VERSION__ >= 201112L @@ -259,7 +267,7 @@ #endif #if zig_has_attribute(packed) || defined(zig_tinyc) -#define zig_packed(definition) __attribute__((packed)) definition +#define zig_packed(definition) definition __attribute__((packed)) #elif defined(zig_msvc) #define zig_packed(definition) __pragma(pack(1)) definition __pragma(pack()) #else diff --git a/stage1/zig1.wasm b/stage1/zig1.wasm Binary files differ. diff --git a/test/behavior.zig b/test/behavior.zig @@ -24,7 +24,6 @@ test { _ = @import("behavior/duplicated_test_names.zig"); _ = @import("behavior/defer.zig"); _ = @import("behavior/destructure.zig"); - _ = @import("behavior/empty_union.zig"); _ = @import("behavior/enum.zig"); _ = @import("behavior/error.zig"); _ = @import("behavior/eval.zig"); diff --git a/test/behavior/align.zig b/test/behavior/align.zig @@ -18,6 +18,7 @@ test "global variable alignment" { test "large alignment of local constant" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // flaky + if (builtin.zig_backend == .stage2_c and builtin.target.abi == .msvc) return error.SkipZigTest; const x: f32 align(128) = 12.34; try std.testing.expect(@intFromPtr(&x) % 128 == 0); @@ -30,13 +31,41 @@ test "slicing array of length 1 can not assume runtime index is always zero" { var runtime_index: usize = 1; _ = &runtime_index; const slice = @as(*align(4) [1]u8, &foo)[runtime_index..]; - try expect(@TypeOf(slice) == []u8); + try expect(@TypeOf(slice) == []align(1) u8); try expect(slice.len == 0); try expect(@as(u2, @truncate(@intFromPtr(slice.ptr) - 1)) == 0); } -test "default alignment allows unspecified in type syntax" { - try expect(*u32 == *align(@alignOf(u32)) u32); +test "implicitly-aligned pointer is coercible to equivalent explicitly-aligned pointer" { + const A = *u32; + const B = *align(@alignOf(u32)) u32; + + comptime assert(A != B); + + const static = struct { + fn doTheTest() !void { + var buf: u32 = 123; + + const ptr: A = &buf; + const coerced_ptr: B = ptr; + + try expect(ptr == coerced_ptr); + try expect(ptr.* == 123); + try expect(coerced_ptr.* == 123); + + const ptr_ptr: *const A = &ptr; + const coerced_ptr_ptr: *const B = ptr_ptr; + + try expect(ptr_ptr == coerced_ptr_ptr); + try expect(ptr_ptr.* == &buf); + try expect(coerced_ptr_ptr.* == &buf); + try expect(ptr_ptr.*.* == 123); + try expect(coerced_ptr_ptr.*.* == 123); + } + }; + + try static.doTheTest(); + try comptime static.doTheTest(); } test "implicitly decreasing pointer alignment" { @@ -307,11 +336,15 @@ test "runtime-known array index has best alignment possible" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // take full advantage of over-alignment - var array align(4) = [_]u8{ 1, 2, 3, 4 }; + var array align(4) = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; comptime assert(@TypeOf(&array[0]) == *align(4) u8); - comptime assert(@TypeOf(&array[1]) == *u8); + comptime assert(@TypeOf(&array[1]) == *align(1) u8); comptime assert(@TypeOf(&array[2]) == *align(2) u8); - comptime assert(@TypeOf(&array[3]) == *u8); + comptime assert(@TypeOf(&array[3]) == *align(1) u8); + comptime assert(@TypeOf(&array[4]) == *align(4) u8); + comptime assert(@TypeOf(&array[5]) == *align(1) u8); + comptime assert(@TypeOf(&array[6]) == *align(2) u8); + comptime assert(@TypeOf(&array[7]) == *align(1) u8); // because align is too small but we still figure out to use 2 var bigger align(2) = [_]u64{ 1, 2, 3, 4 }; @@ -332,10 +365,14 @@ test "runtime-known array index has best alignment possible" { try testIndex(smaller[runtime_zero..].ptr, 3, *align(2) u32); // has to use ABI alignment because index known at runtime only - try testIndex2(&array, 0, *u8); - try testIndex2(&array, 1, *u8); - try testIndex2(&array, 2, *u8); - try testIndex2(&array, 3, *u8); + try testIndex2(&array, 0, *align(1) u8); + try testIndex2(&array, 1, *align(1) u8); + try testIndex2(&array, 2, *align(1) u8); + try testIndex2(&array, 3, *align(1) u8); + try testIndex2(&array, 4, *align(1) u8); + try testIndex2(&array, 5, *align(1) u8); + try testIndex2(&array, 6, *align(1) u8); + try testIndex2(&array, 7, *align(1) u8); } fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) !void { comptime assert(@TypeOf(&smaller[index]) == T); diff --git a/test/behavior/alignof.zig b/test/behavior/alignof.zig @@ -39,3 +39,8 @@ test "correct alignment for elements and slices of aligned array" { try expect(@alignOf(@TypeOf(&buf[start..end])) == @alignOf(*u8)); try expect(@alignOf(@TypeOf(&buf[start])) == @alignOf(*u8)); } + +test "@alignOf(anyerror!noreturn)" { + try expect(@alignOf(anyerror!noreturn) == @alignOf(anyerror)); + try expect(@alignOf(anyerror!anyerror!noreturn) == @alignOf(anyerror)); +} diff --git a/test/behavior/array.zig b/test/behavior/array.zig @@ -539,28 +539,6 @@ test "sentinel element count towards the ABI size calculation" { try comptime S.doTheTest(); } -test "zero-sized array with recursive type definition" { - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - - const U = struct { - fn foo(comptime T: type, comptime n: usize) type { - return struct { - s: [n]T, - x: usize = n, - }; - } - }; - - const S = struct { - list: U.foo(@This(), 0), - }; - - var t: S = .{ .list = .{ .s = undefined } }; - _ = &t; - try expect(@as(usize, 0) == t.list.x); -} - test "type coercion of anon struct literal to array" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig @@ -350,9 +350,6 @@ test "comptime @bitCast packed struct to int and back" { iint_neg2: i3 = -2, float: f32 = 3.14, @"enum": enum(u2) { A, B = 1, C, D } = .B, - vectorb: @Vector(3, bool) = .{ true, false, true }, - vectori: @Vector(2, u8) = .{ 127, 42 }, - vectorf: @Vector(2, f16) = .{ 3.14, 2.71 }, }; const Int = @typeInfo(S).@"struct".backing_integer.?; @@ -511,35 +508,6 @@ test "@bitCast of packed struct of bools all false" { try expect(@as(u8, @as(u4, @bitCast(p))) == 0); } -test "@bitCast of packed struct containing pointer" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://discourse.llvm.org/t/rfc-remove-most-constant-expressions/63179 - - const S = struct { - const A = packed struct { - ptr: *const u32, - }; - - const B = packed struct { - ptr: *const i32, - }; - - fn doTheTest() !void { - const x: u32 = 123; - var a: A = undefined; - a = .{ .ptr = &x }; - const b: B = @bitCast(a); - try expect(b.ptr.* == 123); - } - }; - - try S.doTheTest(); - try comptime S.doTheTest(); -} - test "@bitCast of extern struct containing pointer" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/call.zig b/test/behavior/call.zig @@ -551,19 +551,18 @@ test "generic function pointer can be called" { test "value returned from comptime function is comptime known" { const S = struct { - fn fields(comptime T: type) switch (@typeInfo(T)) { - .@"struct" => []const std.builtin.Type.StructField, + fn fieldCount(comptime T: type) switch (@typeInfo(T)) { + .@"struct" => comptime_int, else => unreachable, } { return switch (@typeInfo(T)) { - .@"struct" => |info| info.fields, + .@"struct" => |info| info.fields.len, else => unreachable, }; } }; - const fields_list = S.fields(@TypeOf(.{})); - if (fields_list.len != 0) - @compileError("Argument count mismatch"); + const fields_len = S.fieldCount(@TypeOf(.{})); + comptime assert(fields_len == 0); } test "registers get overwritten when ignoring return" { diff --git a/test/behavior/empty_union.zig b/test/behavior/empty_union.zig @@ -1,66 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const expect = std.testing.expect; - -test "switch on empty enum" { - const E = enum {}; - var e: E = undefined; - _ = &e; - switch (e) {} -} - -test "switch on empty enum with a specified tag type" { - const E = enum(u8) {}; - var e: E = undefined; - _ = &e; - switch (e) {} -} - -test "switch on empty auto numbered tagged union" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - - const U = union(enum(u8)) {}; - var u: U = undefined; - _ = &u; - switch (u) {} -} - -test "switch on empty tagged union" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - - const E = enum {}; - const U = union(E) {}; - var u: U = undefined; - _ = &u; - switch (u) {} -} - -test "empty union" { - const U = union {}; - try expect(@sizeOf(U) == 0); - try expect(@alignOf(U) == 1); -} - -test "empty extern union" { - const U = extern union {}; - try expect(@sizeOf(U) == 0); - try expect(@alignOf(U) == 1); -} - -test "empty union passed as argument" { - const U = union(enum) { - fn f(u: @This()) void { - switch (u) {} - } - }; - U.f(@as(U, undefined)); -} - -test "empty enum passed as argument" { - const E = enum { - fn f(e: @This()) void { - switch (e) {} - } - }; - E.f(@as(E, undefined)); -} diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig @@ -823,15 +823,6 @@ test "enum with one member and u1 tag type @intFromEnum" { try expect(@intFromEnum(Enum.Test) == 0); } -test "enum with comptime_int tag type" { - const Enum = enum(comptime_int) { - One = 3, - Two = 2, - Three = 1, - }; - comptime assert(Tag(Enum) == comptime_int); -} - test "enum with one member default to u0 tag type" { const E0 = enum { X }; comptime assert(Tag(E0) == u0); @@ -1274,13 +1265,6 @@ fn getLazyInitialized(param: enum(u8) { return @intFromEnum(param); } -test "Non-exhaustive enum backed by comptime_int" { - const E = enum(comptime_int) { a, b, c, _ }; - comptime var e: E = .a; - e = @as(E, @enumFromInt(378089457309184723749)); - try expect(@intFromEnum(e) == 378089457309184723749); -} - test "matching captures causes enum equivalence" { const S = struct { fn Nonexhaustive(comptime I: type) type { @@ -1347,3 +1331,41 @@ test "comptime @enumFromInt with signed arithmetic" { comptime assert(x == .bar); comptime assert(@intFromEnum(x) == 0); } + +test "switch on empty enum" { + const E = enum {}; + var e: E = undefined; + _ = &e; + switch (e) {} +} + +test "switch on empty enum with a specified tag type" { + const E = enum(u8) {}; + var e: E = undefined; + _ = &e; + switch (e) {} +} + +test "empty enum passed as argument" { + const E = enum { + fn f(e: @This()) void { + switch (e) {} + } + }; + E.f(@as(E, undefined)); +} + +test "enum int tag type uses declaration inside the enum" { + const static = struct { + const E = enum(E.IntTag) { + const IntTag = u8; + a, + b, + c, + }; + }; + try expect(@sizeOf(static.E) == @sizeOf(u8)); + const val: static.E = .b; + try expect(val == .b); + try expect(@intFromEnum(val) == 1); +} diff --git a/test/behavior/error.zig b/test/behavior/error.zig @@ -1109,3 +1109,36 @@ test "'if' ignores error via local while 'else' ignores error directly" { try S.testOne(false); try S.testOne(true); } + +test "@errorCast into own inferred error set" { + const static = struct { + fn foo(b: bool) !void { + if (b) { + return @errorCast(error.Bad); + } + } + }; + try static.foo(false); + if (static.foo(true)) { + return error.ExpectedError; + } else |err| { + try expect(err == error.Bad); + } + + const errors = @typeInfo(@typeInfo(@TypeOf(static.foo(false))).error_union.error_set).error_set.?; + comptime assert(errors.len == 1); + comptime assert(std.mem.eql(u8, errors[0].name, "Bad")); +} + +test "@errorCast into other inferred error set" { + const static = struct { + fn foo() !void { + return error.Bad; + } + }; + const Ies = @typeInfo(@TypeOf(static.foo())).error_union.error_set; + const err: Ies = @errorCast(error.Bad); + try expect(err == error.Bad); + const non_err: Ies!u32 = @errorCast(@as(error{}!u32, 123)); + try expect(try non_err == 123); +} diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig @@ -719,13 +719,6 @@ fn testVarInsideInlineLoop(args: anytype) !void { } } -test "*align(1) u16 is the same as *align(1:0:2) u16" { - comptime { - try expect(*align(1:0:2) u16 == *align(1) u16); - try expect(*align(2:0:2) u16 == *u16); - } -} - test "array concatenation of function calls" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1081,120 +1074,6 @@ test "comptime break operand passing through runtime switch converted to runtime try comptime S.doTheTest('b'); } -test "no dependency loop for alignment of self struct" { - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - - const S = struct { - fn doTheTest() !void { - var a: namespace.A = undefined; - a.d = .{ .g = &buf }; - a.d.g[3] = 42; - a.d.g[3] += 1; - try expect(a.d.g[3] == 43); - } - - var buf: [10]u8 align(@alignOf([*]u8)) = undefined; - - const namespace = struct { - const B = struct { a: A }; - const A = C(B); - }; - - pub fn C(comptime B: type) type { - return struct { - d: D(F) = .{}, - - const F = struct { b: B }; - }; - } - - pub fn D(comptime F: type) type { - return struct { - g: [*]align(@alignOf(F)) u8 = undefined, - }; - } - }; - try S.doTheTest(); -} - -test "no dependency loop for alignment of self bare union" { - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - - const S = struct { - fn doTheTest() !void { - var a: namespace.A = undefined; - a.d = .{ .g = &buf }; - a.d.g[3] = 42; - a.d.g[3] += 1; - try expect(a.d.g[3] == 43); - } - - var buf: [10]u8 align(@alignOf([*]u8)) = undefined; - - const namespace = struct { - const B = union { a: A, b: void }; - const A = C(B); - }; - - pub fn C(comptime B: type) type { - return struct { - d: D(F) = .{}, - - const F = struct { b: B }; - }; - } - - pub fn D(comptime F: type) type { - return struct { - g: [*]align(@alignOf(F)) u8 = undefined, - }; - } - }; - try S.doTheTest(); -} - -test "no dependency loop for alignment of self tagged union" { - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - - const S = struct { - fn doTheTest() !void { - var a: namespace.A = undefined; - a.d = .{ .g = &buf }; - a.d.g[3] = 42; - a.d.g[3] += 1; - try expect(a.d.g[3] == 43); - } - - var buf: [10]u8 align(@alignOf([*]u8)) = undefined; - - const namespace = struct { - const B = union(enum) { a: A, b: void }; - const A = C(B); - }; - - pub fn C(comptime B: type) type { - return struct { - d: D(F) = .{}, - - const F = struct { b: B }; - }; - } - - pub fn D(comptime F: type) type { - return struct { - g: [*]align(@alignOf(F)) u8 = undefined, - }; - } - }; - try S.doTheTest(); -} - test "equality of pointers to comptime const" { const a: i32 = undefined; comptime assert(&a == &a); diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig @@ -339,7 +339,7 @@ test "generic instantiation of tagged union with only one field" { try expect(S.foo(.{ .s = "ab" }) == 2); } -test "nested generic function" { +test "generic parameter type is function type" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const S = struct { @@ -349,10 +349,7 @@ test "nested generic function" { fn bar(a: u32) anyerror!void { try expect(a == 123); } - - fn g(_: *const fn (anytype) void) void {} }; - try expect(@typeInfo(@TypeOf(S.g)).@"fn".is_generic); try S.foo(u32, S.bar, 123); } diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig @@ -438,27 +438,6 @@ test "nested packed struct field pointers" { try expectEqual(6, ptr_p1_b.*); } -test "load pointer from packed struct" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - - const A = struct { - index: u16, - }; - const B = packed struct { - x: *A, - y: u32, - }; - var a: A = .{ .index = 123 }; - const b_list: []const B = &.{.{ .x = &a, .y = 99 }}; - for (b_list) |b| { - try expect(b.x.index == 123); - } -} - test "@intFromPtr on a packed struct field" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -601,19 +580,6 @@ test "packed struct fields modification" { try expect(@as(u16, @bitCast(Small.p)) == 0x1313); } -test "optional pointer in packed struct" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - - const T = packed struct { ptr: ?*const u8 }; - var n: u8 = 0; - const x = T{ .ptr = &n }; - try expect(x.ptr.? == &n); -} - test "nested packed struct field access test" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO packed structs larger than 64 bits @@ -854,7 +820,7 @@ test "packed struct passed to callconv(.c) function" { if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { - const Packed = packed struct { + const Packed = packed struct(u64) { a: u16, b: bool = true, c: bool = true, @@ -1042,48 +1008,6 @@ test "packed struct acts as a namespace" { try expect(foo == .fizz); } -test "pointer loaded correctly from packed struct" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - - if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // crashes MSVC - - const RAM = struct { - data: [0xFFFF + 1]u8, - fn new() !@This() { - return .{ .data = [_]u8{0} ** 0x10000 }; - } - fn get(self: *@This(), addr: u16) u8 { - return self.data[addr]; - } - }; - - const CPU = packed struct { - interrupts: bool, - ram: *RAM, - fn new(ram: *RAM) !@This() { - return .{ - .ram = ram, - .interrupts = false, - }; - } - fn tick(self: *@This()) !void { - const queued_interrupts = self.ram.get(0xFFFF) & self.ram.get(0xFF0F); - if (self.interrupts and queued_interrupts != 0) { - self.interrupts = false; - } - } - }; - - var ram = try RAM.new(); - var cpu = try CPU.new(&ram); - try cpu.tick(); - try std.testing.expect(cpu.interrupts == false); -} - test "assignment to non-byte-aligned field in packed struct" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -1227,13 +1151,6 @@ test "2-byte packed struct argument in C calling convention" { } } -test "packed struct contains optional pointer" { - const foo: packed struct { - a: ?*@This() = null, - } = .{}; - try expect(foo.a == null); -} - test "packed struct equality" { const Foo = packed struct { a: u4, @@ -1297,21 +1214,6 @@ test "assign packed struct initialized with RLS to packed struct literal field" try expect(outer.x == x); } -test "byte-aligned packed relocation" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - - const S = struct { - var global: u8 align(2) = 0; - var packed_value: packed struct { x: u8, y: *align(2) u8 } = .{ .x = 111, .y = &global }; - }; - try expect(S.packed_value.x == 111); - try expect(S.packed_value.y == &S.global); -} - test "packed struct store of comparison result" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; diff --git a/test/behavior/packed-union.zig b/test/behavior/packed-union.zig @@ -1,6 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); const assert = std.debug.assert; +const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; test "flags in packed union" { @@ -178,14 +179,23 @@ test "assigning to non-active field at comptime" { } } -test "comptime packed union of pointers" { - const U = packed union { - a: *const u32, - b: *const [1]u32, - }; +test "packed union with explicit backing integer" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const x: u32 = 123; - const u: U = .{ .a = &x }; + const U = packed union(i32) { + raw: i32, + unsigned_halves: packed struct { low: u16, high: u16 }, - comptime assert(u.b[0] == 123); + fn check(val: @This()) !void { + try expect(@as(i32, @bitCast(val)) == -2); + try expect(@as(u32, @bitCast(val)) == 0xFFFFFFFE); + try expect(val.raw == -2); + try expect(val.unsigned_halves.low == 0xFFFE); + try expect(val.unsigned_halves.high == 0xFFFF); + } + }; + try U.check(.{ .raw = -2 }); + try comptime U.check(.{ .raw = -2 }); } diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig @@ -11,13 +11,6 @@ test "@sizeOf and @TypeOf" { const x: u16 = 13; const z: @TypeOf(x) = 19; -test "@sizeOf on compile-time types" { - try expect(@sizeOf(comptime_int) == 0); - try expect(@sizeOf(comptime_float) == 0); - try expect(@sizeOf(@TypeOf(.hi)) == 0); - try expect(@sizeOf(@TypeOf(type)) == 0); -} - test "@TypeOf() with multiple arguments" { { var var_1: u32 = undefined; @@ -127,21 +120,6 @@ test "@bitOffsetOf" { try expect(@offsetOf(A, "g") * 8 == @bitOffsetOf(A, "g")); } -test "@sizeOf(T) == 0 doesn't force resolving struct size" { - const S = struct { - const Foo = struct { - y: if (@sizeOf(Foo) == 0) u64 else u32, - }; - const Bar = struct { - x: i32, - y: if (0 == @sizeOf(Bar)) u64 else u32, - }; - }; - - try expect(@sizeOf(S.Foo) == 4); - try expect(@sizeOf(S.Bar) == 8); -} - test "@TypeOf() has no runtime side effects" { const S = struct { fn foo(comptime T: type, ptr: *T) T { @@ -265,10 +243,6 @@ test "lazy size cast to float" { } } -test "bitSizeOf comptime_int" { - try expect(@bitSizeOf(comptime_int) == 0); -} - test "runtime instructions inside typeof in comptime only scope" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -336,20 +310,6 @@ test "peer type resolution with @TypeOf doesn't trigger dependency loop check" { try std.testing.expect(t.next == null); } -test "@sizeOf reified union zero-size payload fields" { - comptime { - try std.testing.expect(0 == @sizeOf(@Union(.auto, null, &.{}, &.{}, &.{}))); - try std.testing.expect(0 == @sizeOf(@Union(.auto, null, &.{"a"}, &.{void}, &.{.{}}))); - if (builtin.mode == .Debug or builtin.mode == .ReleaseSafe) { - try std.testing.expect(1 == @sizeOf(@Union(.auto, null, &.{ "a", "b" }, &.{ void, void }, &.{ .{}, .{} }))); - try std.testing.expect(1 == @sizeOf(@Union(.auto, null, &.{ "a", "b", "c" }, &.{ void, void, void }, &.{ .{}, .{}, .{} }))); - } else { - try std.testing.expect(0 == @sizeOf(@Union(.auto, null, &.{ "a", "b" }, &.{ void, void }, &.{ .{}, .{} }))); - try std.testing.expect(0 == @sizeOf(@Union(.auto, null, &.{ "a", "b", "c" }, &.{ void, void, void }, &.{ .{}, .{}, .{} }))); - } - } -} - const FILE = extern struct { dummy_field: u8, }; @@ -391,7 +351,7 @@ test "Extern function calls in @TypeOf" { extern fn s_do_thing([*c]const @This(), b: c_int) c_short; }; - const E = struct { + const E = extern struct { export fn s_do_thing(a: [*c]const @This(), b: c_int) c_short { _ = a; _ = b; diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig @@ -160,7 +160,7 @@ test "slice of type" { test "pass a slice of types to a function" { const S = struct { - fn checkTypesSlice(types_slice: []const type) !void { + fn checkTypesSlice(comptime types_slice: []const type) !void { try expect(types_slice.len == 2); try expect(types_slice[0] == anyerror); try expect(types_slice[1] == bool); diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig @@ -2177,7 +2177,7 @@ test "avoid unused field function body compile error" { test "pass a pointer to a comptime-only struct field to a function" { const S = struct { - fn checkField(field_ptr: *const type) !void { + fn checkField(comptime field_ptr: *const type) !void { try expect(field_ptr.* == u42); } }; @@ -2233,3 +2233,75 @@ test "overaligned extern struct fields" { try expect(std.mem.isAligned(@intFromPtr(&e.c), @alignOf(u32))); try expect(std.mem.isAligned(@intFromPtr(&e.d), @alignOf(B))); } + +test "runtime-known slice of comptime-only struct" { + const Mixed = struct { index: u32, T: type }; + + const static = struct { + fn doTheTest(index_offset: usize, s: []const Mixed) !void { + for (s, index_offset..) |*mixed, index| { + try expect(mixed.index == index); + } + } + }; + + try static.doTheTest(10, &.{ + .{ .index = 10, .T = u8 }, + .{ .index = 11, .T = noreturn }, + .{ .index = 12, .T = *opaque {} }, + .{ .index = 13, .T = undefined }, + .{ .index = 14, .T = @TypeOf(undefined) }, + .{ .index = 15, .T = Mixed }, + }); +} + +test "struct contains aligned pointer to itself through type decl" { + const Slab = struct { + const Ptr = *align(64) const @This(); + next: Ptr, + }; + // We intentionally use `Slab.Ptr` before `Slab`. + var ptr: Slab.Ptr = undefined; + var slab: Slab align(64) = undefined; + ptr = &slab; + slab.next = ptr; + + try expect(ptr == &slab); + try expect(slab.next == &slab); + try expect(slab.next.next == &slab); + try expect(slab.next.next.next == &slab); +} + +test "struct contains underaligned field with overaligned pointer to itself" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + const S = struct { + ptr: *align(8) @This() align(1), + }; + var val: S align(8) = undefined; + val.ptr = &val; + try expect(val.ptr == &val); + try expect(val.ptr.ptr == &val); + try expect(val.ptr.ptr.ptr == &val); +} + +test "struct contains pointer to function accepting that struct" { + const S = struct { + const FnPtr = ?*const fn (@This()) void; + fn_ptr: FnPtr, + }; + const dummy_fn_ptr: S.FnPtr = @ptrFromInt(0x100000); + const dummy_s: S = .{ .fn_ptr = dummy_fn_ptr }; + try expect(dummy_s.fn_ptr == dummy_fn_ptr); + try expect(@TypeOf(dummy_s.fn_ptr.?) == *const fn (S) void); +} + +test "struct queries typeinfo of struct containing pointer back to first struct" { + const static = struct { + const A = struct { b: *B }; + const B = struct { a: T: { + _ = @typeInfo(A); + break :T u32; + } }; + }; + _ = @as(static.A, undefined); +} diff --git a/test/behavior/struct_contains_slice_of_itself.zig b/test/behavior/struct_contains_slice_of_itself.zig @@ -8,7 +8,7 @@ const Node = struct { const NodeAligned = struct { payload: i32, - children: []align(@alignOf(NodeAligned)) NodeAligned, + children: []align(1) NodeAligned, }; test "struct contains slice of itself" { diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig @@ -645,7 +645,7 @@ test "switch prong pointer capture alignment" { } switch (u) { - .a, .c => |*p| comptime assert(@TypeOf(p) == *const u8), + .a, .c => |*p| comptime assert(@TypeOf(p) == *align(1) const u8), .b => |*p| { _ = p; return error.TestFailed; @@ -1141,24 +1141,23 @@ test "decl literals as switch cases" { try comptime E.doTheTest(.foo); } -// TODO audit after #15909 and/or #19855 are decided/implemented +// TODO audit after #15909 and/or #19855 are decided/implemented. +// When we do that, consider adding an 'error{}' case if possible. test "switch with uninstantiable union fields" { const U = union(enum) { ok: void, a: noreturn, b: noreturn, - c: error{}, fn doTheTest(u: @This()) void { switch (u) { .ok => {}, .a => comptime unreachable, .b => comptime unreachable, - .c => comptime unreachable, } switch (u) { .ok => {}, - .a, .b, .c => comptime unreachable, + .a, .b => comptime unreachable, } switch (u) { .ok => {}, @@ -1166,7 +1165,7 @@ test "switch with uninstantiable union fields" { } switch (u) { .a => comptime unreachable, - .ok, .b, .c => {}, + .ok, .b => {}, } } }; diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig @@ -592,3 +592,14 @@ test "array of tuples that end with a zero-bit field followed by padding" { try expect(S.foo[1][1] == 4); try expect(S.foo[1][2] == {}); } + +test "call function at comptime through container-level const tuple" { + const static = struct { + const MyTuple = struct { (fn () u32) }; + const val: MyTuple = .{foo}; + fn foo() u32 { + return 1234; + } + }; + comptime assert(static.val[0]() == 1234); +} diff --git a/test/behavior/tuple_declarations.zig b/test/behavior/tuple_declarations.zig @@ -22,13 +22,13 @@ test "tuple declaration type info" { try expect(info.fields[0].type == u32); try expect(info.fields[0].defaultValue() == 1); try expect(info.fields[0].is_comptime); - try expect(info.fields[0].alignment == @alignOf(u32)); + try expect(info.fields[0].alignment == null); try expectEqualStrings(info.fields[1].name, "1"); try expect(info.fields[1].type == []const u8); try expect(info.fields[1].defaultValue() == null); try expect(!info.fields[1].is_comptime); - try expect(info.fields[1].alignment == @alignOf([]const u8)); + try expect(info.fields[1].alignment == null); } } diff --git a/test/behavior/type.zig b/test/behavior/type.zig @@ -278,13 +278,13 @@ test "Type.Union from regular enum" { test "Type.Union from empty regular enum" { const E = enum {}; const U = @Union(.auto, E, &.{}, &.{}, &.{}); - try testing.expectEqual(@sizeOf(U), 0); + try testing.expectEqual(@typeInfo(U).@"union".fields.len, 0); } test "Type.Union from empty Type.Enum" { const E = @Enum(u0, .exhaustive, &.{}, &.{}); const U = @Union(.auto, E, &.{}, &.{}, &.{}); - try testing.expectEqual(@sizeOf(U), 0); + try testing.expectEqual(@typeInfo(U).@"union".fields.len, 0); } test "Type.Fn" { diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig @@ -82,7 +82,7 @@ fn testPointer() !void { try expect(u32_ptr_info.pointer.size == .one); try expect(u32_ptr_info.pointer.is_const == false); try expect(u32_ptr_info.pointer.is_volatile == false); - try expect(u32_ptr_info.pointer.alignment == @alignOf(u32)); + try expect(u32_ptr_info.pointer.alignment == null); try expect(u32_ptr_info.pointer.child == u32); try expect(u32_ptr_info.pointer.sentinel() == null); } @@ -99,7 +99,7 @@ fn testUnknownLenPtr() !void { try expect(u32_ptr_info.pointer.is_const == true); try expect(u32_ptr_info.pointer.is_volatile == true); try expect(u32_ptr_info.pointer.sentinel() == null); - try expect(u32_ptr_info.pointer.alignment == @alignOf(f64)); + try expect(u32_ptr_info.pointer.alignment == null); try expect(u32_ptr_info.pointer.child == f64); } @@ -130,7 +130,7 @@ fn testSlice() !void { try expect(u32_slice_info.pointer.size == .slice); try expect(u32_slice_info.pointer.is_const == false); try expect(u32_slice_info.pointer.is_volatile == false); - try expect(u32_slice_info.pointer.alignment == 4); + try expect(u32_slice_info.pointer.alignment == null); try expect(u32_slice_info.pointer.child == u32); } @@ -266,9 +266,9 @@ fn testUnion() !void { try expect(notag_union_info.@"union".tag_type == null); try expect(notag_union_info.@"union".layout == .auto); try expect(notag_union_info.@"union".fields.len == 2); - try expect(notag_union_info.@"union".fields[0].alignment == @alignOf(void)); + try expect(notag_union_info.@"union".fields[0].alignment == null); try expect(notag_union_info.@"union".fields[1].type == u32); - try expect(notag_union_info.@"union".fields[1].alignment == @alignOf(u32)); + try expect(notag_union_info.@"union".fields[1].alignment == null); const TestExternUnion = extern union { foo: *anyopaque, @@ -292,7 +292,7 @@ fn testStruct() !void { const unpacked_struct_info = @typeInfo(TestStruct); try expect(unpacked_struct_info.@"struct".is_tuple == false); try expect(unpacked_struct_info.@"struct".backing_integer == null); - try expect(unpacked_struct_info.@"struct".fields[0].alignment == @alignOf(u32)); + try expect(unpacked_struct_info.@"struct".fields[0].alignment == null); try expect(unpacked_struct_info.@"struct".fields[0].defaultValue().? == 4); try expect(mem.eql(u8, "foobar", unpacked_struct_info.@"struct".fields[1].defaultValue().?)); } @@ -314,11 +314,11 @@ fn testPackedStruct() !void { try expect(struct_info.@"struct".layout == .@"packed"); try expect(struct_info.@"struct".backing_integer == u128); try expect(struct_info.@"struct".fields.len == 4); - try expect(struct_info.@"struct".fields[0].alignment == 0); + try expect(struct_info.@"struct".fields[0].alignment == null); try expect(struct_info.@"struct".fields[2].type == f32); try expect(struct_info.@"struct".fields[2].defaultValue() == null); try expect(struct_info.@"struct".fields[3].defaultValue().? == 4); - try expect(struct_info.@"struct".fields[3].alignment == 0); + try expect(struct_info.@"struct".fields[3].alignment == null); try expect(struct_info.@"struct".decls.len == 1); } diff --git a/test/behavior/union.zig b/test/behavior/union.zig @@ -148,6 +148,7 @@ const err = @as(anyerror!Agg, Agg{ const array = [_]Value{ v1, v2, v1, v2 }; test "unions embedded in aggregate types" { + if (builtin.zig_backend == .stage2_c and builtin.target.abi == .msvc) return error.SkipZigTest; switch (array[1]) { Value.Array => |arr| try expect(arr[4] == 3), else => unreachable, @@ -217,26 +218,6 @@ test "union with specified enum tag" { try comptime doTest(); } -test "packed union generates correctly aligned type" { - // This test will be removed after the following accepted proposal is implemented: - // https://github.com/ziglang/zig/issues/24657 - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - - const U = packed union { - f1: *const fn () error{TestUnexpectedResult}!void, - f2: usize, - }; - var foo = [_]U{ - U{ .f1 = doTest }, - U{ .f2 = 0 }, - }; - try foo[0].f1(); -} - fn doTest() error{TestUnexpectedResult}!void { try expect((try bar(Payload{ .A = 1234 })) == -10); } @@ -359,12 +340,12 @@ test "simple union(enum(u32))" { try expect(@intFromEnum(@as(Tag(MultipleChoice), x)) == 60); } -const PackedPtrOrInt = packed union { - ptr: *u8, - int: usize, -}; test "packed union size" { - comptime assert(@sizeOf(PackedPtrOrInt) == @sizeOf(usize)); + const U = packed union { + signed: isize, + unsigned: usize, + }; + comptime assert(@sizeOf(U) == @sizeOf(usize)); } const ZeroBits = union { @@ -703,25 +684,23 @@ test "union with only 1 field casted to its enum type which has enum value speci if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const Literal = union(enum) { - Number: f64, - Bool: bool, + number: f64, + bool: bool, }; - const ExprTag = enum(comptime_int) { - Literal = 33, - }; + const ExprTag = enum(u32) { literal = 33 }; + const Expr = union(ExprTag) { literal: Literal }; - const Expr = union(ExprTag) { - Literal: Literal, - }; + comptime assert(Tag(ExprTag) == u32); - var e = Expr{ .Literal = Literal{ .Bool = true } }; - _ = &e; - comptime assert(Tag(ExprTag) == comptime_int); - const t = comptime @as(ExprTag, e); - try expect(t == Expr.Literal); - try expect(@intFromEnum(t) == 33); + var e: Expr = undefined; + e = .{ .literal = .{ .bool = true } }; + + const t: ExprTag = e; + comptime assert(t == Expr.literal); comptime assert(@intFromEnum(t) == 33); + try expect(t == Expr.literal); + try expect(@intFromEnum(t) == 33); } test "@intFromEnum works on unions" { @@ -893,15 +872,6 @@ test "union no tag with struct member" { u.foo(); } -test "union with comptime_int tag" { - const Union = union(enum(comptime_int)) { - X: u32, - Y: u16, - Z: u8, - }; - comptime assert(Tag(Tag(Union)) == comptime_int); -} - test "extern union doesn't trigger field check at comptime" { const U = extern union { x: u32, @@ -1031,7 +1001,7 @@ test "containers with single-field enums" { try comptime S.doTheTest(); } -test "@unionInit on union with tag but no fields" { +test "@unionInit on union with u8 tag but no fields" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1047,10 +1017,6 @@ test "@unionInit on union with tag but no fields" { } }; - comptime { - assert(@sizeOf(Data) == 1); - } - fn doTheTest() !void { var data: Data = .{ .no_op = {} }; _ = &data; @@ -2057,6 +2023,7 @@ test "runtime union init, most-aligned field != largest" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.target.abi == .msvc) return error.SkipZigTest; const U = union(enum) { x: u128, diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig @@ -718,7 +718,7 @@ export fn zig_med_struct_ints(s: MedStructInts) void { expect(s.z == 3) catch @panic("test failure"); } -const SmallPackedStruct = packed struct { +const SmallPackedStruct = packed struct(u8) { a: u2, b: u2, c: u2, @@ -744,7 +744,7 @@ test "C ABI small packed struct" { try expect(s2.d == 3); } -const BigPackedStruct = packed struct { +const BigPackedStruct = packed struct(u128) { a: u64, b: u64, }; diff --git a/test/cases/compile_errors/@import_zon_bad_type.zig b/test/cases/compile_errors/@import_zon_bad_type.zig @@ -116,13 +116,13 @@ export fn testMutablePointer() void { // tmp.zig:85:26: note: ZON does not allow nested optionals // tmp.zig:90:29: error: type '*i32' is not available in ZON // tmp.zig:90:29: note: ZON does not allow mutable pointers -// neg_inf.zon:1:1: error: expected type '@EnumLiteral()' -// tmp.zig:37:38: note: imported here // neg_inf.zon:1:1: error: expected type '?u8' // tmp.zig:57:28: note: imported here +// neg_inf.zon:1:1: error: expected type '@EnumLiteral()' +// tmp.zig:37:38: note: imported here // neg_inf.zon:1:1: error: expected type 'tmp.E' // tmp.zig:63:26: note: imported here -// neg_inf.zon:1:1: error: expected type 'tmp.U' -// tmp.zig:69:26: note: imported here // neg_inf.zon:1:1: error: expected type 'tmp.EU' // tmp.zig:75:27: note: imported here +// neg_inf.zon:1:1: error: expected type 'tmp.U' +// tmp.zig:69:26: note: imported here diff --git a/test/cases/compile_errors/@import_zon_opt_in_err.zig b/test/cases/compile_errors/@import_zon_opt_in_err.zig @@ -58,25 +58,25 @@ export fn testVector() void { // error // imports=zon/vec2.zon // -// vec2.zon:1:2: error: expected type '?f32' -// tmp.zig:2:29: note: imported here // vec2.zon:1:2: error: expected type '*const ?f32' // tmp.zig:7:36: note: imported here // vec2.zon:1:2: error: expected type '?*const f32' // tmp.zig:12:36: note: imported here +// vec2.zon:1:2: error: expected type '?@EnumLiteral()' +// tmp.zig:33:39: note: imported here +// vec2.zon:1:2: error: expected type '?@Vector(3, f32)' +// tmp.zig:54:41: note: imported here +// vec2.zon:1:2: error: expected type '?[1]u8' +// tmp.zig:38:31: note: imported here +// vec2.zon:1:2: error: expected type '?[]const u8' +// tmp.zig:49:36: note: imported here // vec2.zon:1:2: error: expected type '?bool' // tmp.zig:17:30: note: imported here +// vec2.zon:1:2: error: expected type '?f32' +// tmp.zig:2:29: note: imported here // vec2.zon:1:2: error: expected type '?i32' // tmp.zig:22:29: note: imported here // vec2.zon:1:2: error: expected type '?tmp.Enum' // tmp.zig:28:30: note: imported here -// vec2.zon:1:2: error: expected type '?@EnumLiteral()' -// tmp.zig:33:39: note: imported here -// vec2.zon:1:2: error: expected type '?[1]u8' -// tmp.zig:38:31: note: imported here // vec2.zon:1:2: error: expected type '?tmp.Union' // tmp.zig:44:31: note: imported here -// vec2.zon:1:2: error: expected type '?[]const u8' -// tmp.zig:49:36: note: imported here -// vec2.zon:1:2: error: expected type '?@Vector(3, f32)' -// tmp.zig:54:41: note: imported here diff --git a/test/cases/compile_errors/@import_zon_opt_in_err_struct.zig b/test/cases/compile_errors/@import_zon_opt_in_err_struct.zig @@ -13,7 +13,7 @@ export fn testTuple() void { // error // imports=zon/nan.zon // -//nan.zon:1:1: error: expected type '?tmp.Struct' -//tmp.zig:3:32: note: imported here -//nan.zon:1:1: error: expected type '?struct { bool }' -//tmp.zig:9:31: note: imported here +// nan.zon:1:1: error: expected type '?struct { bool }' +// tmp.zig:9:31: note: imported here +// nan.zon:1:1: error: expected type '?tmp.Struct' +// tmp.zig:3:32: note: imported here diff --git a/test/cases/compile_errors/@intFromPtr_with_bad_type.zig b/test/cases/compile_errors/@intFromPtr_with_bad_type.zig @@ -1,9 +0,0 @@ -const x = 42; -const y = @intFromPtr(&x); -pub export fn entry() void { - _ = y; -} - -// error -// -// :2:23: error: comptime-only type 'comptime_int' has no pointer address diff --git a/test/cases/compile_errors/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig b/test/cases/compile_errors/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig @@ -1,12 +0,0 @@ -const Foo = struct { a: u32 }; -export fn a() void { - const T = [*c]Foo; - const t: T = undefined; - _ = t; -} - -// error -// -// :3:19: error: C pointers cannot point to non-C-ABI-compatible type 'tmp.Foo' -// :3:19: note: only extern structs and ABI sized packed structs are extern compatible -// :1:13: note: struct declared here diff --git a/test/cases/compile_errors/aggregate_too_large.zig b/test/cases/compile_errors/aggregate_too_large.zig @@ -12,16 +12,14 @@ const U = union { b: [1 << 32]u8, }; -const V = union { - a: u32, - b: T, -}; - comptime { - _ = S; - _ = T; - _ = U; - _ = V; + _ = @as(S, undefined); +} +comptime { + _ = @as(T, undefined); +} +comptime { + _ = @as(U, undefined); } // error diff --git a/test/cases/compile_errors/alignOf_bad_type.zig b/test/cases/compile_errors/alignOf_bad_type.zig @@ -1,7 +1,13 @@ -export fn entry() usize { +export fn entry0() usize { return @alignOf(noreturn); } +const S = struct { a: u32, b: noreturn }; +export fn entry1() usize { + return @alignOf(S); +} // error // -// :2:21: error: no align available for type 'noreturn' +// :2:21: error: no align available for uninstantiable type 'noreturn' +// :6:21: error: no align available for uninstantiable type 'tmp.S' +// :4:11: note: struct declared here diff --git a/test/cases/compile_errors/align_zero.zig b/test/cases/compile_errors/align_zero.zig @@ -30,11 +30,11 @@ export fn g() void { } export fn h() void { - _ = struct { field: i32 align(0) }; + _ = @as(struct { field: i32 align(0) }, undefined); } export fn i() void { - _ = union { field: i32 align(0) }; + _ = @as(union { field: i32 align(0) }, undefined); } export fn j() void { @@ -54,7 +54,7 @@ export fn k() void { // :20:30: error: alignment must be >= 1 // :25:16: error: alignment must be >= 1 // :29:17: error: alignment must be >= 1 -// :33:35: error: alignment must be >= 1 -// :37:34: error: alignment must be >= 1 +// :33:39: error: alignment must be >= 1 +// :37:38: error: alignment must be >= 1 // :41:51: error: alignment must be >= 1 // :45:25: error: alignment must be >= 1 diff --git a/test/cases/compile_errors/assign_inline_fn_to_non-comptime_var.zig b/test/cases/compile_errors/assign_inline_fn_to_non-comptime_var.zig @@ -1,10 +0,0 @@ -export fn entry() void { - var a = &b; - _ = &a; -} -inline fn b() void {} - -// error -// -// :2:9: error: variable of type '*const fn () callconv(.@"inline") void' must be const or comptime -// :2:9: note: function has inline calling convention diff --git a/test/cases/compile_errors/bit_ptr_non_packed.zig b/test/cases/compile_errors/bit_ptr_non_packed.zig @@ -16,7 +16,11 @@ export fn entry3() void { // error // // :3:23: error: bit-pointer cannot refer to value of type 'tmp.entry1.S' -// :3:23: note: only packed structs layout are allowed in packed types +// :3:23: note: non-packed structs do not have a bit-packed representation +// :2:22: note: struct declared here // :8:36: error: bit-pointer cannot refer to value of type 'tmp.entry2.S' -// :8:36: note: only packed structs layout are allowed in packed types +// :8:36: note: non-packed structs do not have a bit-packed representation +// :7:15: note: struct declared here // :13:23: error: bit-pointer cannot refer to value of type 'tmp.entry3.E' +// :12:15: note: integer tag type of enum is inferred +// :12:15: note: consider explicitly specifying the integer tag type diff --git a/test/cases/compile_errors/bitsize_of_packed_struct_checks_backing_int_ty.zig b/test/cases/compile_errors/bitsize_of_packed_struct_checks_backing_int_ty.zig @@ -8,4 +8,6 @@ pub export fn entry() void { // error // -// :1:27: error: backing integer type 'u32' has bit size 32 but the struct fields have a total bit size of 1 +// :1:20: error: backing integer bit width does not match total bit width of fields +// :1:27: note: backing integer 'u32' has bit width '32' +// :1:20: note: struct fields have total bit width '1' diff --git a/test/cases/compile_errors/c_pointer_to_void.zig b/test/cases/compile_errors/c_pointer_to_void.zig @@ -1,9 +0,0 @@ -export fn entry() void { - const a: [*c]void = undefined; - _ = a; -} - -// error -// -// :2:18: error: C pointers cannot point to non-C-ABI-compatible type 'void' -// :2:18: note: 'void' is a zero bit type; for C 'void' use 'anyopaque' diff --git a/test/cases/compile_errors/call_runtime_known_inline_fn_ptr.zig b/test/cases/compile_errors/call_runtime_known_inline_fn_ptr.zig @@ -0,0 +1,11 @@ +export fn entry() void { + var a = &b; + a = a; + a(); +} +inline fn b() void {} + +// error +// +// :4:5: error: unable to resolve comptime value +// :4:5: note: function being called inline must be comptime-known diff --git a/test/cases/compile_errors/coerce_int_to_float.zig b/test/cases/compile_errors/coerce_int_to_float.zig @@ -40,13 +40,13 @@ export fn entry() void { // error // -// :6:20: error: expected type 'f16', found 'u12' +// :6:20: error: expected type 'f128', found 'i115' +// :6:20: error: expected type 'f128', found 'u114' // :6:20: error: expected type 'f16', found 'i13' -// :6:20: error: expected type 'f32', found 'u25' +// :6:20: error: expected type 'f16', found 'u12' // :6:20: error: expected type 'f32', found 'i26' -// :6:20: error: expected type 'f64', found 'u54' +// :6:20: error: expected type 'f32', found 'u25' // :6:20: error: expected type 'f64', found 'i55' -// :6:20: error: expected type 'f80', found 'u65' +// :6:20: error: expected type 'f64', found 'u54' // :6:20: error: expected type 'f80', found 'i66' -// :6:20: error: expected type 'f128', found 'u114' -// :6:20: error: expected type 'f128', found 'i115' +// :6:20: error: expected type 'f80', found 'u65' diff --git a/test/cases/compile_errors/comptime_var_referenced_by_type.zig b/test/cases/compile_errors/comptime_var_referenced_by_type.zig @@ -21,6 +21,6 @@ comptime { // error // // :7:16: error: captured value contains reference to comptime var -// :7:16: note: 'wrapper' points to '@as(*const tmp.Wrapper, @ptrCast(&v0)).*', where +// :7:16: note: 'wrapper' points to 'v0', where // :16:5: note: 'v0.ptr' points to comptime var declared here // :17:29: note: called at comptime here diff --git a/test/cases/compile_errors/direct_struct_loop.zig b/test/cases/compile_errors/direct_struct_loop.zig @@ -7,4 +7,4 @@ export fn entry() usize { // error // -// :1:11: error: struct 'tmp.A' depends on itself +// :2:8: error: type 'tmp.A' depends on itself for field declared here diff --git a/test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig b/test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig @@ -26,9 +26,11 @@ export fn d() void { // error // -// :3:8: error: opaque types have unknown size and therefore cannot be directly embedded in structs +// :3:8: error: cannot directly embed opaque type 'tmp.O' in struct +// :3:8: note: opaque types have unknown size // :1:11: note: opaque declared here -// :7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions +// :7:10: error: cannot directly embed opaque type 'tmp.O' in union +// :7:10: note: opaque types have unknown size // :1:11: note: opaque declared here // :18:24: error: cannot cast to opaque type 'tmp.O' // :1:11: note: opaque declared here diff --git a/test/cases/compile_errors/empty_extern_union.zig b/test/cases/compile_errors/empty_extern_union.zig @@ -0,0 +1,8 @@ +export fn foo() void { + const U = extern union {}; + _ = @as(U, undefined); +} + +// error +// +// :2:22: error: extern union has no fields diff --git a/test/cases/compile_errors/empty_packed_union.zig b/test/cases/compile_errors/empty_packed_union.zig @@ -0,0 +1,8 @@ +export fn foo() void { + const U = packed union {}; + _ = @as(U, undefined); +} + +// error +// +// :2:22: error: packed union has no fields diff --git a/test/cases/compile_errors/enum_backed_by_comptime_int.zig b/test/cases/compile_errors/enum_backed_by_comptime_int.zig @@ -0,0 +1,8 @@ +const E = enum(comptime_int) { a }; +comptime { + _ = E.a; +} + +// error +// +// :1:16: error: expected integer tag type, found 'comptime_int' diff --git a/test/cases/compile_errors/enum_backed_by_comptime_int_must_be_casted_from_comptime_value.zig b/test/cases/compile_errors/enum_backed_by_comptime_int_must_be_casted_from_comptime_value.zig @@ -1,12 +0,0 @@ -export fn entry() void { - const Tag = enum(comptime_int) { a, b }; - - var v: u32 = 0; - _ = &v; - _ = @as(Tag, @enumFromInt(v)); -} - -// error -// -// :6:31: error: unable to resolve comptime value -// :6:31: note: value casted to enum with 'comptime_int' tag type must be comptime-known diff --git a/test/cases/compile_errors/enum_backed_by_comptime_int_must_be_comptime.zig b/test/cases/compile_errors/enum_backed_by_comptime_int_must_be_comptime.zig @@ -1,9 +0,0 @@ -pub export fn entry() void { - const E = enum(comptime_int) { a, b, c, _ }; - var e: E = .a; - _ = &e; -} - -// error -// -// :3:12: error: variable of type 'tmp.entry.E' must be const or comptime diff --git a/test/cases/compile_errors/enum_field_value_references_enum.zig b/test/cases/compile_errors/enum_field_value_references_enum.zig @@ -1,15 +1,11 @@ pub const Foo = enum(c_int) { - A = Foo.B, - C = D, - - pub const B = 0; + a = 10, + b = @intFromEnum(Foo.a) - 1, }; export fn entry() void { - const s: Foo = Foo.E; - _ = s; + _ = @as(Foo, .a); } -const D = 1; // error // -// :1:5: error: dependency loop detected +// :3:25: error: type 'tmp.Foo' depends on itself for field usage here diff --git a/test/cases/compile_errors/enum_field_value_references_nonexistent_circular.zig b/test/cases/compile_errors/enum_field_value_references_nonexistent_circular.zig @@ -10,4 +10,4 @@ const D = 1; // error // -// :1:5: error: dependency loop detected +// :2:12: error: type 'tmp.Foo' depends on itself for field usage here diff --git a/test/cases/compile_errors/enum_uses_own_typeinfo.zig b/test/cases/compile_errors/enum_uses_own_typeinfo.zig @@ -0,0 +1,15 @@ +const E = enum(u9) { + const a_val: @typeInfo(E).@"enum".tag_type = 0; + a = a_val, +}; +comptime { + _ = E.a; +} + +// error +// +// error: dependency loop with length 3 +// :3:9: note: type 'tmp.E' uses value of declaration 'tmp.E.a_val' here +// :2:50: note: value of declaration 'tmp.E.a_val' uses type of declaration 'tmp.E.a_val' here +// :2:18: note: type of declaration 'tmp.E.a_val' depends on type 'tmp.E' for type information query here +// note: eliminate any one of these dependencies to break the loop diff --git a/test/cases/compile_errors/enum_value_already_taken.zig b/test/cases/compile_errors/enum_value_already_taken.zig @@ -12,5 +12,5 @@ export fn entry() void { // error // -// :6:9: error: enum tag value 60 already taken -// :4:9: note: other occurrence here +// :6:9: error: enum tag value '60' for field 'E' already taken +// :4:9: note: previous occurrence in field 'C' diff --git a/test/cases/compile_errors/error_set_membership.zig b/test/cases/compile_errors/error_set_membership.zig @@ -26,5 +26,6 @@ pub fn main() Error!void { // error // target=x86_64-linux // -// :23:29: error: expected type 'error{InvalidCharacter}', found '@typeInfo(@typeInfo(@TypeOf(tmp.fooey)).@"fn".return_type.?).error_union.error_set' +// :23:29: error: expected type 'error{InvalidCharacter}!void', found '@typeInfo(@typeInfo(@TypeOf(tmp.fooey)).@"fn".return_type.?).error_union.error_set' // :23:29: note: 'error.InvalidDirection' not a member of destination error set +// :22:20: note: function return type declared here diff --git a/test/cases/compile_errors/exported_enum_without_explicit_integer_tag_type.zig b/test/cases/compile_errors/exported_enum_without_explicit_integer_tag_type.zig @@ -11,6 +11,6 @@ comptime { // // :3:5: error: unable to export type 'type' // :7:5: error: unable to export type 'tmp.E' -// :7:5: note: enum tag type 'u1' is not extern compatible -// :7:5: note: only integers with 0, 8, 16, 32, 64 and 128 bits are extern compatible +// :1:11: note: integer tag type of enum is inferred +// :1:11: note: consider explicitly specifying the integer tag type // :1:11: note: enum declared here diff --git a/test/cases/compile_errors/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig b/test/cases/compile_errors/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig @@ -1,45 +0,0 @@ -// zig fmt: off -pub const E = enum { -@"0",@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10",@"11",@"12", -@"13",@"14",@"15",@"16",@"17",@"18",@"19",@"20",@"21",@"22",@"23", -@"24",@"25",@"26",@"27",@"28",@"29",@"30",@"31",@"32",@"33",@"34", -@"35",@"36",@"37",@"38",@"39",@"40",@"41",@"42",@"43",@"44",@"45", -@"46",@"47",@"48",@"49",@"50",@"51",@"52",@"53",@"54",@"55",@"56", -@"57",@"58",@"59",@"60",@"61",@"62",@"63",@"64",@"65",@"66",@"67", -@"68",@"69",@"70",@"71",@"72",@"73",@"74",@"75",@"76",@"77",@"78", -@"79",@"80",@"81",@"82",@"83",@"84",@"85",@"86",@"87",@"88",@"89", -@"90",@"91",@"92",@"93",@"94",@"95",@"96",@"97",@"98",@"99",@"100", -@"101",@"102",@"103",@"104",@"105",@"106",@"107",@"108",@"109", -@"110",@"111",@"112",@"113",@"114",@"115",@"116",@"117",@"118", -@"119",@"120",@"121",@"122",@"123",@"124",@"125",@"126",@"127", -@"128",@"129",@"130",@"131",@"132",@"133",@"134",@"135",@"136", -@"137",@"138",@"139",@"140",@"141",@"142",@"143",@"144",@"145", -@"146",@"147",@"148",@"149",@"150",@"151",@"152",@"153",@"154", -@"155",@"156",@"157",@"158",@"159",@"160",@"161",@"162",@"163", -@"164",@"165",@"166",@"167",@"168",@"169",@"170",@"171",@"172", -@"173",@"174",@"175",@"176",@"177",@"178",@"179",@"180",@"181", -@"182",@"183",@"184",@"185",@"186",@"187",@"188",@"189",@"190", -@"191",@"192",@"193",@"194",@"195",@"196",@"197",@"198",@"199", -@"200",@"201",@"202",@"203",@"204",@"205",@"206",@"207",@"208", -@"209",@"210",@"211",@"212",@"213",@"214",@"215",@"216",@"217", -@"218",@"219",@"220",@"221",@"222",@"223",@"224",@"225",@"226", -@"227",@"228",@"229",@"230",@"231",@"232",@"233",@"234",@"235", -@"236",@"237",@"238",@"239",@"240",@"241",@"242",@"243",@"244", -@"245",@"246",@"247",@"248",@"249",@"250",@"251",@"252",@"253", -@"254",@"255", @"256" -}; -// zig fmt: on -pub const S = extern struct { - e: E, -}; -export fn entry() void { - const s: S = undefined; - _ = s; -} - -// error -// -// :33:8: error: extern structs cannot contain fields of type 'tmp.E' -// :33:8: note: enum tag type 'u9' is not extern compatible -// :33:8: note: only integers with 0 or power of two bits are extern compatible -// :2:15: note: enum declared here diff --git a/test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig b/test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig @@ -10,6 +10,6 @@ export fn entry() void { // error // // :3:8: error: extern structs cannot contain fields of type 'tmp.E' -// :3:8: note: enum tag type 'u31' is not extern compatible -// :3:8: note: only integers with 0 or power of two bits are extern compatible +// :1:15: note: enum tag type 'u31' is not extern compatible +// :1:15: note: only integers with 0 or power of two bits are extern compatible // :1:15: note: enum declared here diff --git a/test/cases/compile_errors/AstGen_comptime_known_struct_is_resolved_before_error.zig b/test/cases/compile_errors/fn_body_in_struct_runtime_known.zig diff --git a/test/cases/compile_errors/fn_type_returning_pointer_to_itself.zig b/test/cases/compile_errors/fn_type_returning_pointer_to_itself.zig @@ -0,0 +1,8 @@ +const MyFn = fn () ?*const MyFn; +comptime { + _ = MyFn; +} + +// error +// +// :1:28: error: value of declaration 'tmp.MyFn' depends on itself here diff --git a/test/cases/compile_errors/function_ptr_alignment.zig b/test/cases/compile_errors/function_ptr_alignment.zig @@ -11,5 +11,5 @@ comptime { // error // target=x86_64-linux // -// :8:41: error: expected type '*align(2) const fn () void', found '*const fn () void' +// :8:41: error: expected type '*align(2) const fn () void', found '*align(1) const fn () void' // :8:41: note: pointer alignment '1' cannot cast into pointer alignment '2' diff --git a/test/cases/compile_errors/function_with_non-extern_non-packed_enum_parameter.zig b/test/cases/compile_errors/function_with_non-extern_non-packed_enum_parameter.zig @@ -7,6 +7,6 @@ export fn entry(foo: Foo) void { // target=x86_64-linux // // :2:17: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'x86_64_sysv' -// :2:17: note: enum tag type 'u2' is not extern compatible -// :2:17: note: only integers with 0, 8, 16, 32, 64 and 128 bits are extern compatible +// :1:13: note: integer tag type of enum is inferred +// :1:13: note: consider explicitly specifying the integer tag type // :1:13: note: enum declared here diff --git a/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig b/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig @@ -11,5 +11,5 @@ export fn entry(foo: Foo) void { // target=x86_64-linux // // :6:17: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'x86_64_sysv' -// :6:17: note: only extern structs and ABI sized packed structs are extern compatible +// :6:17: note: struct with automatic layout has no guaranteed in-memory representation // :1:13: note: struct declared here diff --git a/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig b/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig @@ -11,5 +11,5 @@ export fn entry(foo: Foo) void { // target=x86_64-linux // // :6:17: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'x86_64_sysv' -// :6:17: note: only extern unions and ABI sized packed unions are extern compatible +// :6:17: note: union with automatic layout has no guaranteed in-memory representation // :1:13: note: union declared here diff --git a/test/cases/compile_errors/generic_function_returning_opaque_type.zig b/test/cases/compile_errors/generic_function_returning_opaque_type.zig @@ -11,6 +11,6 @@ export fn bar() void { // error // +// :1:30: error: opaque return type 'anyopaque' not allowed // :1:30: error: opaque return type 'tmp.MyOpaque' not allowed // :4:18: note: opaque declared here -// :1:30: error: opaque return type 'anyopaque' not allowed diff --git a/test/cases/compile_errors/implicit_backing_type_in_extern_context.zig b/test/cases/compile_errors/implicit_backing_type_in_extern_context.zig @@ -0,0 +1,51 @@ +const PackedStruct = packed struct { x: u32 }; +const PackedUnion = packed union { x: u32 }; + +/// This enum has 256 fields, so `u8` will be its inferred tag type. +const Enum = enum { + // zig fmt: off + _00, _01, _02, _03, _04, _05, _06, _07, _08, _09, _0a, _0b, _0c, _0d, _0e, _0f, + _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _1a, _1b, _1c, _1d, _1e, _1f, + _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _2a, _2b, _2c, _2d, _2e, _2f, + _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _3a, _3b, _3c, _3d, _3e, _3f, + _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _4a, _4b, _4c, _4d, _4e, _4f, + _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _5a, _5b, _5c, _5d, _5e, _5f, + _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _6a, _6b, _6c, _6d, _6e, _6f, + _70, _71, _72, _73, _74, _75, _76, _77, _78, _79, _7a, _7b, _7c, _7d, _7e, _7f, + _80, _81, _82, _83, _84, _85, _86, _87, _88, _89, _8a, _8b, _8c, _8d, _8e, _8f, + _90, _91, _92, _93, _94, _95, _96, _97, _98, _99, _9a, _9b, _9c, _9d, _9e, _9f, + _a0, _a1, _a2, _a3, _a4, _a5, _a6, _a7, _a8, _a9, _aa, _ab, _ac, _ad, _ae, _af, + _b0, _b1, _b2, _b3, _b4, _b5, _b6, _b7, _b8, _b9, _ba, _bb, _bc, _bd, _be, _bf, + _c0, _c1, _c2, _c3, _c4, _c5, _c6, _c7, _c8, _c9, _ca, _cb, _cc, _cd, _ce, _cf, + _d0, _d1, _d2, _d3, _d4, _d5, _d6, _d7, _d8, _d9, _da, _db, _dc, _dd, _de, _df, + _e0, _e1, _e2, _e3, _e4, _e5, _e6, _e7, _e8, _e9, _ea, _eb, _ec, _ed, _ee, _ef, + _f0, _f1, _f2, _f3, _f4, _f5, _f6, _f7, _f8, _f9, _fa, _fb, _fc, _fd, _fe, _ff, + // zig fmt: on +}; + +const Extern0 = extern struct { val: PackedStruct }; +const Extern1 = extern struct { val: PackedUnion }; +const Extern2 = extern struct { val: Enum }; + +comptime { + _ = @as(Extern0, undefined); +} +comptime { + _ = @as(Extern1, undefined); +} +comptime { + _ = @as(Extern2, undefined); +} + +// error +// +// :26:38: error: extern structs cannot contain fields of type 'tmp.PackedStruct' +// :26:38: note: inferred backing integer of packed struct has unspecified signedness +// :1:29: note: struct declared here +// :27:38: error: extern structs cannot contain fields of type 'tmp.PackedUnion' +// :27:38: note: inferred backing integer of packed union has unspecified signedness +// :2:28: note: union declared here +// :28:38: error: extern structs cannot contain fields of type 'tmp.Enum' +// :5:14: note: integer tag type of enum is inferred +// :5:14: note: consider explicitly specifying the integer tag type +// :5:14: note: enum declared here diff --git a/test/cases/compile_errors/indexing_an_array_of_size_zero.zig b/test/cases/compile_errors/indexing_an_array_of_size_zero.zig @@ -6,4 +6,4 @@ export fn foo() void { // error // -// :3:27: error: indexing into empty array is not allowed +// :3:27: error: cannot index into empty array diff --git a/test/cases/compile_errors/indexing_an_array_of_size_zero_with_runtime_index.zig b/test/cases/compile_errors/indexing_an_array_of_size_zero_with_runtime_index.zig @@ -8,4 +8,4 @@ export fn foo() void { // error // -// :5:27: error: indexing into empty array is not allowed +// :5:27: error: cannot index into empty array diff --git a/test/cases/compile_errors/indirect_struct_loop.zig b/test/cases/compile_errors/indirect_struct_loop.zig @@ -13,4 +13,8 @@ export fn entry() usize { // error // -// :1:11: error: struct 'tmp.A' depends on itself +// error: dependency loop with length 3 +// :2:8: note: type 'tmp.A' depends on type 'tmp.B' for field declared here +// :5:8: note: type 'tmp.B' depends on type 'tmp.C' for field declared here +// :8:8: note: type 'tmp.C' depends on type 'tmp.A' for field declared here +// note: eliminate any one of these dependencies to break the loop diff --git a/test/cases/compile_errors/initialize_empty_union.zig b/test/cases/compile_errors/initialize_empty_union.zig @@ -0,0 +1,81 @@ +const EnumInferred = enum {}; +const EnumExplicit = enum(u8) {}; +const EnumNonexhaustive = enum(u8) { _ }; + +const U0 = union {}; +const U1 = union(enum) {}; +const U2 = union(enum(u8)) {}; +const U3 = union(EnumInferred) {}; +const U4 = union(EnumExplicit) {}; +const U5 = union(EnumNonexhaustive) {}; + +export fn init0() void { + _ = @as(U0, undefined); +} +export fn init1() void { + _ = @as(U1, undefined); +} +export fn init2() void { + _ = @as(U2, undefined); +} +export fn init3() void { + _ = @as(U3, undefined); +} +export fn init4() void { + _ = @as(U4, undefined); +} +export fn init5() void { + _ = @as(U5, undefined); +} + +export fn deref0(ptr: *const U0) void { + _ = ptr.*; +} +export fn deref1(ptr: *const U1) void { + _ = ptr.*; +} +export fn deref2(ptr: *const U2) void { + _ = ptr.*; +} +export fn deref3(ptr: *const U3) void { + _ = ptr.*; +} +export fn deref4(ptr: *const U4) void { + _ = ptr.*; +} +export fn deref5(ptr: *const U5) void { + _ = ptr.*; +} + +// error +// +// :13:17: error: expected type 'tmp.U0', found '@TypeOf(undefined)' +// :13:17: note: cannot coerce to uninstantiable type 'tmp.U0' +// :5:12: note: union declared here +// :16:17: error: expected type 'tmp.U1', found '@TypeOf(undefined)' +// :16:17: note: cannot coerce to uninstantiable type 'tmp.U1' +// :6:12: note: union declared here +// :19:17: error: expected type 'tmp.U2', found '@TypeOf(undefined)' +// :19:17: note: cannot coerce to uninstantiable type 'tmp.U2' +// :7:12: note: union declared here +// :22:17: error: expected type 'tmp.U3', found '@TypeOf(undefined)' +// :22:17: note: cannot coerce to uninstantiable type 'tmp.U3' +// :8:12: note: union declared here +// :25:17: error: expected type 'tmp.U4', found '@TypeOf(undefined)' +// :25:17: note: cannot coerce to uninstantiable type 'tmp.U4' +// :9:12: note: union declared here +// :28:17: error: expected type 'tmp.U5', found '@TypeOf(undefined)' +// :28:17: note: cannot coerce to uninstantiable type 'tmp.U5' +// :10:12: note: union declared here +// :32:12: error: cannot load uninstantiable type 'tmp.U0' +// :5:12: note: union declared here +// :35:12: error: cannot load uninstantiable type 'tmp.U1' +// :6:12: note: union declared here +// :38:12: error: cannot load uninstantiable type 'tmp.U2' +// :7:12: note: union declared here +// :41:12: error: cannot load uninstantiable type 'tmp.U3' +// :8:12: note: union declared here +// :44:12: error: cannot load uninstantiable type 'tmp.U4' +// :9:12: note: union declared here +// :47:12: error: cannot load uninstantiable type 'tmp.U5' +// :10:12: note: union declared here diff --git a/test/cases/compile_errors/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig b/test/cases/compile_errors/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig @@ -10,4 +10,4 @@ export fn entry() usize { // error // -// :1:13: error: struct 'tmp.Foo' depends on itself +// :2:8: error: type 'tmp.Foo' depends on itself for field declared here diff --git a/test/cases/compile_errors/instantiating_an_undefined_value_for_an_invalid_union_that_contains_itself.zig b/test/cases/compile_errors/instantiating_an_undefined_value_for_an_invalid_union_that_contains_itself.zig @@ -10,4 +10,4 @@ export fn entry() usize { // error // -// :1:13: error: union 'tmp.Foo' depends on itself +// :2:8: error: type 'tmp.Foo' depends on itself for field declared here diff --git a/test/cases/compile_errors/invalid_dependency_on_struct_size.zig b/test/cases/compile_errors/invalid_dependency_on_struct_size.zig @@ -1,16 +1,18 @@ -comptime { - const S = struct { - const Foo = struct { - y: Bar, - }; - const Bar = struct { - y: if (@sizeOf(Foo) == 0) u64 else void, - }; +const S = struct { + const Foo = struct { + y: Bar, }; - + const Bar = struct { + y: if (@sizeOf(Foo) == 0) u64 else void, + }; +}; +comptime { _ = @sizeOf(S.Foo) + 1; } // error // -// :6:21: error: struct layout depends on it having runtime bits +// error: dependency loop with length 2 +// :3:12: note: type 'tmp.S.Foo' depends on type 'tmp.S.Bar' for field declared here +// :6:24: note: type 'tmp.S.Bar' depends on type 'tmp.S.Foo' for size query here +// note: eliminate any one of these dependencies to break the loop diff --git a/test/cases/compile_errors/invalid_optional_type_in_extern_struct.zig b/test/cases/compile_errors/invalid_optional_type_in_extern_struct.zig @@ -2,10 +2,10 @@ const stroo = extern struct { moo: ?[*c]u8, }; export fn testf(fluff: *stroo) void { - _ = fluff; + _ = fluff.*; } // error // // :2:10: error: extern structs cannot contain fields of type '?[*c]u8' -// :2:10: note: only pointer like optionals are extern compatible +// :2:10: note: non-pointer optionals have no guaranteed in-memory representation diff --git a/test/cases/compile_errors/invalid_pointer_arithmetic.zig b/test/cases/compile_errors/invalid_pointer_arithmetic.zig @@ -27,11 +27,6 @@ comptime { } comptime { - const x: [*]u0 = @ptrFromInt(1); - _ = x + 1; -} - -comptime { const x: *u0 = @ptrFromInt(1); const y: *u0 = @ptrFromInt(2); _ = x - y; @@ -46,5 +41,4 @@ comptime { // :12:11: error: invalid operands to binary expression: 'pointer' and 'pointer' // :20:11: error: incompatible pointer arithmetic operands '[*]u8' and '[*]u16' // :26:11: error: incompatible pointer arithmetic operands '*u8' and '*u16' -// :31:11: error: pointer arithmetic requires element type 'u0' to have runtime bits -// :37:11: error: pointer arithmetic requires element type 'u0' to have runtime bits +// :32:11: error: pointer subtraction requires element type 'u0' to have runtime bits diff --git a/test/cases/compile_errors/invalid_type_in_builtin_extern.zig b/test/cases/compile_errors/invalid_type_in_builtin_extern.zig @@ -1,16 +1,22 @@ const x = @extern(*comptime_int, .{ .name = "foo" }); const y = @extern(*fn (u8) u8, .{ .name = "bar" }); -pub export fn entry() void { +const z = @extern(*fn (u8) callconv(.c) u8, .{ .name = "bar" }); +comptime { _ = x; } -pub export fn entry2() void { +comptime { _ = y; } +comptime { + _ = z; +} // error // // :1:19: error: extern symbol cannot have type '*comptime_int' -// :1:19: note: pointer to comptime-only type 'comptime_int' +// :1:19: note: pointer element type 'comptime_int' is not extern compatible // :2:19: error: extern symbol cannot have type '*fn (u8) u8' -// :2:19: note: pointer to extern function must be 'const' +// :2:19: note: pointer element type 'fn (u8) u8' is not extern compatible // :2:19: note: extern function must specify calling convention +// :3:19: error: extern symbol cannot have type '*fn (u8) callconv(.c) u8' +// :3:19: note: pointer to extern function must be 'const' diff --git a/test/cases/compile_errors/non-const_variables_of_things_that_require_const_variables.zig b/test/cases/compile_errors/non-const_variables_of_things_that_require_const_variables.zig @@ -1,49 +1,44 @@ -export fn entry1() void { - var m2 = &2; - _ = &m2; -} -export fn entry2() void { +export fn entry0() void { var a = undefined; _ = &a; } -export fn entry3() void { +export fn entry1() void { var b = 1; _ = &b; } -export fn entry4() void { +export fn entry2() void { var c = 1.0; _ = &c; } -export fn entry5() void { +export fn entry3() void { var d = null; _ = &d; } -export fn entry6(opaque_: *Opaque) void { +export fn entry4(opaque_: *Opaque) void { var e = opaque_.*; _ = &e; } -export fn entry7() void { +export fn entry5() void { var f = i32; _ = &f; } const Opaque = opaque {}; -export fn entry8() void { +export fn entry6() void { var e: Opaque = undefined; _ = &e; } // error // -// :2:9: error: variable of type '*const comptime_int' must be const or comptime -// :6:9: error: variable of type '@TypeOf(undefined)' must be const or comptime -// :10:9: error: variable of type 'comptime_int' must be const or comptime +// :2:9: error: variable of type '@TypeOf(undefined)' must be const or comptime +// :6:9: error: variable of type 'comptime_int' must be const or comptime +// :6:9: note: to modify this variable at runtime, it must be given an explicit fixed-size number type +// :10:9: error: variable of type 'comptime_float' must be const or comptime // :10:9: note: to modify this variable at runtime, it must be given an explicit fixed-size number type -// :14:9: error: variable of type 'comptime_float' must be const or comptime -// :14:9: note: to modify this variable at runtime, it must be given an explicit fixed-size number type -// :18:9: error: variable of type '@TypeOf(null)' must be const or comptime -// :22:20: error: cannot load opaque type 'tmp.Opaque' -// :29:16: note: opaque declared here -// :26:9: error: variable of type 'type' must be const or comptime -// :26:9: note: types are not available at runtime -// :31:12: error: non-extern variable with opaque type 'tmp.Opaque' -// :29:16: note: opaque declared here +// :14:9: error: variable of type '@TypeOf(null)' must be const or comptime +// :18:20: error: cannot load opaque type 'tmp.Opaque' +// :25:16: note: opaque declared here +// :22:9: error: variable of type 'type' must be const or comptime +// :22:9: note: types are not available at runtime +// :27:12: error: non-extern variable with opaque type 'tmp.Opaque' +// :25:16: note: opaque declared here diff --git a/test/cases/compile_errors/non-exhaustive_enum_marker_assigned_a_value.zig b/test/cases/compile_errors/non-exhaustive_enum_marker_assigned_a_value.zig @@ -3,18 +3,7 @@ const A = enum { b, _ = 1, }; -const B = enum { - a, - b, - _, -}; -comptime { - _ = A; - _ = B; -} // error // // :4:9: error: '_' is used to mark an enum as non-exhaustive and cannot be assigned a value -// :6:11: error: non-exhaustive enum missing integer tag type -// :9:5: note: marked non-exhaustive here diff --git a/test/cases/compile_errors/non-exhaustive_enum_missing_tag_type.zig b/test/cases/compile_errors/non-exhaustive_enum_missing_tag_type.zig @@ -0,0 +1,10 @@ +const E = enum { + a, + b, + _, +}; + +// error +// +// :1:11: error: non-exhaustive enum missing integer tag type +// :4:5: note: marked non-exhaustive here diff --git a/test/cases/compile_errors/non-exhaustive_enum_specifies_every_value.zig b/test/cases/compile_errors/non-exhaustive_enum_specifies_every_value.zig @@ -4,7 +4,7 @@ const C = enum(u1) { _, }; pub export fn entry() void { - _ = C; + _ = C.a; } // error diff --git a/test/cases/compile_errors/non-inline_for_loop_on_a_type_that_requires_comptime.zig b/test/cases/compile_errors/non-inline_for_loop_on_a_type_that_requires_comptime.zig @@ -11,6 +11,6 @@ export fn entry() void { // error // -// :7:10: error: values of type '[2]tmp.Foo' must be comptime-known, but index value is runtime-known +// :7:10: error: values of type 'tmp.Foo' must be comptime-known, but index value is runtime-known // :3:8: note: struct requires comptime because of this field // :3:8: note: types are not available at runtime diff --git a/test/cases/compile_errors/non_constant_expression_in_array_size.zig b/test/cases/compile_errors/non_constant_expression_in_array_size.zig @@ -14,4 +14,4 @@ export fn entry() usize { // // :6:12: error: unable to resolve comptime value // :2:12: note: called at comptime from here -// :1:13: note: types must be comptime-known +// :2:8: note: struct field types must be comptime-known diff --git a/test/cases/compile_errors/noreturn_struct_field.zig b/test/cases/compile_errors/noreturn_struct_field.zig @@ -1,10 +0,0 @@ -const S = struct { - s: noreturn, -}; -comptime { - _ = @typeInfo(S); -} - -// error -// -// :2:8: error: struct fields cannot be 'noreturn' diff --git a/test/cases/compile_errors/old_fn_ptr_in_extern_context.zig b/test/cases/compile_errors/old_fn_ptr_in_extern_context.zig @@ -4,15 +4,9 @@ const S = extern struct { comptime { _ = @sizeOf(S) == 1; } -comptime { - _ = [*c][4]fn () callconv(.c) void; -} // error // // :2:8: error: extern structs cannot contain fields of type 'fn () callconv(.c) void' // :2:8: note: type has no guaranteed in-memory representation // :2:8: note: use '*const ' to make a function pointer type -// :8:13: error: C pointers cannot point to non-C-ABI-compatible type '[4]fn () callconv(.c) void' -// :8:13: note: type has no guaranteed in-memory representation -// :8:13: note: use '*const ' to make a function pointer type diff --git a/test/cases/compile_errors/overflow_in_enum_value_allocation.zig b/test/cases/compile_errors/overflow_in_enum_value_allocation.zig @@ -9,4 +9,4 @@ pub export fn entry() void { // error // -// :3:5: error: enumeration value '256' too large for type 'u8' +// :3:5: error: enum tag value '256' too large for type 'u8' diff --git a/test/cases/compile_errors/packed_struct_backing_int_wrong.zig b/test/cases/compile_errors/packed_struct_backing_int_wrong.zig @@ -44,8 +44,12 @@ export fn entry7() void { // error // -// :2:31: error: backing integer type 'u32' has bit size 32 but the struct fields have a total bit size of 29 -// :9:31: error: backing integer type 'i31' has bit size 31 but the struct fields have a total bit size of 32 +// :2:24: error: backing integer bit width does not match total bit width of fields +// :2:31: note: backing integer 'u32' has bit width '32' +// :2:24: note: struct fields have total bit width '29' +// :9:24: error: backing integer bit width does not match total bit width of fields +// :9:31: note: backing integer 'i31' has bit width '31' +// :9:24: note: struct fields have total bit width '32' // :17:31: error: expected backing integer type, found 'void' // :23:31: error: expected backing integer type, found 'void' // :27:31: error: expected backing integer type, found 'noreturn' diff --git a/test/cases/compile_errors/packed_struct_uses_own_size.zig b/test/cases/compile_errors/packed_struct_uses_own_size.zig @@ -0,0 +1,10 @@ +const S = packed struct { + x: @Int(.unsigned, @sizeOf(S)), +}; +comptime { + _ = @as(S, undefined); +} + +// error +// +// :2:32: error: type 'tmp.S' depends on itself for size query here diff --git a/test/cases/compile_errors/packed_struct_uses_own_typeinfo.zig b/test/cases/compile_errors/packed_struct_uses_own_typeinfo.zig @@ -0,0 +1,13 @@ +const S = packed struct(u16) { + a: bool, + b: bool, + _padding: @Int(.unsigned, 17 - @typeInfo(S).Struct.fields.len) = 0, +}; + +comptime { + _ = @as(S, .{ .a = true, .b = true }); +} + +// error +// +// :4:36: error: type 'tmp.S' depends on itself for type information query here diff --git a/test/cases/compile_errors/packed_struct_with_fields_of_not_allowed_types.zig b/test/cases/compile_errors/packed_struct_with_fields_of_not_allowed_types.zig @@ -76,30 +76,41 @@ export fn entry14() void { x: E, }); } +export fn entry15() void { + _ = @sizeOf(packed struct { + x: *const u32, + }); +} // error // // :3:12: error: packed structs cannot contain fields of type 'anyerror' -// :3:12: note: type has no guaranteed in-memory representation +// :3:12: note: type does not have a bit-packed representation // :8:12: error: packed structs cannot contain fields of type '[2]u24' -// :8:12: note: type has no guaranteed in-memory representation +// :8:12: note: type does not have a bit-packed representation // :13:20: error: packed structs cannot contain fields of type 'anyerror!u32' -// :13:20: note: type has no guaranteed in-memory representation +// :13:20: note: type does not have a bit-packed representation // :18:12: error: packed structs cannot contain fields of type 'tmp.S' -// :18:12: note: only packed structs layout are allowed in packed types +// :18:12: note: non-packed structs do not have a bit-packed representation // :56:11: note: struct declared here // :23:12: error: packed structs cannot contain fields of type 'tmp.U' -// :23:12: note: only packed unions layout are allowed in packed types +// :23:12: note: non-packed unions do not have a bit-packed representation // :59:18: note: union declared here // :28:12: error: packed structs cannot contain fields of type '?anyerror' -// :28:12: note: type has no guaranteed in-memory representation +// :28:12: note: type does not have a bit-packed representation // :38:12: error: packed structs cannot contain fields of type 'fn () void' -// :38:12: note: type has no guaranteed in-memory representation -// :38:12: note: use '*const ' to make a function pointer type +// :38:12: note: type does not have a bit-packed representation +// :43:12: error: packed structs cannot contain fields of type '*const fn () void' +// :43:12: note: pointers cannot be directly bitpacked +// :43:12: note: consider using 'usize' and '@intFromPtr' // :65:31: error: packed structs cannot contain fields of type '[]u8' -// :65:31: note: slices have no guaranteed in-memory representation +// :65:31: note: slices do not have a bit-packed representation // :70:12: error: packed structs cannot contain fields of type '*type' -// :70:12: note: comptime-only pointer has no guaranteed in-memory representation -// :70:12: note: types are not available at runtime +// :70:12: note: pointers cannot be directly bitpacked +// :70:12: note: consider using 'usize' and '@intFromPtr' // :76:12: error: packed structs cannot contain fields of type 'tmp.entry14.E' -// :74:15: note: enum declared here +// :74:15: note: integer tag type of enum is inferred +// :74:15: note: consider explicitly specifying the integer tag type +// :81:12: error: packed structs cannot contain fields of type '*const u32' +// :81:12: note: pointers cannot be directly bitpacked +// :81:12: note: consider using 'usize' and '@intFromPtr' diff --git a/test/cases/compile_errors/packed_union_fields_mismatch.zig b/test/cases/compile_errors/packed_union_fields_mismatch.zig @@ -1,12 +1,14 @@ export fn entry1() void { - _ = packed union { + const U = packed union { a: u1, b: u2, }; + _ = @as(U, undefined); } // error // -// :2:16: error: packed union has fields with mismatching bit sizes -// :3:12: note: 1 bits here -// :4:12: note: 2 bits here +// :4:12: error: field bit width does not match earlier field +// :4:12: note: field type 'u2' has bit width '2' +// :3:12: note: other field type 'u1' has bit width '1' +// :4:12: note: all fields in a packed union must have the same bit width diff --git a/test/cases/compile_errors/packed_union_given_enum_tag_type.zig b/test/cases/compile_errors/packed_union_given_enum_tag_type.zig @@ -1,18 +0,0 @@ -const Letter = enum { - A, - B, - C, -}; -const Payload = packed union(Letter) { - A: i32, - B: f64, - C: bool, -}; -export fn entry() void { - const a: Payload = .{ .A = 1234 }; - _ = a; -} - -// error -// -// :6:30: error: packed union does not support enum tag type diff --git a/test/cases/compile_errors/packed_union_with_automatic_layout_field.zig b/test/cases/compile_errors/packed_union_with_automatic_layout_field.zig @@ -1,18 +0,0 @@ -const Foo = struct { - a: u32, - b: f32, -}; -const Payload = packed union { - A: Foo, - B: bool, -}; -export fn entry() void { - const a: Payload = .{ .B = true }; - _ = a; -} - -// error -// -// :6:8: error: packed unions cannot contain fields of type 'tmp.Foo' -// :6:8: note: only packed structs layout are allowed in packed types -// :1:13: note: struct declared here diff --git a/test/cases/compile_errors/packed_union_with_fields_of_not_allowed_types.zig b/test/cases/compile_errors/packed_union_with_fields_of_not_allowed_types.zig @@ -0,0 +1,21 @@ +const S = struct { a: u32 }; +export fn entry0() void { + _ = @sizeOf(packed union { + foo: S, + bar: bool, + }); +} +export fn entry1() void { + _ = @sizeOf(packed union { + x: *const u32, + }); +} + +// error +// +// :4:14: error: packed unions cannot contain fields of type 'tmp.S' +// :4:14: note: non-packed structs do not have a bit-packed representation +// :1:11: note: struct declared here +// :10:12: error: packed unions cannot contain fields of type '*const u32' +// :10:12: note: pointers cannot be directly bitpacked +// :10:12: note: consider using 'usize' and '@intFromPtr' diff --git a/test/cases/compile_errors/pointer_in_bitpack.zig b/test/cases/compile_errors/pointer_in_bitpack.zig @@ -0,0 +1,22 @@ +const S = packed struct { + ptr: *u32, +}; +export fn foo() void { + _ = @as(S, undefined); +} + +const U = packed union { + ptr: *u32, +}; +export fn bar() void { + _ = @as(U, undefined); +} + +// error +// +// :2:10: error: packed structs cannot contain fields of type '*u32' +// :2:10: note: pointers cannot be directly bitpacked +// :2:10: note: consider using 'usize' and '@intFromPtr' +// :9:10: error: packed unions cannot contain fields of type '*u32' +// :9:10: note: pointers cannot be directly bitpacked +// :9:10: note: consider using 'usize' and '@intFromPtr' diff --git a/test/cases/compile_errors/reify_enum_with_duplicate_field.zig b/test/cases/compile_errors/reify_enum_with_duplicate_field.zig @@ -1,8 +1,9 @@ export fn entry() void { - _ = @Enum(u32, .nonexhaustive, &.{ "A", "A" }, &.{ 0, 1 }); + const E = @Enum(u32, .nonexhaustive, &.{ "A", "A" }, &.{ 0, 1 }); + _ = @as(E, undefined); } // error // -// :2:36: error: duplicate enum field 'A' -// :2:36: note: other field here +// :2:42: error: duplicate enum field 'A' at index '1' +// :2:42: note: previous field at index '0' diff --git a/test/cases/compile_errors/reify_enum_with_duplicate_tag_value.zig b/test/cases/compile_errors/reify_enum_with_duplicate_tag_value.zig @@ -1,8 +1,9 @@ export fn entry() void { - _ = @Enum(u32, .nonexhaustive, &.{ "A", "B" }, &.{ 10, 10 }); + const E = @Enum(u32, .nonexhaustive, &.{ "a", "b" }, &.{ 10, 10 }); + _ = E.a; } // error // -// :2:52: error: enum tag value 10 already taken -// :2:52: note: other enum tag value here +// :2:58: error: enum tag value '10' for field 'b' already taken +// :2:58: note: previous occurrence in field 'a' diff --git a/test/cases/compile_errors/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig b/test/cases/compile_errors/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig @@ -5,4 +5,4 @@ export fn entry() void { // error // -// :1:19: error: tag type must be an integer type +// :1:19: error: expected integer tag type, found 'bool' diff --git a/test/cases/compile_errors/reify_type_for_tagged_packed_union.zig b/test/cases/compile_errors/reify_type_for_tagged_packed_union.zig @@ -1,11 +0,0 @@ -const Tag = @Enum(u2, .exhaustive, &.{ "signed", "unsigned" }, &.{ 0, 1 }); -const Packed = @Union(.@"packed", Tag, &.{ "signed", "unsigned" }, &.{ i32, u32 }, &@splat(.{})); - -export fn entry() void { - const tagged: Packed = .{ .signed = -1 }; - _ = tagged; -} - -// error -// -// :2:35: error: packed union does not support enum tag type diff --git a/test/cases/compile_errors/reify_type_for_tagged_union_with_extra_enum_field.zig b/test/cases/compile_errors/reify_type_for_tagged_union_with_extra_enum_field.zig @@ -7,6 +7,5 @@ export fn entry() void { // error // -// :2:35: error: 1 enum fields missing in union -// :1:13: note: field 'arst' missing, declared here -// :1:13: note: enum declared here +// :2:16: error: enum field 'arst' missing from union +// :1:36: note: enum field here diff --git a/test/cases/compile_errors/reify_type_for_tagged_union_with_no_union_fields.zig b/test/cases/compile_errors/reify_type_for_tagged_union_with_no_union_fields.zig @@ -7,7 +7,5 @@ export fn entry() void { // error // -// :2:35: error: 2 enum fields missing in union -// :1:13: note: field 'signed' missing, declared here -// :1:13: note: field 'unsigned' missing, declared here -// :1:13: note: enum declared here +// :2:16: error: enum field 'signed' missing from union +// :1:36: note: enum field here diff --git a/test/cases/compile_errors/reify_type_for_union_with_opaque_field.zig b/test/cases/compile_errors/reify_type_for_union_with_opaque_field.zig @@ -1,9 +1,11 @@ -const Untagged = @Union(.auto, null, &.{"foo"}, &.{opaque {}}, &.{.{}}); +const Opaque = opaque {}; +const Untagged = @Union(.auto, null, &.{"foo"}, &.{Opaque}, &.{.{}}); export fn entry() usize { return @sizeOf(Untagged); } // error // -// :1:49: error: opaque types have unknown size and therefore cannot be directly embedded in unions -// :1:52: note: opaque declared here +// :2:49: error: cannot directly embed opaque type 'tmp.Opaque' in union +// :2:49: note: opaque types have unknown size +// :1:16: note: opaque declared here diff --git a/test/cases/compile_errors/reify_type_with_invalid_field_alignment.zig b/test/cases/compile_errors/reify_type_with_invalid_field_alignment.zig @@ -2,7 +2,11 @@ comptime { _ = @Union(.auto, null, &.{"foo"}, &.{usize}, &.{.{ .@"align" = 3 }}); } comptime { - _ = @Struct(.auto, null, &.{"a"}, &.{u32}, &.{.{ .@"comptime" = true, .@"align" = 5 }}); + _ = @Struct(.auto, null, &.{"a"}, &.{u32}, &.{.{ + .@"comptime" = true, + .@"align" = 5, + .default_value_ptr = &@as(u32, 0), + }}); } comptime { _ = @Pointer(.many, .{ .@"align" = 7 }, u8, null); @@ -12,4 +16,4 @@ comptime { // // :2:51: error: alignment value '3' is not a power of two // :5:48: error: alignment value '5' is not a power of two -// :8:26: error: alignment value '7' is not a power of two +// :12:26: error: alignment value '7' is not a power of two diff --git a/test/cases/compile_errors/resolve_inferred_error_set_of_generic_fn.zig b/test/cases/compile_errors/resolve_inferred_error_set_of_generic_fn.zig @@ -12,5 +12,4 @@ export fn entry() void { // error // -// :10:15: error: unable to resolve inferred error set of generic function -// :1:1: note: generic function declared here +// :1:1: error: cannot resolve inferred error set of generic function type 'fn (anytype) @typeInfo(@typeInfo(@TypeOf(tmp.foo)).@"fn".return_type.?).error_union.error_set!void' diff --git a/test/cases/compile_errors/runtime_@ptrFromInt_to_comptime_only_type.zig b/test/cases/compile_errors/runtime_@ptrFromInt_to_comptime_only_type.zig @@ -10,6 +10,5 @@ pub export fn callbackFin(id: c_int, arg: ?*anyopaque) void { // error // -// :5:54: error: pointer to comptime-only type '?*tmp.GuSettings' must be comptime-known, but operand is runtime-known -// :2:10: note: struct requires comptime because of this field -// :2:10: note: use '*const fn (c_int) callconv(.c) void' for a function pointer type +// :6:19: error: cannot load comptime-only type '?fn (c_int) callconv(.c) void' +// :6:20: note: pointer of type '*?fn (c_int) callconv(.c) void' is runtime-known diff --git a/test/cases/compile_errors/runtime_index_into_comptime_only_many_ptr.zig b/test/cases/compile_errors/runtime_index_into_comptime_only_many_ptr.zig @@ -1,10 +1,10 @@ var rt: usize = 0; export fn foo() void { const x: [*]const type = &.{ u8, u16 }; - _ = &x[rt]; + _ = x[rt]; } // error // -// :4:12: error: values of type '[*]const type' must be comptime-known, but index value is runtime-known -// :4:11: note: types are not available at runtime +// :4:11: error: values of type 'type' must be comptime-known, but index value is runtime-known +// :4:10: note: types are not available at runtime diff --git a/test/cases/compile_errors/runtime_index_into_comptime_type_slice.zig b/test/cases/compile_errors/runtime_index_into_comptime_type_slice.zig @@ -12,7 +12,6 @@ export fn entry() void { // error // -// :9:54: error: values of type '[]const builtin.Type.StructField' must be comptime-known, but index value is runtime-known +// :9:54: error: values of type 'builtin.Type.StructField' must be comptime-known, but index value is runtime-known // : note: struct requires comptime because of this field // : note: types are not available at runtime -// : struct requires comptime because of this field diff --git a/test/cases/compile_errors/runtime_indexing_comptime_array.zig b/test/cases/compile_errors/runtime_indexing_comptime_array.zig @@ -24,9 +24,9 @@ pub export fn entry3() void { } // error // -// :7:10: error: values of type '[2]fn () void' must be comptime-known, but index value is runtime-known +// :7:10: error: values of type 'fn () void' must be comptime-known, but index value is runtime-known // :7:10: note: use '*const fn () void' for a function pointer type -// :15:18: error: values of type '[2]fn () void' must be comptime-known, but index value is runtime-known +// :15:18: error: values of type 'fn () void' must be comptime-known, but index value is runtime-known // :15:17: note: use '*const fn () void' for a function pointer type -// :22:19: error: values of type '[2]fn () void' must be comptime-known, but index value is runtime-known +// :22:19: error: values of type 'fn () void' must be comptime-known, but index value is runtime-known // :22:18: note: use '*const fn () void' for a function pointer type diff --git a/test/cases/compile_errors/runtime_operation_in_comptime_scope.zig b/test/cases/compile_errors/runtime_operation_in_comptime_scope.zig @@ -25,13 +25,13 @@ var rt: u32 = undefined; // // :19:8: error: unable to evaluate comptime expression // :19:5: note: operation is runtime due to this operand +// :6:8: note: called at comptime from here +// :5:1: note: 'comptime' keyword forces comptime evaluation +// :19:8: error: unable to evaluate comptime expression +// :19:5: note: operation is runtime due to this operand // :14:8: note: called at comptime from here // :10:12: note: called at comptime from here // :10:12: note: call to function with comptime-only return type 'type' is evaluated at comptime // :13:10: note: return type declared here // :10:12: note: types are not available at runtime // :2:8: note: called inline here -// :19:8: error: unable to evaluate comptime expression -// :19:5: note: operation is runtime due to this operand -// :6:8: note: called at comptime from here -// :5:1: note: 'comptime' keyword forces comptime evaluation diff --git a/test/cases/compile_errors/self_referential_struct_requires_comptime.zig b/test/cases/compile_errors/self_referential_struct_requires_comptime.zig @@ -12,4 +12,3 @@ pub export fn entry() void { // :6:12: error: variable of type 'tmp.S' must be const or comptime // :2:8: note: struct requires comptime because of this field // :2:8: note: use '*const fn () void' for a function pointer type -// :3:8: note: struct requires comptime because of this field diff --git a/test/cases/compile_errors/self_referential_union_requires_comptime.zig b/test/cases/compile_errors/self_referential_union_requires_comptime.zig @@ -12,4 +12,3 @@ pub export fn entry() void { // :6:12: error: variable of type 'tmp.U' must be const or comptime // :2:8: note: union requires comptime because of this field // :2:8: note: use '*const fn () void' for a function pointer type -// :3:8: note: union requires comptime because of this field diff --git a/test/cases/compile_errors/simple_struct_loop.zig b/test/cases/compile_errors/simple_struct_loop.zig @@ -0,0 +1,16 @@ +const A = struct { + b: B, +}; +const B = struct { + a: A, +}; +comptime { + _ = @as(A, undefined); +} + +// error +// +// error: dependency loop with length 2 +// :2:8: note: type 'tmp.A' depends on type 'tmp.B' for field declared here +// :5:8: note: type 'tmp.B' depends on type 'tmp.A' for field declared here +// note: eliminate any one of these dependencies to break the loop diff --git a/test/cases/compile_errors/sizeOf_bad_type.zig b/test/cases/compile_errors/sizeOf_bad_type.zig @@ -1,7 +1,31 @@ -export fn entry() usize { +export fn entry0() usize { return @sizeOf(@TypeOf(null)); } +export fn entry1() usize { + return @sizeOf(comptime_int); +} +export fn entry2() usize { + return @sizeOf(noreturn); +} +const S3 = struct { a: u32, b: comptime_int }; +export fn entry3() usize { + return @sizeOf(S3); +} +const S4 = struct { a: u32, b: noreturn }; +export fn entry4() usize { + return @sizeOf(S4); +} +export fn entry5() usize { + return @sizeOf([1]fn () void); +} // error // -// :2:20: error: no size available for type '@TypeOf(null)' +// :2:20: error: no size available for comptime-only type '@TypeOf(null)' +// :5:20: error: no size available for comptime-only type 'comptime_int' +// :8:20: error: no size available for uninstantiable type 'noreturn' +// :12:20: error: no size available for comptime-only type 'tmp.S3' +// :10:12: note: struct declared here +// :16:20: error: no size available for uninstantiable type 'tmp.S4' +// :14:12: note: struct declared here +// :19:20: error: no size available for comptime-only type '[1]fn () void' diff --git a/test/cases/compile_errors/sizeof_alignof_empty_union.zig b/test/cases/compile_errors/sizeof_alignof_empty_union.zig @@ -0,0 +1,75 @@ +const EnumInferred = enum {}; +const EnumExplicit = enum(u8) {}; +const EnumNonexhaustive = enum(u8) { _ }; + +const U0 = union {}; +const U1 = union(enum) {}; +const U2 = union(enum(u8)) {}; +const U3 = union(EnumInferred) {}; +const U4 = union(EnumExplicit) {}; +const U5 = union(EnumNonexhaustive) {}; + +export fn size0() void { + _ = @sizeOf(U0); +} +export fn size1() void { + _ = @sizeOf(U1); +} +export fn size2() void { + _ = @sizeOf(U2); +} +export fn size3() void { + _ = @sizeOf(U3); +} +export fn size4() void { + _ = @sizeOf(U4); +} +export fn size5() void { + _ = @sizeOf(U5); +} + +export fn align0() void { + _ = @alignOf(U0); +} +export fn align1() void { + _ = @alignOf(U1); +} +export fn align2() void { + _ = @alignOf(U2); +} +export fn align3() void { + _ = @alignOf(U3); +} +export fn align4() void { + _ = @alignOf(U4); +} +export fn align5() void { + _ = @alignOf(U5); +} + +// error +// +// :13:17: error: no size available for uninstantiable type 'tmp.U0' +// :5:12: note: union declared here +// :16:17: error: no size available for uninstantiable type 'tmp.U1' +// :6:12: note: union declared here +// :19:17: error: no size available for uninstantiable type 'tmp.U2' +// :7:12: note: union declared here +// :22:17: error: no size available for uninstantiable type 'tmp.U3' +// :8:12: note: union declared here +// :25:17: error: no size available for uninstantiable type 'tmp.U4' +// :9:12: note: union declared here +// :28:17: error: no size available for uninstantiable type 'tmp.U5' +// :10:12: note: union declared here +// :32:18: error: no align available for uninstantiable type 'tmp.U0' +// :5:12: note: union declared here +// :35:18: error: no align available for uninstantiable type 'tmp.U1' +// :6:12: note: union declared here +// :38:18: error: no align available for uninstantiable type 'tmp.U2' +// :7:12: note: union declared here +// :41:18: error: no align available for uninstantiable type 'tmp.U3' +// :8:12: note: union declared here +// :44:18: error: no align available for uninstantiable type 'tmp.U4' +// :9:12: note: union declared here +// :47:18: error: no align available for uninstantiable type 'tmp.U5' +// :10:12: note: union declared here diff --git a/test/cases/compile_errors/slice_used_as_extern_fn_param.zig b/test/cases/compile_errors/slice_used_as_extern_fn_param.zig @@ -1,6 +1,6 @@ extern fn Text(str: []const u8, num: i32) callconv(.c) void; export fn entry() void { - _ = Text; + Text(undefined, undefined); } // error diff --git a/test/cases/compile_errors/specify_enum_tag_type_that_is_too_small.zig b/test/cases/compile_errors/specify_enum_tag_type_that_is_too_small.zig @@ -1,9 +1,9 @@ const Small = enum(u2) { - One, - Two, - Three, - Four, - Five, + one, + two, + three, + four, + five, }; const SmallUnion = union(enum(u2)) { @@ -14,13 +14,13 @@ const SmallUnion = union(enum(u2)) { }; comptime { - _ = Small; + _ = Small.one; } comptime { - _ = SmallUnion; + _ = SmallUnion.one; } // error // -// :6:5: error: enumeration value '4' too large for type 'u2' -// :13:5: error: enumeration value '4' too large for type 'u2' +// :6:5: error: enum tag value '4' too large for type 'u2' +// :13:5: error: enum tag value '4' too large for type 'u2' diff --git a/test/cases/compile_errors/store_comptime_only_type_to_runtime_pointer.zig b/test/cases/compile_errors/store_comptime_only_type_to_runtime_pointer.zig @@ -21,22 +21,15 @@ export fn e() void { p.* = undefined; } -export fn f() void { - const p: **comptime_int = @ptrFromInt(16); // double pointer ('*comptime_int' is comptime-only) - p.* = undefined; -} - // error // // :3:9: error: cannot store comptime-only type 'fn () void' at runtime // :3:6: note: operation is runtime due to this pointer // :7:11: error: expected type 'anyopaque', found '@TypeOf(undefined)' -// :7:11: note: cannot coerce to 'anyopaque' +// :7:11: note: cannot coerce to uninstantiable type 'anyopaque' // :11:12: error: cannot load opaque type 'anyopaque' // :16:11: error: expected type 'tmp.Opaque', found '@TypeOf(undefined)' -// :16:11: note: cannot coerce to 'tmp.Opaque' +// :16:11: note: cannot coerce to uninstantiable type 'tmp.Opaque' // :14:16: note: opaque declared here // :21:9: error: cannot store comptime-only type 'comptime_int' at runtime // :21:6: note: operation is runtime due to this pointer -// :26:9: error: cannot store comptime-only type '*comptime_int' at runtime -// :26:6: note: operation is runtime due to this pointer diff --git a/test/cases/compile_errors/struct_depends_on_itself_via_non_initial_field.zig b/test/cases/compile_errors/struct_depends_on_itself_via_non_initial_field.zig @@ -4,9 +4,9 @@ const A = struct { }; comptime { - _ = A; + _ = @as(A, undefined); } // error // -// :1:11: error: struct 'tmp.A' depends on itself +// :3:21: error: type 'tmp.A' depends on itself for size query here diff --git a/test/cases/compile_errors/struct_depends_on_itself_via_optional_field.zig b/test/cases/compile_errors/struct_depends_on_itself_via_optional_field.zig @@ -12,4 +12,7 @@ export fn entry() void { // error // -// :1:17: error: struct 'tmp.LhsExpr' depends on itself +// error: dependency loop with length 2 +// :2:14: note: type 'tmp.LhsExpr' depends on type 'tmp.AstObject' for field declared here +// :5:14: note: type 'tmp.AstObject' depends on type 'tmp.LhsExpr' for field declared here +// note: eliminate any one of these dependencies to break the loop diff --git a/test/cases/compile_errors/struct_depends_on_pointer_alignment.zig b/test/cases/compile_errors/struct_depends_on_pointer_alignment.zig @@ -1,11 +0,0 @@ -const S = struct { - next: ?*align(1) S align(128), -}; - -export fn entry() usize { - return @alignOf(S); -} - -// error -// -// :1:11: error: struct layout depends on being pointer aligned diff --git a/test/cases/compile_errors/struct_field_queries_hasfield_of_itself.zig b/test/cases/compile_errors/struct_field_queries_hasfield_of_itself.zig @@ -0,0 +1,14 @@ +const Foo = packed struct { + bar: (T: { + _ = @hasField(Foo, "bar"); + break :T void; + }), +}; + +comptime { + _ = @as(Foo, undefined); +} + +// error +// +// :3:23: error: type 'tmp.Foo' depends on itself for field query here diff --git a/test/cases/compile_errors/struct_uses_reified_type_which_queries_struct_alignment.zig b/test/cases/compile_errors/struct_uses_reified_type_which_queries_struct_alignment.zig @@ -0,0 +1,13 @@ +const A = struct { b: *B }; +const B = @Struct(.auto, null, &.{"x"}, &.{A}, &.{.{ .@"align" = @alignOf(A) }}); +comptime { + _ = @as(A, undefined); + _ = @as(B, undefined); +} + +// error +// +// error: dependency loop with length 2 +// :1:24: note: type 'tmp.A' uses value of declaration 'tmp.B' here +// :2:75: note: value of declaration 'tmp.B' depends on type 'tmp.A' for alignment query here +// note: eliminate any one of these dependencies to break the loop diff --git a/test/cases/compile_errors/struct_uses_sizeof_self_as_array_len.zig b/test/cases/compile_errors/struct_uses_sizeof_self_as_array_len.zig @@ -0,0 +1,10 @@ +const S = struct { + a: *[@sizeOf(S)]u8, +}; +comptime { + _ = @as(S, undefined); +} + +// error +// +// :2:18: error: type 'tmp.S' depends on itself for size query here diff --git a/test/cases/compile_errors/too_big_packed_struct.zig b/test/cases/compile_errors/too_big_packed_struct.zig @@ -8,4 +8,4 @@ pub export fn entry() void { // error // -// :2:22: error: size of packed struct '131070' exceeds maximum bit width of 65535 +// :2:22: error: packed struct bit width '131070' exceeds maximum bit width of 65535 diff --git a/test/cases/compile_errors/top_level_decl_dependency_loop.zig b/test/cases/compile_errors/top_level_decl_dependency_loop.zig @@ -7,4 +7,9 @@ export fn entry() void { // error // -// :1:1: error: dependency loop detected +// error: dependency loop with length 4 +// :1:23: note: value of declaration 'tmp.a' uses type of declaration 'tmp.a' here +// :1:18: note: type of declaration 'tmp.a' uses value of declaration 'tmp.b' here +// :2:23: note: value of declaration 'tmp.b' uses type of declaration 'tmp.b' here +// :2:18: note: type of declaration 'tmp.b' uses value of declaration 'tmp.a' here +// note: eliminate any one of these dependencies to break the loop diff --git a/test/cases/compile_errors/unable_to_evaluate_comptime_expr.zig b/test/cases/compile_errors/unable_to_evaluate_comptime_expr.zig @@ -16,22 +16,6 @@ pub export fn entry2() void { _ = b; } -const Int = @typeInfo(bar).@"struct".backing_integer.?; - -const foo = enum(Int) { - c = @bitCast(bar{ - .name = "test", - }), -}; - -const bar = packed struct { - name: [*:0]const u8, -}; - -pub export fn entry3() void { - _ = @field(foo, "c"); -} - // error // // :7:13: error: unable to evaluate comptime expression @@ -40,6 +24,3 @@ pub export fn entry3() void { // :13:13: error: unable to evaluate comptime expression // :13:16: note: operation is runtime due to this operand // :13:13: note: initializer of container-level variable must be comptime-known -// :22:9: error: unable to evaluate comptime expression -// :22:21: note: operation is runtime due to this operand -// :21:13: note: enum field values must be comptime-known diff --git a/test/cases/compile_errors/undef_arith_is_illegal.zig b/test/cases/compile_errors/undef_arith_is_illegal.zig @@ -192,50 +192,30 @@ const std = @import("std"); // :65:17: error: use of undefined value here causes illegal behavior // :65:17: error: use of undefined value here causes illegal behavior // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: error: use of undefined value here causes illegal behavior // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: error: use of undefined value here causes illegal behavior -// :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' @@ -244,10 +224,6 @@ const std = @import("std"); // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' -// :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' -// :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' @@ -256,7 +232,9 @@ const std = @import("std"); // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior @@ -266,9 +244,9 @@ const std = @import("std"); // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior @@ -278,7 +256,9 @@ const std = @import("std"); // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior @@ -288,9 +268,9 @@ const std = @import("std"); // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior @@ -300,7 +280,9 @@ const std = @import("std"); // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior @@ -310,9 +292,9 @@ const std = @import("std"); // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior @@ -322,7 +304,9 @@ const std = @import("std"); // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior @@ -332,9 +316,9 @@ const std = @import("std"); // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior @@ -344,7 +328,9 @@ const std = @import("std"); // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior @@ -354,9 +340,9 @@ const std = @import("std"); // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior @@ -366,7 +352,9 @@ const std = @import("std"); // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior @@ -376,9 +364,9 @@ const std = @import("std"); // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '1' +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior @@ -388,7 +376,9 @@ const std = @import("std"); // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '0' // :65:17: error: use of undefined value here causes illegal behavior @@ -402,35 +392,45 @@ const std = @import("std"); // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' +// :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' +// :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' +// :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' +// :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' +// :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' +// :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' +// :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' +// :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior // :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' +// :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' +// :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' +// :65:17: note: when computing vector element at index '1' // :65:17: error: use of undefined value here causes illegal behavior -// :65:17: note: when computing vector element at index '0' +// :65:17: note: when computing vector element at index '1' +// :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '1' +// :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '1' +// :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '1' +// :65:17: error: use of undefined value here causes illegal behavior +// :65:17: note: when computing vector element at index '1' // :65:21: error: use of undefined value here causes illegal behavior // :65:21: note: when computing vector element at index '0' // :65:21: error: use of undefined value here causes illegal behavior @@ -478,50 +478,30 @@ const std = @import("std"); // :69:27: error: use of undefined value here causes illegal behavior // :69:27: error: use of undefined value here causes illegal behavior // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: error: use of undefined value here causes illegal behavior // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: error: use of undefined value here causes illegal behavior -// :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' @@ -530,10 +510,6 @@ const std = @import("std"); // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' -// :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' -// :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' @@ -542,7 +518,9 @@ const std = @import("std"); // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior @@ -552,9 +530,9 @@ const std = @import("std"); // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior @@ -564,7 +542,9 @@ const std = @import("std"); // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior @@ -574,9 +554,9 @@ const std = @import("std"); // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior @@ -586,7 +566,9 @@ const std = @import("std"); // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior @@ -596,9 +578,9 @@ const std = @import("std"); // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior @@ -608,7 +590,9 @@ const std = @import("std"); // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior @@ -618,9 +602,9 @@ const std = @import("std"); // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior @@ -630,7 +614,9 @@ const std = @import("std"); // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior @@ -640,9 +626,9 @@ const std = @import("std"); // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior @@ -652,7 +638,9 @@ const std = @import("std"); // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior @@ -662,9 +650,9 @@ const std = @import("std"); // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '1' +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior @@ -674,7 +662,9 @@ const std = @import("std"); // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '0' // :69:27: error: use of undefined value here causes illegal behavior @@ -688,35 +678,45 @@ const std = @import("std"); // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' +// :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' +// :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' +// :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' +// :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' +// :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' +// :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' +// :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' +// :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior // :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' +// :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' +// :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' +// :69:27: note: when computing vector element at index '1' // :69:27: error: use of undefined value here causes illegal behavior -// :69:27: note: when computing vector element at index '0' +// :69:27: note: when computing vector element at index '1' +// :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '1' +// :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '1' +// :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '1' +// :69:27: error: use of undefined value here causes illegal behavior +// :69:27: note: when computing vector element at index '1' // :69:30: error: use of undefined value here causes illegal behavior // :69:30: note: when computing vector element at index '0' // :69:30: error: use of undefined value here causes illegal behavior @@ -764,50 +764,30 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: error: use of undefined value here causes illegal behavior // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: error: use of undefined value here causes illegal behavior // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: error: use of undefined value here causes illegal behavior -// :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' @@ -816,10 +796,6 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' -// :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' -// :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' @@ -828,7 +804,9 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior @@ -838,9 +816,9 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior @@ -850,7 +828,9 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior @@ -860,9 +840,9 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior @@ -872,7 +852,9 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior @@ -882,9 +864,9 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior @@ -894,7 +876,9 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior @@ -904,9 +888,9 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior @@ -916,7 +900,9 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior @@ -926,9 +912,9 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior @@ -938,7 +924,9 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior @@ -948,9 +936,9 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior @@ -960,7 +948,9 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior @@ -974,35 +964,45 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' +// :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '1' +// :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '1' +// :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '1' +// :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '1' // :73:30: error: use of undefined value here causes illegal behavior // :73:30: note: when computing vector element at index '0' // :73:30: error: use of undefined value here causes illegal behavior @@ -1050,50 +1050,30 @@ const std = @import("std"); // :77:27: error: use of undefined value here causes illegal behavior // :77:27: error: use of undefined value here causes illegal behavior // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: error: use of undefined value here causes illegal behavior // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: error: use of undefined value here causes illegal behavior -// :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' @@ -1102,10 +1082,6 @@ const std = @import("std"); // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' -// :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' -// :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' @@ -1114,7 +1090,9 @@ const std = @import("std"); // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior @@ -1124,9 +1102,9 @@ const std = @import("std"); // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior @@ -1136,7 +1114,9 @@ const std = @import("std"); // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior @@ -1146,9 +1126,9 @@ const std = @import("std"); // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior @@ -1158,7 +1138,9 @@ const std = @import("std"); // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior @@ -1168,9 +1150,9 @@ const std = @import("std"); // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior @@ -1180,7 +1162,9 @@ const std = @import("std"); // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior @@ -1190,9 +1174,9 @@ const std = @import("std"); // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior @@ -1202,7 +1186,9 @@ const std = @import("std"); // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior @@ -1212,9 +1198,9 @@ const std = @import("std"); // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior @@ -1224,7 +1210,9 @@ const std = @import("std"); // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior @@ -1234,9 +1222,9 @@ const std = @import("std"); // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '1' +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior @@ -1246,7 +1234,9 @@ const std = @import("std"); // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '0' // :77:27: error: use of undefined value here causes illegal behavior @@ -1260,35 +1250,45 @@ const std = @import("std"); // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' +// :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' +// :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' +// :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' +// :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' +// :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' +// :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' +// :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' +// :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior // :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' +// :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' +// :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' +// :77:27: note: when computing vector element at index '1' // :77:27: error: use of undefined value here causes illegal behavior -// :77:27: note: when computing vector element at index '0' +// :77:27: note: when computing vector element at index '1' +// :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '1' +// :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '1' +// :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '1' +// :77:27: error: use of undefined value here causes illegal behavior +// :77:27: note: when computing vector element at index '1' // :77:30: error: use of undefined value here causes illegal behavior // :77:30: note: when computing vector element at index '0' // :77:30: error: use of undefined value here causes illegal behavior @@ -1336,50 +1336,30 @@ const std = @import("std"); // :81:17: error: use of undefined value here causes illegal behavior // :81:17: error: use of undefined value here causes illegal behavior // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: error: use of undefined value here causes illegal behavior // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: error: use of undefined value here causes illegal behavior -// :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' @@ -1388,10 +1368,6 @@ const std = @import("std"); // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' -// :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' -// :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' @@ -1400,7 +1376,9 @@ const std = @import("std"); // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior @@ -1410,9 +1388,9 @@ const std = @import("std"); // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior @@ -1422,7 +1400,9 @@ const std = @import("std"); // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior @@ -1432,9 +1412,9 @@ const std = @import("std"); // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior @@ -1444,7 +1424,9 @@ const std = @import("std"); // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior @@ -1454,9 +1436,9 @@ const std = @import("std"); // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior @@ -1466,7 +1448,9 @@ const std = @import("std"); // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior @@ -1476,9 +1460,9 @@ const std = @import("std"); // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior @@ -1488,7 +1472,9 @@ const std = @import("std"); // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior @@ -1498,9 +1484,9 @@ const std = @import("std"); // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior @@ -1510,7 +1496,9 @@ const std = @import("std"); // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior @@ -1520,9 +1508,9 @@ const std = @import("std"); // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '1' +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior @@ -1532,7 +1520,9 @@ const std = @import("std"); // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '0' // :81:17: error: use of undefined value here causes illegal behavior @@ -1546,35 +1536,45 @@ const std = @import("std"); // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' +// :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' +// :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' +// :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' +// :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' +// :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' +// :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' +// :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' +// :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior // :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' +// :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' +// :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' +// :81:17: note: when computing vector element at index '1' // :81:17: error: use of undefined value here causes illegal behavior -// :81:17: note: when computing vector element at index '0' +// :81:17: note: when computing vector element at index '1' +// :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '1' +// :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '1' +// :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '1' +// :81:17: error: use of undefined value here causes illegal behavior +// :81:17: note: when computing vector element at index '1' // :81:21: error: use of undefined value here causes illegal behavior // :81:21: note: when computing vector element at index '0' // :81:21: error: use of undefined value here causes illegal behavior @@ -1622,39 +1622,25 @@ const std = @import("std"); // :85:22: error: use of undefined value here causes illegal behavior // :85:22: error: use of undefined value here causes illegal behavior // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: error: use of undefined value here causes illegal behavior // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' +// :85:22: error: use of undefined value here causes illegal behavior +// :85:22: error: use of undefined value here causes illegal behavior // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior @@ -1664,7 +1650,9 @@ const std = @import("std"); // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior @@ -1674,9 +1662,9 @@ const std = @import("std"); // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior @@ -1686,7 +1674,9 @@ const std = @import("std"); // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior @@ -1696,9 +1686,9 @@ const std = @import("std"); // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior @@ -1708,7 +1698,9 @@ const std = @import("std"); // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior @@ -1718,10 +1710,6 @@ const std = @import("std"); // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' -// :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' -// :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' @@ -1730,8 +1718,6 @@ const std = @import("std"); // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: error: use of undefined value here causes illegal behavior -// :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' @@ -1740,10 +1726,6 @@ const std = @import("std"); // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' -// :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' -// :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' @@ -1752,7 +1734,9 @@ const std = @import("std"); // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior @@ -1762,9 +1746,9 @@ const std = @import("std"); // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior @@ -1774,7 +1758,9 @@ const std = @import("std"); // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior @@ -1784,9 +1770,9 @@ const std = @import("std"); // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior @@ -1796,7 +1782,9 @@ const std = @import("std"); // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior @@ -1806,9 +1794,9 @@ const std = @import("std"); // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '1' +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior @@ -1818,7 +1806,9 @@ const std = @import("std"); // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '0' // :85:22: error: use of undefined value here causes illegal behavior @@ -1832,35 +1822,45 @@ const std = @import("std"); // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' +// :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' +// :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' +// :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' +// :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' +// :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' +// :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' +// :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' +// :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior // :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' +// :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' +// :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' +// :85:22: note: when computing vector element at index '1' // :85:22: error: use of undefined value here causes illegal behavior -// :85:22: note: when computing vector element at index '0' +// :85:22: note: when computing vector element at index '1' +// :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '1' +// :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '1' +// :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '1' +// :85:22: error: use of undefined value here causes illegal behavior +// :85:22: note: when computing vector element at index '1' // :85:25: error: use of undefined value here causes illegal behavior // :85:25: note: when computing vector element at index '0' // :85:25: error: use of undefined value here causes illegal behavior @@ -1908,50 +1908,30 @@ const std = @import("std"); // :89:22: error: use of undefined value here causes illegal behavior // :89:22: error: use of undefined value here causes illegal behavior // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: error: use of undefined value here causes illegal behavior // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: error: use of undefined value here causes illegal behavior -// :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' @@ -1960,10 +1940,6 @@ const std = @import("std"); // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' -// :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' -// :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' @@ -1972,7 +1948,9 @@ const std = @import("std"); // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior @@ -1982,9 +1960,9 @@ const std = @import("std"); // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior @@ -1994,7 +1972,9 @@ const std = @import("std"); // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior @@ -2004,9 +1984,9 @@ const std = @import("std"); // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior @@ -2016,7 +1996,9 @@ const std = @import("std"); // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior @@ -2026,9 +2008,9 @@ const std = @import("std"); // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior @@ -2038,7 +2020,9 @@ const std = @import("std"); // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior @@ -2048,9 +2032,9 @@ const std = @import("std"); // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior @@ -2060,7 +2044,9 @@ const std = @import("std"); // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior @@ -2070,9 +2056,9 @@ const std = @import("std"); // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior @@ -2082,7 +2068,9 @@ const std = @import("std"); // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior @@ -2092,9 +2080,9 @@ const std = @import("std"); // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '1' +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior @@ -2104,7 +2092,9 @@ const std = @import("std"); // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '0' // :89:22: error: use of undefined value here causes illegal behavior @@ -2118,35 +2108,45 @@ const std = @import("std"); // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' +// :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' +// :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' +// :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' +// :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' +// :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' +// :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' +// :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' +// :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior // :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' +// :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' +// :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' +// :89:22: note: when computing vector element at index '1' // :89:22: error: use of undefined value here causes illegal behavior -// :89:22: note: when computing vector element at index '0' +// :89:22: note: when computing vector element at index '1' +// :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '1' +// :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '1' +// :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '1' +// :89:22: error: use of undefined value here causes illegal behavior +// :89:22: note: when computing vector element at index '1' // :89:25: error: use of undefined value here causes illegal behavior // :89:25: note: when computing vector element at index '0' // :89:25: error: use of undefined value here causes illegal behavior @@ -2198,21 +2198,13 @@ const std = @import("std"); // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior @@ -2220,21 +2212,13 @@ const std = @import("std"); // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior @@ -2242,21 +2226,13 @@ const std = @import("std"); // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior @@ -2264,21 +2240,13 @@ const std = @import("std"); // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior @@ -2286,13 +2254,9 @@ const std = @import("std"); // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior // :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior @@ -2302,19 +2266,21 @@ const std = @import("std"); // :95:17: error: use of undefined value here causes illegal behavior // :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' -// :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' -// :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior // :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior @@ -2324,19 +2290,25 @@ const std = @import("std"); // :95:17: error: use of undefined value here causes illegal behavior // :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior // :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior @@ -2346,19 +2318,25 @@ const std = @import("std"); // :95:17: error: use of undefined value here causes illegal behavior // :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '1' +// :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior // :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior @@ -2368,11 +2346,17 @@ const std = @import("std"); // :95:17: error: use of undefined value here causes illegal behavior // :95:17: note: when computing vector element at index '0' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior // :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior @@ -2382,19 +2366,25 @@ const std = @import("std"); // :95:17: error: use of undefined value here causes illegal behavior // :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior // :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior @@ -2404,19 +2394,25 @@ const std = @import("std"); // :95:17: error: use of undefined value here causes illegal behavior // :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior // :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior @@ -2426,13 +2422,17 @@ const std = @import("std"); // :95:17: error: use of undefined value here causes illegal behavior // :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' +// :95:17: note: when computing vector element at index '1' // :95:17: error: use of undefined value here causes illegal behavior -// :95:17: note: when computing vector element at index '0' +// :95:17: note: when computing vector element at index '1' +// :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' +// :95:17: error: use of undefined value here causes illegal behavior +// :95:17: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior @@ -2440,21 +2440,13 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior @@ -2462,21 +2454,13 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior @@ -2484,21 +2468,13 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior @@ -2506,21 +2482,13 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior @@ -2528,13 +2496,9 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior @@ -2544,19 +2508,21 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' -// :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' -// :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior @@ -2566,19 +2532,25 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior @@ -2588,19 +2560,25 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior @@ -2610,11 +2588,17 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior @@ -2624,19 +2608,25 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior @@ -2646,19 +2636,25 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior @@ -2668,13 +2664,17 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' +// :99:27: note: when computing vector element at index '1' +// :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' +// :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior @@ -2682,21 +2682,13 @@ const std = @import("std"); // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior @@ -2704,21 +2696,13 @@ const std = @import("std"); // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior @@ -2726,21 +2710,13 @@ const std = @import("std"); // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior @@ -2748,21 +2724,13 @@ const std = @import("std"); // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior @@ -2770,13 +2738,9 @@ const std = @import("std"); // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior // :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior @@ -2786,19 +2750,21 @@ const std = @import("std"); // :103:27: error: use of undefined value here causes illegal behavior // :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' -// :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' -// :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior // :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior @@ -2808,19 +2774,25 @@ const std = @import("std"); // :103:27: error: use of undefined value here causes illegal behavior // :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior // :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior @@ -2830,19 +2802,25 @@ const std = @import("std"); // :103:27: error: use of undefined value here causes illegal behavior // :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '1' +// :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior // :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior @@ -2852,11 +2830,17 @@ const std = @import("std"); // :103:27: error: use of undefined value here causes illegal behavior // :103:27: note: when computing vector element at index '0' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior // :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior @@ -2866,19 +2850,25 @@ const std = @import("std"); // :103:27: error: use of undefined value here causes illegal behavior // :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior // :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior @@ -2888,19 +2878,25 @@ const std = @import("std"); // :103:27: error: use of undefined value here causes illegal behavior // :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior // :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior @@ -2910,13 +2906,17 @@ const std = @import("std"); // :103:27: error: use of undefined value here causes illegal behavior // :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' +// :103:27: note: when computing vector element at index '1' // :103:27: error: use of undefined value here causes illegal behavior -// :103:27: note: when computing vector element at index '0' +// :103:27: note: when computing vector element at index '1' +// :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' +// :103:27: error: use of undefined value here causes illegal behavior +// :103:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior @@ -2924,21 +2924,13 @@ const std = @import("std"); // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior @@ -2946,21 +2938,13 @@ const std = @import("std"); // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior @@ -2968,21 +2952,13 @@ const std = @import("std"); // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior @@ -2990,21 +2966,13 @@ const std = @import("std"); // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior @@ -3012,13 +2980,9 @@ const std = @import("std"); // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior // :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior @@ -3028,19 +2992,21 @@ const std = @import("std"); // :107:27: error: use of undefined value here causes illegal behavior // :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' -// :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' -// :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior // :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior @@ -3050,19 +3016,25 @@ const std = @import("std"); // :107:27: error: use of undefined value here causes illegal behavior // :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior // :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior @@ -3072,19 +3044,25 @@ const std = @import("std"); // :107:27: error: use of undefined value here causes illegal behavior // :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '1' +// :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior // :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior @@ -3094,11 +3072,17 @@ const std = @import("std"); // :107:27: error: use of undefined value here causes illegal behavior // :107:27: note: when computing vector element at index '0' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior // :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior @@ -3108,19 +3092,25 @@ const std = @import("std"); // :107:27: error: use of undefined value here causes illegal behavior // :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior // :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior @@ -3130,19 +3120,29 @@ const std = @import("std"); // :107:27: error: use of undefined value here causes illegal behavior // :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' +// :107:27: note: when computing vector element at index '1' +// :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' +// :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior // :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior @@ -3152,13 +3152,13 @@ const std = @import("std"); // :107:27: error: use of undefined value here causes illegal behavior // :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' +// :107:27: note: when computing vector element at index '1' // :107:27: error: use of undefined value here causes illegal behavior -// :107:27: note: when computing vector element at index '0' +// :107:27: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior @@ -3166,21 +3166,13 @@ const std = @import("std"); // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior @@ -3188,21 +3180,13 @@ const std = @import("std"); // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior @@ -3210,21 +3194,13 @@ const std = @import("std"); // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior @@ -3232,21 +3208,13 @@ const std = @import("std"); // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior @@ -3254,13 +3222,9 @@ const std = @import("std"); // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior // :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior @@ -3270,19 +3234,21 @@ const std = @import("std"); // :111:22: error: use of undefined value here causes illegal behavior // :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' -// :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' -// :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior // :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior @@ -3292,19 +3258,25 @@ const std = @import("std"); // :111:22: error: use of undefined value here causes illegal behavior // :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior // :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior @@ -3314,19 +3286,25 @@ const std = @import("std"); // :111:22: error: use of undefined value here causes illegal behavior // :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '1' +// :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior // :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior @@ -3336,11 +3314,17 @@ const std = @import("std"); // :111:22: error: use of undefined value here causes illegal behavior // :111:22: note: when computing vector element at index '0' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior // :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior @@ -3350,19 +3334,25 @@ const std = @import("std"); // :111:22: error: use of undefined value here causes illegal behavior // :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior // :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior @@ -3372,19 +3362,25 @@ const std = @import("std"); // :111:22: error: use of undefined value here causes illegal behavior // :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior // :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior @@ -3394,13 +3390,17 @@ const std = @import("std"); // :111:22: error: use of undefined value here causes illegal behavior // :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' +// :111:22: note: when computing vector element at index '1' // :111:22: error: use of undefined value here causes illegal behavior -// :111:22: note: when computing vector element at index '0' +// :111:22: note: when computing vector element at index '1' +// :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' +// :111:22: error: use of undefined value here causes illegal behavior +// :111:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior @@ -3408,21 +3408,13 @@ const std = @import("std"); // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior @@ -3430,21 +3422,13 @@ const std = @import("std"); // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior @@ -3452,21 +3436,13 @@ const std = @import("std"); // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior @@ -3474,21 +3450,13 @@ const std = @import("std"); // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior @@ -3496,13 +3464,9 @@ const std = @import("std"); // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior // :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior @@ -3512,19 +3476,21 @@ const std = @import("std"); // :115:22: error: use of undefined value here causes illegal behavior // :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' -// :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' -// :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior // :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior @@ -3534,19 +3500,25 @@ const std = @import("std"); // :115:22: error: use of undefined value here causes illegal behavior // :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior // :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior @@ -3556,19 +3528,25 @@ const std = @import("std"); // :115:22: error: use of undefined value here causes illegal behavior // :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '1' +// :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior // :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior @@ -3578,11 +3556,17 @@ const std = @import("std"); // :115:22: error: use of undefined value here causes illegal behavior // :115:22: note: when computing vector element at index '0' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior // :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior @@ -3592,19 +3576,25 @@ const std = @import("std"); // :115:22: error: use of undefined value here causes illegal behavior // :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior // :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior @@ -3614,19 +3604,27 @@ const std = @import("std"); // :115:22: error: use of undefined value here causes illegal behavior // :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' +// :115:22: note: when computing vector element at index '1' +// :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior // :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior @@ -3636,37 +3634,32 @@ const std = @import("std"); // :115:22: error: use of undefined value here causes illegal behavior // :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' +// :115:22: note: when computing vector element at index '1' // :115:22: error: use of undefined value here causes illegal behavior -// :115:22: note: when computing vector element at index '0' +// :115:22: note: when computing vector element at index '1' +// :115:22: error: use of undefined value here causes illegal behavior +// :115:22: note: when computing vector element at index '1' +// :121:17: error: use of undefined value here causes illegal behavior // :121:17: error: use of undefined value here causes illegal behavior // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '1' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '1' +// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior @@ -3674,6 +3667,7 @@ const std = @import("std"); // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior +// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior @@ -3681,7 +3675,7 @@ const std = @import("std"); // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '1' +// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior @@ -3689,6 +3683,7 @@ const std = @import("std"); // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior +// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior @@ -3696,7 +3691,7 @@ const std = @import("std"); // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '1' +// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior @@ -3704,6 +3699,7 @@ const std = @import("std"); // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior +// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior @@ -3711,7 +3707,7 @@ const std = @import("std"); // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '1' +// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior @@ -3719,6 +3715,7 @@ const std = @import("std"); // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior +// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior @@ -3726,7 +3723,7 @@ const std = @import("std"); // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '1' +// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior @@ -3734,6 +3731,7 @@ const std = @import("std"); // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior +// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior @@ -3741,7 +3739,7 @@ const std = @import("std"); // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '1' +// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior @@ -3749,6 +3747,7 @@ const std = @import("std"); // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior +// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior @@ -3756,7 +3755,7 @@ const std = @import("std"); // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '1' +// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior @@ -3764,6 +3763,7 @@ const std = @import("std"); // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior +// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior @@ -3771,7 +3771,7 @@ const std = @import("std"); // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '1' +// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior @@ -3779,6 +3779,7 @@ const std = @import("std"); // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior +// :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '0' // :121:17: error: use of undefined value here causes illegal behavior @@ -3788,126 +3789,120 @@ const std = @import("std"); // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '1' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '0' -// :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '0' +// :121:17: note: when computing vector element at index '1' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '0' +// :121:17: note: when computing vector element at index '1' // :121:17: error: use of undefined value here causes illegal behavior +// :121:17: note: when computing vector element at index '1' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '0' +// :121:17: note: when computing vector element at index '1' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '0' +// :121:17: note: when computing vector element at index '1' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '0' +// :121:17: note: when computing vector element at index '1' // :121:17: error: use of undefined value here causes illegal behavior // :121:17: note: when computing vector element at index '1' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '0' +// :121:17: note: when computing vector element at index '1' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '0' +// :121:17: note: when computing vector element at index '1' // :121:17: error: use of undefined value here causes illegal behavior -// :121:17: note: when computing vector element at index '0' +// :121:17: note: when computing vector element at index '1' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '1' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '1' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '1' -// :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior +// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '1' +// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior +// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '1' +// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior +// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '1' +// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior +// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '1' +// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior +// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '1' +// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior +// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '1' +// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior +// :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '0' // :121:21: error: use of undefined value here causes illegal behavior @@ -3915,44 +3910,42 @@ const std = @import("std"); // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '1' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '0' +// :121:21: note: when computing vector element at index '1' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '0' +// :121:21: note: when computing vector element at index '1' // :121:21: error: use of undefined value here causes illegal behavior +// :121:21: note: when computing vector element at index '1' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '0' +// :121:21: note: when computing vector element at index '1' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '0' +// :121:21: note: when computing vector element at index '1' // :121:21: error: use of undefined value here causes illegal behavior // :121:21: note: when computing vector element at index '1' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '0' +// :121:21: note: when computing vector element at index '1' // :121:21: error: use of undefined value here causes illegal behavior -// :121:21: note: when computing vector element at index '0' +// :121:21: note: when computing vector element at index '1' +// :121:21: error: use of undefined value here causes illegal behavior +// :121:21: note: when computing vector element at index '1' +// :121:21: error: use of undefined value here causes illegal behavior +// :121:21: note: when computing vector element at index '1' +// :125:27: error: use of undefined value here causes illegal behavior // :125:27: error: use of undefined value here causes illegal behavior // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '1' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '1' +// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior @@ -3960,6 +3953,7 @@ const std = @import("std"); // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior +// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior @@ -3967,7 +3961,7 @@ const std = @import("std"); // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '1' +// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior @@ -3975,6 +3969,7 @@ const std = @import("std"); // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior +// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior @@ -3982,7 +3977,7 @@ const std = @import("std"); // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '1' +// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior @@ -3990,6 +3985,7 @@ const std = @import("std"); // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior +// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior @@ -3997,7 +3993,7 @@ const std = @import("std"); // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '1' +// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior @@ -4005,6 +4001,7 @@ const std = @import("std"); // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior +// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior @@ -4012,7 +4009,7 @@ const std = @import("std"); // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '1' +// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior @@ -4020,6 +4017,7 @@ const std = @import("std"); // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior +// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior @@ -4027,7 +4025,7 @@ const std = @import("std"); // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '1' +// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior @@ -4035,6 +4033,7 @@ const std = @import("std"); // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior +// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior @@ -4042,7 +4041,7 @@ const std = @import("std"); // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '1' +// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior @@ -4050,6 +4049,7 @@ const std = @import("std"); // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior +// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior @@ -4057,7 +4057,7 @@ const std = @import("std"); // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '1' +// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior @@ -4065,6 +4065,7 @@ const std = @import("std"); // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior +// :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '0' // :125:27: error: use of undefined value here causes illegal behavior @@ -4074,126 +4075,120 @@ const std = @import("std"); // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '1' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '0' -// :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '0' +// :125:27: note: when computing vector element at index '1' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '0' +// :125:27: note: when computing vector element at index '1' // :125:27: error: use of undefined value here causes illegal behavior +// :125:27: note: when computing vector element at index '1' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '0' +// :125:27: note: when computing vector element at index '1' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '0' +// :125:27: note: when computing vector element at index '1' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '0' +// :125:27: note: when computing vector element at index '1' // :125:27: error: use of undefined value here causes illegal behavior // :125:27: note: when computing vector element at index '1' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '0' +// :125:27: note: when computing vector element at index '1' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '0' +// :125:27: note: when computing vector element at index '1' // :125:27: error: use of undefined value here causes illegal behavior -// :125:27: note: when computing vector element at index '0' +// :125:27: note: when computing vector element at index '1' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '1' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '1' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '1' -// :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior +// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '1' +// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior +// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '1' +// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior +// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '1' +// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior +// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '1' +// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior +// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '1' +// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior +// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '1' +// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior +// :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '0' // :125:30: error: use of undefined value here causes illegal behavior @@ -4201,44 +4196,42 @@ const std = @import("std"); // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '1' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '0' +// :125:30: note: when computing vector element at index '1' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '0' +// :125:30: note: when computing vector element at index '1' // :125:30: error: use of undefined value here causes illegal behavior +// :125:30: note: when computing vector element at index '1' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '0' +// :125:30: note: when computing vector element at index '1' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '0' +// :125:30: note: when computing vector element at index '1' // :125:30: error: use of undefined value here causes illegal behavior // :125:30: note: when computing vector element at index '1' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '0' +// :125:30: note: when computing vector element at index '1' // :125:30: error: use of undefined value here causes illegal behavior -// :125:30: note: when computing vector element at index '0' +// :125:30: note: when computing vector element at index '1' +// :125:30: error: use of undefined value here causes illegal behavior +// :125:30: note: when computing vector element at index '1' +// :125:30: error: use of undefined value here causes illegal behavior +// :125:30: note: when computing vector element at index '1' +// :129:27: error: use of undefined value here causes illegal behavior // :129:27: error: use of undefined value here causes illegal behavior // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '1' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '1' +// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior @@ -4246,6 +4239,7 @@ const std = @import("std"); // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior +// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior @@ -4253,7 +4247,7 @@ const std = @import("std"); // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '1' +// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior @@ -4261,6 +4255,7 @@ const std = @import("std"); // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior +// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior @@ -4268,7 +4263,7 @@ const std = @import("std"); // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '1' +// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior @@ -4276,6 +4271,7 @@ const std = @import("std"); // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior +// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior @@ -4283,7 +4279,7 @@ const std = @import("std"); // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '1' +// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior @@ -4291,6 +4287,7 @@ const std = @import("std"); // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior +// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior @@ -4298,7 +4295,7 @@ const std = @import("std"); // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '1' +// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior @@ -4306,6 +4303,7 @@ const std = @import("std"); // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior +// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior @@ -4313,7 +4311,7 @@ const std = @import("std"); // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '1' +// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior @@ -4321,6 +4319,7 @@ const std = @import("std"); // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior +// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior @@ -4328,7 +4327,7 @@ const std = @import("std"); // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '1' +// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior @@ -4336,6 +4335,7 @@ const std = @import("std"); // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior +// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior @@ -4343,7 +4343,7 @@ const std = @import("std"); // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '1' +// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior @@ -4351,6 +4351,7 @@ const std = @import("std"); // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior +// :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '0' // :129:27: error: use of undefined value here causes illegal behavior @@ -4360,126 +4361,120 @@ const std = @import("std"); // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '1' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '0' -// :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '0' +// :129:27: note: when computing vector element at index '1' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '0' +// :129:27: note: when computing vector element at index '1' // :129:27: error: use of undefined value here causes illegal behavior +// :129:27: note: when computing vector element at index '1' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '0' +// :129:27: note: when computing vector element at index '1' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '0' +// :129:27: note: when computing vector element at index '1' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '0' +// :129:27: note: when computing vector element at index '1' // :129:27: error: use of undefined value here causes illegal behavior // :129:27: note: when computing vector element at index '1' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '0' +// :129:27: note: when computing vector element at index '1' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '0' +// :129:27: note: when computing vector element at index '1' // :129:27: error: use of undefined value here causes illegal behavior -// :129:27: note: when computing vector element at index '0' +// :129:27: note: when computing vector element at index '1' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '1' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '1' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '1' -// :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior +// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '1' +// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior +// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '1' +// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior +// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '1' +// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior +// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '1' +// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior +// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '1' +// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior +// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '1' +// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior +// :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '0' // :129:30: error: use of undefined value here causes illegal behavior @@ -4487,44 +4482,42 @@ const std = @import("std"); // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '1' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '0' +// :129:30: note: when computing vector element at index '1' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '0' +// :129:30: note: when computing vector element at index '1' +// :129:30: error: use of undefined value here causes illegal behavior +// :129:30: note: when computing vector element at index '1' // :129:30: error: use of undefined value here causes illegal behavior +// :129:30: note: when computing vector element at index '1' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '0' +// :129:30: note: when computing vector element at index '1' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '0' +// :129:30: note: when computing vector element at index '1' // :129:30: error: use of undefined value here causes illegal behavior // :129:30: note: when computing vector element at index '1' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '0' +// :129:30: note: when computing vector element at index '1' // :129:30: error: use of undefined value here causes illegal behavior -// :129:30: note: when computing vector element at index '0' +// :129:30: note: when computing vector element at index '1' +// :129:30: error: use of undefined value here causes illegal behavior +// :129:30: note: when computing vector element at index '1' +// :133:27: error: use of undefined value here causes illegal behavior // :133:27: error: use of undefined value here causes illegal behavior // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '1' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '1' +// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior @@ -4532,6 +4525,7 @@ const std = @import("std"); // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior +// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior @@ -4539,7 +4533,7 @@ const std = @import("std"); // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '1' +// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior @@ -4547,6 +4541,7 @@ const std = @import("std"); // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior +// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior @@ -4554,7 +4549,7 @@ const std = @import("std"); // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '1' +// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior @@ -4562,6 +4557,7 @@ const std = @import("std"); // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior +// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior @@ -4569,7 +4565,7 @@ const std = @import("std"); // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '1' +// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior @@ -4577,6 +4573,7 @@ const std = @import("std"); // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior +// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior @@ -4584,7 +4581,7 @@ const std = @import("std"); // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '1' +// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior @@ -4592,6 +4589,7 @@ const std = @import("std"); // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior +// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior @@ -4599,7 +4597,7 @@ const std = @import("std"); // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '1' +// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior @@ -4607,6 +4605,7 @@ const std = @import("std"); // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior +// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior @@ -4614,7 +4613,7 @@ const std = @import("std"); // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '1' +// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior @@ -4622,6 +4621,7 @@ const std = @import("std"); // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior +// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior @@ -4629,7 +4629,7 @@ const std = @import("std"); // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '1' +// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior @@ -4637,6 +4637,7 @@ const std = @import("std"); // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior +// :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '0' // :133:27: error: use of undefined value here causes illegal behavior @@ -4646,126 +4647,120 @@ const std = @import("std"); // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '1' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '0' -// :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '0' +// :133:27: note: when computing vector element at index '1' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '0' +// :133:27: note: when computing vector element at index '1' // :133:27: error: use of undefined value here causes illegal behavior +// :133:27: note: when computing vector element at index '1' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '0' +// :133:27: note: when computing vector element at index '1' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '0' +// :133:27: note: when computing vector element at index '1' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '0' +// :133:27: note: when computing vector element at index '1' // :133:27: error: use of undefined value here causes illegal behavior // :133:27: note: when computing vector element at index '1' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '0' +// :133:27: note: when computing vector element at index '1' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '0' +// :133:27: note: when computing vector element at index '1' // :133:27: error: use of undefined value here causes illegal behavior -// :133:27: note: when computing vector element at index '0' +// :133:27: note: when computing vector element at index '1' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '1' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '1' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '1' -// :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior +// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '1' +// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior +// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '1' +// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior +// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '1' +// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior +// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '1' +// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior +// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '1' +// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior +// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '1' +// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior +// :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '0' // :133:30: error: use of undefined value here causes illegal behavior @@ -4773,44 +4768,42 @@ const std = @import("std"); // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '1' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '0' +// :133:30: note: when computing vector element at index '1' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '0' +// :133:30: note: when computing vector element at index '1' // :133:30: error: use of undefined value here causes illegal behavior +// :133:30: note: when computing vector element at index '1' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '0' +// :133:30: note: when computing vector element at index '1' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '0' +// :133:30: note: when computing vector element at index '1' // :133:30: error: use of undefined value here causes illegal behavior // :133:30: note: when computing vector element at index '1' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '0' +// :133:30: note: when computing vector element at index '1' // :133:30: error: use of undefined value here causes illegal behavior -// :133:30: note: when computing vector element at index '0' +// :133:30: note: when computing vector element at index '1' +// :133:30: error: use of undefined value here causes illegal behavior +// :133:30: note: when computing vector element at index '1' +// :133:30: error: use of undefined value here causes illegal behavior +// :133:30: note: when computing vector element at index '1' +// :137:17: error: use of undefined value here causes illegal behavior // :137:17: error: use of undefined value here causes illegal behavior // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '1' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '1' +// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior @@ -4818,6 +4811,7 @@ const std = @import("std"); // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior +// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior @@ -4825,7 +4819,7 @@ const std = @import("std"); // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '1' +// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior @@ -4833,6 +4827,7 @@ const std = @import("std"); // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior +// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior @@ -4840,7 +4835,7 @@ const std = @import("std"); // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '1' +// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior @@ -4848,6 +4843,7 @@ const std = @import("std"); // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior +// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior @@ -4855,7 +4851,7 @@ const std = @import("std"); // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '1' +// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior @@ -4863,6 +4859,7 @@ const std = @import("std"); // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior +// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior @@ -4870,7 +4867,7 @@ const std = @import("std"); // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '1' +// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior @@ -4878,6 +4875,7 @@ const std = @import("std"); // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior +// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior @@ -4885,7 +4883,7 @@ const std = @import("std"); // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '1' +// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior @@ -4893,6 +4891,7 @@ const std = @import("std"); // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior +// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior @@ -4900,7 +4899,7 @@ const std = @import("std"); // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '1' +// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior @@ -4908,6 +4907,7 @@ const std = @import("std"); // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior +// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior @@ -4915,7 +4915,7 @@ const std = @import("std"); // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '1' +// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior @@ -4923,6 +4923,7 @@ const std = @import("std"); // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior +// :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '0' // :137:17: error: use of undefined value here causes illegal behavior @@ -4932,126 +4933,120 @@ const std = @import("std"); // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '1' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '0' -// :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '0' +// :137:17: note: when computing vector element at index '1' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '0' +// :137:17: note: when computing vector element at index '1' // :137:17: error: use of undefined value here causes illegal behavior +// :137:17: note: when computing vector element at index '1' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '0' +// :137:17: note: when computing vector element at index '1' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '0' +// :137:17: note: when computing vector element at index '1' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '0' +// :137:17: note: when computing vector element at index '1' // :137:17: error: use of undefined value here causes illegal behavior // :137:17: note: when computing vector element at index '1' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '0' +// :137:17: note: when computing vector element at index '1' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '0' +// :137:17: note: when computing vector element at index '1' // :137:17: error: use of undefined value here causes illegal behavior -// :137:17: note: when computing vector element at index '0' +// :137:17: note: when computing vector element at index '1' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '1' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '1' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '1' -// :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior +// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '1' +// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior +// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '1' +// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior +// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '1' +// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior +// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '1' +// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior +// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '1' +// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior +// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '1' +// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior +// :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '0' // :137:21: error: use of undefined value here causes illegal behavior @@ -5059,44 +5054,42 @@ const std = @import("std"); // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '1' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '0' +// :137:21: note: when computing vector element at index '1' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '0' +// :137:21: note: when computing vector element at index '1' // :137:21: error: use of undefined value here causes illegal behavior +// :137:21: note: when computing vector element at index '1' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '0' +// :137:21: note: when computing vector element at index '1' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '0' +// :137:21: note: when computing vector element at index '1' // :137:21: error: use of undefined value here causes illegal behavior // :137:21: note: when computing vector element at index '1' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '0' +// :137:21: note: when computing vector element at index '1' // :137:21: error: use of undefined value here causes illegal behavior -// :137:21: note: when computing vector element at index '0' +// :137:21: note: when computing vector element at index '1' +// :137:21: error: use of undefined value here causes illegal behavior +// :137:21: note: when computing vector element at index '1' +// :137:21: error: use of undefined value here causes illegal behavior +// :137:21: note: when computing vector element at index '1' +// :141:22: error: use of undefined value here causes illegal behavior // :141:22: error: use of undefined value here causes illegal behavior // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '1' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '1' +// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior @@ -5104,6 +5097,7 @@ const std = @import("std"); // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior +// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior @@ -5111,7 +5105,7 @@ const std = @import("std"); // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '1' +// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior @@ -5119,6 +5113,7 @@ const std = @import("std"); // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior +// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior @@ -5126,7 +5121,7 @@ const std = @import("std"); // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '1' +// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior @@ -5134,6 +5129,7 @@ const std = @import("std"); // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior +// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior @@ -5141,7 +5137,7 @@ const std = @import("std"); // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '1' +// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior @@ -5149,6 +5145,7 @@ const std = @import("std"); // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior +// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior @@ -5156,7 +5153,7 @@ const std = @import("std"); // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '1' +// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior @@ -5164,6 +5161,7 @@ const std = @import("std"); // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior +// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior @@ -5171,7 +5169,7 @@ const std = @import("std"); // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '1' +// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior @@ -5179,6 +5177,7 @@ const std = @import("std"); // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior +// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior @@ -5186,7 +5185,7 @@ const std = @import("std"); // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '1' +// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior @@ -5194,6 +5193,7 @@ const std = @import("std"); // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior +// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior @@ -5201,7 +5201,7 @@ const std = @import("std"); // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '1' +// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior @@ -5209,6 +5209,7 @@ const std = @import("std"); // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior +// :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '0' // :141:22: error: use of undefined value here causes illegal behavior @@ -5218,126 +5219,120 @@ const std = @import("std"); // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '1' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '0' -// :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '0' +// :141:22: note: when computing vector element at index '1' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '0' +// :141:22: note: when computing vector element at index '1' // :141:22: error: use of undefined value here causes illegal behavior +// :141:22: note: when computing vector element at index '1' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '0' +// :141:22: note: when computing vector element at index '1' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '0' +// :141:22: note: when computing vector element at index '1' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '0' +// :141:22: note: when computing vector element at index '1' // :141:22: error: use of undefined value here causes illegal behavior // :141:22: note: when computing vector element at index '1' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '0' +// :141:22: note: when computing vector element at index '1' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '0' +// :141:22: note: when computing vector element at index '1' // :141:22: error: use of undefined value here causes illegal behavior -// :141:22: note: when computing vector element at index '0' +// :141:22: note: when computing vector element at index '1' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '1' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '1' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '1' -// :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior +// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '1' +// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior +// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '1' +// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior +// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '1' +// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior +// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '1' +// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior +// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '1' +// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior +// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '1' +// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior +// :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '0' // :141:25: error: use of undefined value here causes illegal behavior @@ -5345,44 +5340,42 @@ const std = @import("std"); // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '1' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '0' +// :141:25: note: when computing vector element at index '1' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '0' +// :141:25: note: when computing vector element at index '1' // :141:25: error: use of undefined value here causes illegal behavior +// :141:25: note: when computing vector element at index '1' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '0' +// :141:25: note: when computing vector element at index '1' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '0' +// :141:25: note: when computing vector element at index '1' // :141:25: error: use of undefined value here causes illegal behavior // :141:25: note: when computing vector element at index '1' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '0' +// :141:25: note: when computing vector element at index '1' // :141:25: error: use of undefined value here causes illegal behavior -// :141:25: note: when computing vector element at index '0' +// :141:25: note: when computing vector element at index '1' +// :141:25: error: use of undefined value here causes illegal behavior +// :141:25: note: when computing vector element at index '1' +// :141:25: error: use of undefined value here causes illegal behavior +// :141:25: note: when computing vector element at index '1' +// :145:22: error: use of undefined value here causes illegal behavior // :145:22: error: use of undefined value here causes illegal behavior // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '1' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '1' +// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior @@ -5390,6 +5383,7 @@ const std = @import("std"); // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior +// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior @@ -5397,7 +5391,7 @@ const std = @import("std"); // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '1' +// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior @@ -5405,6 +5399,7 @@ const std = @import("std"); // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior +// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior @@ -5412,7 +5407,7 @@ const std = @import("std"); // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '1' +// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior @@ -5420,6 +5415,7 @@ const std = @import("std"); // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior +// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior @@ -5427,7 +5423,7 @@ const std = @import("std"); // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '1' +// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior @@ -5435,6 +5431,7 @@ const std = @import("std"); // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior +// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior @@ -5442,7 +5439,7 @@ const std = @import("std"); // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '1' +// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior @@ -5450,6 +5447,7 @@ const std = @import("std"); // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior +// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior @@ -5457,7 +5455,7 @@ const std = @import("std"); // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '1' +// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior @@ -5465,6 +5463,7 @@ const std = @import("std"); // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior +// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior @@ -5472,7 +5471,7 @@ const std = @import("std"); // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '1' +// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior @@ -5480,6 +5479,7 @@ const std = @import("std"); // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior +// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior @@ -5487,7 +5487,7 @@ const std = @import("std"); // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '1' +// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior @@ -5495,6 +5495,7 @@ const std = @import("std"); // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior +// :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '0' // :145:22: error: use of undefined value here causes illegal behavior @@ -5504,126 +5505,120 @@ const std = @import("std"); // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '1' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '0' -// :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '0' +// :145:22: note: when computing vector element at index '1' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '0' +// :145:22: note: when computing vector element at index '1' // :145:22: error: use of undefined value here causes illegal behavior +// :145:22: note: when computing vector element at index '1' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '0' +// :145:22: note: when computing vector element at index '1' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '0' +// :145:22: note: when computing vector element at index '1' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '0' +// :145:22: note: when computing vector element at index '1' // :145:22: error: use of undefined value here causes illegal behavior // :145:22: note: when computing vector element at index '1' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '0' +// :145:22: note: when computing vector element at index '1' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '0' +// :145:22: note: when computing vector element at index '1' // :145:22: error: use of undefined value here causes illegal behavior -// :145:22: note: when computing vector element at index '0' +// :145:22: note: when computing vector element at index '1' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '1' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '1' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '1' -// :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior +// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '1' +// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior +// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '1' +// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior +// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '1' +// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior +// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '1' +// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior +// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '1' +// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior +// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '1' +// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior +// :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '0' // :145:25: error: use of undefined value here causes illegal behavior @@ -5631,20 +5626,25 @@ const std = @import("std"); // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '1' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '0' +// :145:25: note: when computing vector element at index '1' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '0' +// :145:25: note: when computing vector element at index '1' +// :145:25: error: use of undefined value here causes illegal behavior +// :145:25: note: when computing vector element at index '1' // :145:25: error: use of undefined value here causes illegal behavior +// :145:25: note: when computing vector element at index '1' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '0' +// :145:25: note: when computing vector element at index '1' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '0' +// :145:25: note: when computing vector element at index '1' // :145:25: error: use of undefined value here causes illegal behavior // :145:25: note: when computing vector element at index '1' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '0' +// :145:25: note: when computing vector element at index '1' // :145:25: error: use of undefined value here causes illegal behavior -// :145:25: note: when computing vector element at index '0' +// :145:25: note: when computing vector element at index '1' +// :145:25: error: use of undefined value here causes illegal behavior +// :145:25: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior @@ -5652,21 +5652,13 @@ const std = @import("std"); // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior @@ -5674,21 +5666,13 @@ const std = @import("std"); // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior @@ -5696,21 +5680,13 @@ const std = @import("std"); // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior @@ -5718,21 +5694,13 @@ const std = @import("std"); // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior @@ -5740,13 +5708,9 @@ const std = @import("std"); // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior // :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior @@ -5756,19 +5720,21 @@ const std = @import("std"); // :151:21: error: use of undefined value here causes illegal behavior // :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' -// :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' -// :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior // :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior @@ -5778,19 +5744,25 @@ const std = @import("std"); // :151:21: error: use of undefined value here causes illegal behavior // :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior // :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior @@ -5800,19 +5772,25 @@ const std = @import("std"); // :151:21: error: use of undefined value here causes illegal behavior // :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '1' +// :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior // :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior @@ -5822,11 +5800,17 @@ const std = @import("std"); // :151:21: error: use of undefined value here causes illegal behavior // :151:21: note: when computing vector element at index '0' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior // :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior @@ -5836,19 +5820,25 @@ const std = @import("std"); // :151:21: error: use of undefined value here causes illegal behavior // :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior // :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior @@ -5858,19 +5848,25 @@ const std = @import("std"); // :151:21: error: use of undefined value here causes illegal behavior // :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior // :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior @@ -5880,13 +5876,17 @@ const std = @import("std"); // :151:21: error: use of undefined value here causes illegal behavior // :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' +// :151:21: note: when computing vector element at index '1' // :151:21: error: use of undefined value here causes illegal behavior -// :151:21: note: when computing vector element at index '0' +// :151:21: note: when computing vector element at index '1' +// :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' +// :151:21: error: use of undefined value here causes illegal behavior +// :151:21: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior @@ -5894,21 +5894,13 @@ const std = @import("std"); // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior @@ -5916,21 +5908,13 @@ const std = @import("std"); // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior @@ -5938,21 +5922,13 @@ const std = @import("std"); // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior @@ -5960,21 +5936,13 @@ const std = @import("std"); // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior @@ -5982,13 +5950,9 @@ const std = @import("std"); // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior // :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior @@ -5998,19 +5962,21 @@ const std = @import("std"); // :155:30: error: use of undefined value here causes illegal behavior // :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' -// :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' -// :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior // :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior @@ -6020,19 +5986,25 @@ const std = @import("std"); // :155:30: error: use of undefined value here causes illegal behavior // :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior // :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior @@ -6042,19 +6014,25 @@ const std = @import("std"); // :155:30: error: use of undefined value here causes illegal behavior // :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '1' +// :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior // :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior @@ -6064,11 +6042,17 @@ const std = @import("std"); // :155:30: error: use of undefined value here causes illegal behavior // :155:30: note: when computing vector element at index '0' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior // :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior @@ -6078,19 +6062,25 @@ const std = @import("std"); // :155:30: error: use of undefined value here causes illegal behavior // :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior // :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior @@ -6100,19 +6090,25 @@ const std = @import("std"); // :155:30: error: use of undefined value here causes illegal behavior // :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior // :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior @@ -6122,13 +6118,17 @@ const std = @import("std"); // :155:30: error: use of undefined value here causes illegal behavior // :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' +// :155:30: note: when computing vector element at index '1' // :155:30: error: use of undefined value here causes illegal behavior -// :155:30: note: when computing vector element at index '0' +// :155:30: note: when computing vector element at index '1' +// :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' +// :155:30: error: use of undefined value here causes illegal behavior +// :155:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior @@ -6136,21 +6136,13 @@ const std = @import("std"); // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior @@ -6158,21 +6150,13 @@ const std = @import("std"); // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior @@ -6180,21 +6164,13 @@ const std = @import("std"); // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior @@ -6202,21 +6178,13 @@ const std = @import("std"); // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior @@ -6224,13 +6192,9 @@ const std = @import("std"); // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior // :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior @@ -6240,19 +6204,21 @@ const std = @import("std"); // :159:30: error: use of undefined value here causes illegal behavior // :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' -// :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' -// :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior // :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior @@ -6262,19 +6228,25 @@ const std = @import("std"); // :159:30: error: use of undefined value here causes illegal behavior // :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior // :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior @@ -6284,19 +6256,25 @@ const std = @import("std"); // :159:30: error: use of undefined value here causes illegal behavior // :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '1' +// :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior // :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior @@ -6306,11 +6284,17 @@ const std = @import("std"); // :159:30: error: use of undefined value here causes illegal behavior // :159:30: note: when computing vector element at index '0' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior // :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior @@ -6320,19 +6304,25 @@ const std = @import("std"); // :159:30: error: use of undefined value here causes illegal behavior // :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior // :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior @@ -6342,19 +6332,27 @@ const std = @import("std"); // :159:30: error: use of undefined value here causes illegal behavior // :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' +// :159:30: note: when computing vector element at index '1' +// :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior // :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior @@ -6364,13 +6362,15 @@ const std = @import("std"); // :159:30: error: use of undefined value here causes illegal behavior // :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' +// :159:30: note: when computing vector element at index '1' // :159:30: error: use of undefined value here causes illegal behavior -// :159:30: note: when computing vector element at index '0' +// :159:30: note: when computing vector element at index '1' +// :159:30: error: use of undefined value here causes illegal behavior +// :159:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior @@ -6378,21 +6378,13 @@ const std = @import("std"); // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior @@ -6400,21 +6392,13 @@ const std = @import("std"); // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior @@ -6422,21 +6406,13 @@ const std = @import("std"); // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior @@ -6444,21 +6420,13 @@ const std = @import("std"); // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior @@ -6466,13 +6434,9 @@ const std = @import("std"); // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior // :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior @@ -6482,19 +6446,21 @@ const std = @import("std"); // :163:30: error: use of undefined value here causes illegal behavior // :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' -// :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' -// :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior // :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior @@ -6504,19 +6470,25 @@ const std = @import("std"); // :163:30: error: use of undefined value here causes illegal behavior // :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior // :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior @@ -6526,19 +6498,25 @@ const std = @import("std"); // :163:30: error: use of undefined value here causes illegal behavior // :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '1' +// :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior // :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior @@ -6548,11 +6526,19 @@ const std = @import("std"); // :163:30: error: use of undefined value here causes illegal behavior // :163:30: note: when computing vector element at index '0' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' +// :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior // :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior @@ -6562,19 +6548,27 @@ const std = @import("std"); // :163:30: error: use of undefined value here causes illegal behavior // :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' +// :163:30: note: when computing vector element at index '1' +// :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior // :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior @@ -6584,19 +6578,25 @@ const std = @import("std"); // :163:30: error: use of undefined value here causes illegal behavior // :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior // :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior @@ -6606,13 +6606,13 @@ const std = @import("std"); // :163:30: error: use of undefined value here causes illegal behavior // :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' +// :163:30: note: when computing vector element at index '1' // :163:30: error: use of undefined value here causes illegal behavior -// :163:30: note: when computing vector element at index '0' +// :163:30: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior @@ -6620,21 +6620,13 @@ const std = @import("std"); // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior @@ -6642,21 +6634,13 @@ const std = @import("std"); // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior @@ -6664,21 +6648,13 @@ const std = @import("std"); // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior @@ -6686,21 +6662,13 @@ const std = @import("std"); // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior @@ -6708,13 +6676,9 @@ const std = @import("std"); // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior // :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior @@ -6724,19 +6688,21 @@ const std = @import("std"); // :167:25: error: use of undefined value here causes illegal behavior // :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' -// :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' -// :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior // :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior @@ -6746,19 +6712,25 @@ const std = @import("std"); // :167:25: error: use of undefined value here causes illegal behavior // :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior // :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior @@ -6768,19 +6740,25 @@ const std = @import("std"); // :167:25: error: use of undefined value here causes illegal behavior // :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '1' +// :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior // :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior @@ -6790,11 +6768,21 @@ const std = @import("std"); // :167:25: error: use of undefined value here causes illegal behavior // :167:25: note: when computing vector element at index '0' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' +// :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' +// :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior // :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior @@ -6804,19 +6792,25 @@ const std = @import("std"); // :167:25: error: use of undefined value here causes illegal behavior // :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior // :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior @@ -6826,19 +6820,25 @@ const std = @import("std"); // :167:25: error: use of undefined value here causes illegal behavior // :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior // :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior @@ -6848,13 +6848,13 @@ const std = @import("std"); // :167:25: error: use of undefined value here causes illegal behavior // :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' +// :167:25: note: when computing vector element at index '1' // :167:25: error: use of undefined value here causes illegal behavior -// :167:25: note: when computing vector element at index '0' +// :167:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior @@ -6862,21 +6862,13 @@ const std = @import("std"); // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior @@ -6884,21 +6876,13 @@ const std = @import("std"); // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior @@ -6906,21 +6890,13 @@ const std = @import("std"); // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior @@ -6928,21 +6904,13 @@ const std = @import("std"); // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior @@ -6950,13 +6918,9 @@ const std = @import("std"); // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior // :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior @@ -6966,19 +6930,21 @@ const std = @import("std"); // :171:25: error: use of undefined value here causes illegal behavior // :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' -// :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' -// :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior // :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior @@ -6988,19 +6954,25 @@ const std = @import("std"); // :171:25: error: use of undefined value here causes illegal behavior // :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior // :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior @@ -7010,19 +6982,25 @@ const std = @import("std"); // :171:25: error: use of undefined value here causes illegal behavior // :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '1' +// :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior // :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior @@ -7032,11 +7010,17 @@ const std = @import("std"); // :171:25: error: use of undefined value here causes illegal behavior // :171:25: note: when computing vector element at index '0' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior // :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior @@ -7046,19 +7030,25 @@ const std = @import("std"); // :171:25: error: use of undefined value here causes illegal behavior // :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior // :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior @@ -7068,19 +7058,25 @@ const std = @import("std"); // :171:25: error: use of undefined value here causes illegal behavior // :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior // :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior @@ -7090,37 +7086,33 @@ const std = @import("std"); // :171:25: error: use of undefined value here causes illegal behavior // :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' +// :171:25: note: when computing vector element at index '1' // :171:25: error: use of undefined value here causes illegal behavior -// :171:25: note: when computing vector element at index '0' +// :171:25: note: when computing vector element at index '1' +// :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' +// :171:25: error: use of undefined value here causes illegal behavior +// :171:25: note: when computing vector element at index '1' // :177:17: error: use of undefined value here causes illegal behavior // :177:17: error: use of undefined value here causes illegal behavior // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '1' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '1' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior +// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior +// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior @@ -7130,9 +7122,9 @@ const std = @import("std"); // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '1' +// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '1' +// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior @@ -7142,7 +7134,9 @@ const std = @import("std"); // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior +// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior +// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior @@ -7152,9 +7146,9 @@ const std = @import("std"); // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '1' +// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '1' +// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior @@ -7164,7 +7158,9 @@ const std = @import("std"); // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior +// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior +// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior @@ -7174,9 +7170,9 @@ const std = @import("std"); // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '1' +// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '1' +// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior @@ -7186,7 +7182,9 @@ const std = @import("std"); // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior +// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior +// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior @@ -7196,9 +7194,9 @@ const std = @import("std"); // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '1' +// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '1' +// :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior @@ -7208,27 +7206,29 @@ const std = @import("std"); // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '0' // :177:17: error: use of undefined value here causes illegal behavior +// :177:17: note: when computing vector element at index '1' // :177:17: error: use of undefined value here causes illegal behavior +// :177:17: note: when computing vector element at index '1' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '0' +// :177:17: note: when computing vector element at index '1' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '0' +// :177:17: note: when computing vector element at index '1' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '0' +// :177:17: note: when computing vector element at index '1' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '0' +// :177:17: note: when computing vector element at index '1' // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '1' // :177:17: error: use of undefined value here causes illegal behavior // :177:17: note: when computing vector element at index '1' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '0' +// :177:17: note: when computing vector element at index '1' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '0' +// :177:17: note: when computing vector element at index '1' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '0' +// :177:17: note: when computing vector element at index '1' // :177:17: error: use of undefined value here causes illegal behavior -// :177:17: note: when computing vector element at index '0' +// :177:17: note: when computing vector element at index '1' // :177:21: error: use of undefined value here causes illegal behavior // :177:21: note: when computing vector element at index '0' // :177:21: error: use of undefined value here causes illegal behavior @@ -7256,27 +7256,19 @@ const std = @import("std"); // :180:17: error: use of undefined value here causes illegal behavior // :180:17: error: use of undefined value here causes illegal behavior // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '1' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '1' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior +// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior +// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior @@ -7286,9 +7278,9 @@ const std = @import("std"); // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '1' +// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '1' +// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior @@ -7298,7 +7290,9 @@ const std = @import("std"); // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior +// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior +// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior @@ -7308,9 +7302,9 @@ const std = @import("std"); // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '1' +// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '1' +// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior @@ -7320,7 +7314,9 @@ const std = @import("std"); // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior +// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior +// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior @@ -7330,9 +7326,9 @@ const std = @import("std"); // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '1' +// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '1' +// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior @@ -7342,7 +7338,9 @@ const std = @import("std"); // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior +// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior +// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior @@ -7352,9 +7350,9 @@ const std = @import("std"); // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '1' +// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '1' +// :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior @@ -7364,27 +7362,29 @@ const std = @import("std"); // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '0' // :180:17: error: use of undefined value here causes illegal behavior +// :180:17: note: when computing vector element at index '1' // :180:17: error: use of undefined value here causes illegal behavior +// :180:17: note: when computing vector element at index '1' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '0' +// :180:17: note: when computing vector element at index '1' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '0' +// :180:17: note: when computing vector element at index '1' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '0' +// :180:17: note: when computing vector element at index '1' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '0' +// :180:17: note: when computing vector element at index '1' // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '1' // :180:17: error: use of undefined value here causes illegal behavior // :180:17: note: when computing vector element at index '1' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '0' +// :180:17: note: when computing vector element at index '1' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '0' +// :180:17: note: when computing vector element at index '1' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '0' +// :180:17: note: when computing vector element at index '1' // :180:17: error: use of undefined value here causes illegal behavior -// :180:17: note: when computing vector element at index '0' +// :180:17: note: when computing vector element at index '1' // :180:21: error: use of undefined value here causes illegal behavior // :180:21: note: when computing vector element at index '0' // :180:21: error: use of undefined value here causes illegal behavior @@ -7412,27 +7412,19 @@ const std = @import("std"); // :183:17: error: use of undefined value here causes illegal behavior // :183:17: error: use of undefined value here causes illegal behavior // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '1' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '1' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior +// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior +// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior @@ -7442,9 +7434,9 @@ const std = @import("std"); // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '1' +// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '1' +// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior @@ -7454,7 +7446,9 @@ const std = @import("std"); // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior +// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior +// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior @@ -7464,9 +7458,9 @@ const std = @import("std"); // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '1' +// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '1' +// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior @@ -7476,7 +7470,9 @@ const std = @import("std"); // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior +// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior +// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior @@ -7486,9 +7482,9 @@ const std = @import("std"); // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '1' +// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '1' +// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior @@ -7498,7 +7494,9 @@ const std = @import("std"); // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior +// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior +// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior @@ -7508,9 +7506,9 @@ const std = @import("std"); // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '1' +// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '1' +// :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior @@ -7520,27 +7518,29 @@ const std = @import("std"); // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '0' // :183:17: error: use of undefined value here causes illegal behavior +// :183:17: note: when computing vector element at index '1' // :183:17: error: use of undefined value here causes illegal behavior +// :183:17: note: when computing vector element at index '1' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '0' +// :183:17: note: when computing vector element at index '1' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '0' +// :183:17: note: when computing vector element at index '1' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '0' +// :183:17: note: when computing vector element at index '1' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '0' +// :183:17: note: when computing vector element at index '1' // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '1' // :183:17: error: use of undefined value here causes illegal behavior // :183:17: note: when computing vector element at index '1' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '0' +// :183:17: note: when computing vector element at index '1' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '0' +// :183:17: note: when computing vector element at index '1' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '0' +// :183:17: note: when computing vector element at index '1' // :183:17: error: use of undefined value here causes illegal behavior -// :183:17: note: when computing vector element at index '0' +// :183:17: note: when computing vector element at index '1' // :183:21: error: use of undefined value here causes illegal behavior // :183:21: note: when computing vector element at index '0' // :183:21: error: use of undefined value here causes illegal behavior diff --git a/test/cases/compile_errors/undef_arith_returns_undef.zig b/test/cases/compile_errors/undef_arith_returns_undef.zig @@ -681,1800 +681,1360 @@ inline fn testFloatWithValue(comptime Float: type, x: Float) void { // @as(@Vector(2, u8), undefined) // @as(@Vector(2, u8), [runtime value]) // @as(@Vector(2, u8), [runtime value]) -// @as(i8, undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), .{ 6, undefined }) -// @as(@Vector(2, i8), .{ undefined, 6 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ 6, undefined }) -// @as(@Vector(2, i8), .{ 6, undefined }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ undefined, 6 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ undefined, 6 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), .{ 6, undefined }) -// @as(@Vector(2, i8), .{ undefined, 6 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ 6, undefined }) -// @as(@Vector(2, i8), .{ 6, undefined }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ undefined, 6 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ undefined, 6 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), .{ 0, undefined }) -// @as(@Vector(2, i8), .{ undefined, 0 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ 0, undefined }) -// @as(@Vector(2, i8), .{ 0, undefined }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ undefined, 0 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ undefined, 0 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), .{ 0, undefined }) -// @as(@Vector(2, i8), .{ undefined, 0 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ 0, undefined }) -// @as(@Vector(2, i8), .{ 0, undefined }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ undefined, 0 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ undefined, 0 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), .{ 9, undefined }) -// @as(@Vector(2, i8), .{ undefined, 9 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ 9, undefined }) -// @as(@Vector(2, i8), .{ 9, undefined }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ undefined, 9 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ undefined, 9 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), .{ 9, undefined }) -// @as(@Vector(2, i8), .{ undefined, 9 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ 9, undefined }) -// @as(@Vector(2, i8), .{ 9, undefined }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ undefined, 9 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ undefined, 9 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), .{ 0, undefined }) -// @as(@Vector(2, i8), .{ undefined, 0 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ 0, undefined }) -// @as(@Vector(2, i8), .{ 0, undefined }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ undefined, 0 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), .{ undefined, 0 }) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, [runtime value]) -// @as(i8, [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), [runtime value]) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), undefined) -// @as(@Vector(2, i8), undefined) -// @as(i8, undefined) -// @as(@Vector(2, i8), undefined) -// @as(u32, undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), .{ 6, undefined }) -// @as(@Vector(2, u32), .{ undefined, 6 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ 6, undefined }) -// @as(@Vector(2, u32), .{ 6, undefined }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ undefined, 6 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ undefined, 6 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), .{ 6, undefined }) -// @as(@Vector(2, u32), .{ undefined, 6 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ 6, undefined }) -// @as(@Vector(2, u32), .{ 6, undefined }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ undefined, 6 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ undefined, 6 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), .{ 0, undefined }) -// @as(@Vector(2, u32), .{ undefined, 0 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ 0, undefined }) -// @as(@Vector(2, u32), .{ 0, undefined }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ undefined, 0 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ undefined, 0 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), .{ 0, undefined }) -// @as(@Vector(2, u32), .{ undefined, 0 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ 0, undefined }) -// @as(@Vector(2, u32), .{ 0, undefined }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ undefined, 0 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ undefined, 0 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), .{ 9, undefined }) -// @as(@Vector(2, u32), .{ undefined, 9 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ 9, undefined }) -// @as(@Vector(2, u32), .{ 9, undefined }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ undefined, 9 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ undefined, 9 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), .{ 9, undefined }) -// @as(@Vector(2, u32), .{ undefined, 9 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ 9, undefined }) -// @as(@Vector(2, u32), .{ 9, undefined }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ undefined, 9 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ undefined, 9 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), .{ 24, undefined }) -// @as(@Vector(2, u32), .{ undefined, 24 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ 24, undefined }) -// @as(@Vector(2, u32), .{ 24, undefined }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ undefined, 24 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ undefined, 24 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), .{ 0, undefined }) -// @as(@Vector(2, u32), .{ undefined, 0 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ 0, undefined }) -// @as(@Vector(2, u32), .{ 0, undefined }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ undefined, 0 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), .{ undefined, 0 }) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(u1, undefined) -// @as(@Vector(2, u1), .{ 1, undefined }) -// @as(@Vector(2, u1), .{ undefined, 1 }) -// @as(@Vector(2, u1), undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, [runtime value]) -// @as(u32, [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), [runtime value]) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), undefined) -// @as(u32, undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), undefined) -// @as(@Vector(2, u32), undefined) -// @as(u1, undefined) -// @as(@Vector(2, u1), [runtime value]) -// @as(@Vector(2, u1), [runtime value]) -// @as(@Vector(2, u1), undefined) -// @as(u32, undefined) -// @as(@Vector(2, u32), undefined) -// @as(i32, undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), .{ 6, undefined }) -// @as(@Vector(2, i32), .{ undefined, 6 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ 6, undefined }) -// @as(@Vector(2, i32), .{ 6, undefined }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ undefined, 6 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ undefined, 6 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), .{ 6, undefined }) -// @as(@Vector(2, i32), .{ undefined, 6 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ 6, undefined }) -// @as(@Vector(2, i32), .{ 6, undefined }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ undefined, 6 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ undefined, 6 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), .{ 0, undefined }) -// @as(@Vector(2, i32), .{ undefined, 0 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ 0, undefined }) -// @as(@Vector(2, i32), .{ 0, undefined }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ undefined, 0 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ undefined, 0 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), .{ 0, undefined }) -// @as(@Vector(2, i32), .{ undefined, 0 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ 0, undefined }) -// @as(@Vector(2, i32), .{ 0, undefined }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ undefined, 0 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ undefined, 0 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), .{ 9, undefined }) -// @as(@Vector(2, i32), .{ undefined, 9 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ 9, undefined }) -// @as(@Vector(2, i32), .{ 9, undefined }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ undefined, 9 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ undefined, 9 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), .{ 9, undefined }) -// @as(@Vector(2, i32), .{ undefined, 9 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ 9, undefined }) -// @as(@Vector(2, i32), .{ 9, undefined }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ undefined, 9 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ undefined, 9 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), .{ 0, undefined }) -// @as(@Vector(2, i32), .{ undefined, 0 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ 0, undefined }) -// @as(@Vector(2, i32), .{ 0, undefined }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ undefined, 0 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), .{ undefined, 0 }) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, [runtime value]) -// @as(i32, [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), [runtime value]) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), undefined) -// @as(@Vector(2, i32), undefined) -// @as(i32, undefined) -// @as(@Vector(2, i32), undefined) -// @as(u500, undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), .{ 6, undefined }) -// @as(@Vector(2, u500), .{ undefined, 6 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ 6, undefined }) -// @as(@Vector(2, u500), .{ 6, undefined }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ undefined, 6 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ undefined, 6 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), .{ 6, undefined }) -// @as(@Vector(2, u500), .{ undefined, 6 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ 6, undefined }) -// @as(@Vector(2, u500), .{ 6, undefined }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ undefined, 6 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ undefined, 6 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), .{ 0, undefined }) -// @as(@Vector(2, u500), .{ undefined, 0 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ 0, undefined }) -// @as(@Vector(2, u500), .{ 0, undefined }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ undefined, 0 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ undefined, 0 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), .{ 0, undefined }) -// @as(@Vector(2, u500), .{ undefined, 0 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ 0, undefined }) -// @as(@Vector(2, u500), .{ 0, undefined }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ undefined, 0 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ undefined, 0 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), .{ 9, undefined }) -// @as(@Vector(2, u500), .{ undefined, 9 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ 9, undefined }) -// @as(@Vector(2, u500), .{ 9, undefined }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ undefined, 9 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ undefined, 9 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), .{ 9, undefined }) -// @as(@Vector(2, u500), .{ undefined, 9 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ 9, undefined }) -// @as(@Vector(2, u500), .{ 9, undefined }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ undefined, 9 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ undefined, 9 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), .{ 24, undefined }) -// @as(@Vector(2, u500), .{ undefined, 24 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ 24, undefined }) -// @as(@Vector(2, u500), .{ 24, undefined }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ undefined, 24 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ undefined, 24 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), .{ 0, undefined }) -// @as(@Vector(2, u500), .{ undefined, 0 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ 0, undefined }) -// @as(@Vector(2, u500), .{ 0, undefined }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ undefined, 0 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), .{ undefined, 0 }) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(u1, undefined) -// @as(@Vector(2, u1), .{ 1, undefined }) -// @as(@Vector(2, u1), .{ undefined, 1 }) -// @as(@Vector(2, u1), undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, [runtime value]) -// @as(u500, [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), [runtime value]) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), undefined) -// @as(u500, undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), undefined) -// @as(@Vector(2, u500), undefined) -// @as(u1, undefined) -// @as(@Vector(2, u1), [runtime value]) -// @as(@Vector(2, u1), [runtime value]) -// @as(@Vector(2, u1), undefined) -// @as(u500, undefined) -// @as(@Vector(2, u500), undefined) -// @as(i500, undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), .{ 6, undefined }) -// @as(@Vector(2, i500), .{ undefined, 6 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ 6, undefined }) -// @as(@Vector(2, i500), .{ 6, undefined }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ undefined, 6 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ undefined, 6 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), .{ 6, undefined }) -// @as(@Vector(2, i500), .{ undefined, 6 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ 6, undefined }) -// @as(@Vector(2, i500), .{ 6, undefined }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ undefined, 6 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ undefined, 6 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), .{ 0, undefined }) -// @as(@Vector(2, i500), .{ undefined, 0 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ 0, undefined }) -// @as(@Vector(2, i500), .{ 0, undefined }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ undefined, 0 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ undefined, 0 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), .{ 0, undefined }) -// @as(@Vector(2, i500), .{ undefined, 0 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ 0, undefined }) -// @as(@Vector(2, i500), .{ 0, undefined }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ undefined, 0 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ undefined, 0 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), .{ 9, undefined }) -// @as(@Vector(2, i500), .{ undefined, 9 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ 9, undefined }) -// @as(@Vector(2, i500), .{ 9, undefined }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ undefined, 9 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ undefined, 9 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), .{ 9, undefined }) -// @as(@Vector(2, i500), .{ undefined, 9 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ 9, undefined }) -// @as(@Vector(2, i500), .{ 9, undefined }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ undefined, 9 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ undefined, 9 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), .{ 0, undefined }) -// @as(@Vector(2, i500), .{ undefined, 0 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ 0, undefined }) -// @as(@Vector(2, i500), .{ 0, undefined }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ undefined, 0 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), .{ undefined, 0 }) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, [runtime value]) -// @as(i500, [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), [runtime value]) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), undefined) -// @as(@Vector(2, i500), undefined) -// @as(i500, undefined) -// @as(@Vector(2, i500), undefined) -// @as(f16, undefined) -// @as(f16, undefined) -// @as(@Vector(2, f16), .{ 6, undefined }) -// @as(@Vector(2, f16), .{ undefined, 6 }) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), .{ 6, undefined }) -// @as(@Vector(2, f16), .{ 6, undefined }) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), .{ undefined, 6 }) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), .{ undefined, 6 }) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(f16, undefined) -// @as(f16, undefined) -// @as(@Vector(2, f16), .{ 0, undefined }) -// @as(@Vector(2, f16), .{ undefined, 0 }) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), .{ 0, undefined }) -// @as(@Vector(2, f16), .{ 0, undefined }) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), .{ undefined, 0 }) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), .{ undefined, 0 }) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(f16, undefined) -// @as(f16, undefined) -// @as(@Vector(2, f16), .{ 9, undefined }) -// @as(@Vector(2, f16), .{ undefined, 9 }) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), .{ 9, undefined }) -// @as(@Vector(2, f16), .{ 9, undefined }) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), .{ undefined, 9 }) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), .{ undefined, 9 }) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(f16, undefined) -// @as(@Vector(2, f16), .{ -3, undefined }) -// @as(@Vector(2, f16), .{ undefined, -3 }) -// @as(@Vector(2, f16), undefined) -// @as(f16, undefined) -// @as(f16, undefined) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(f16, undefined) -// @as(f16, undefined) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(f16, undefined) -// @as(f16, undefined) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(@Vector(2, f16), undefined) -// @as(f16, undefined) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), [runtime value]) -// @as(@Vector(2, f16), undefined) -// @as(f32, undefined) -// @as(f32, undefined) -// @as(@Vector(2, f32), .{ 6, undefined }) -// @as(@Vector(2, f32), .{ undefined, 6 }) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), .{ 6, undefined }) -// @as(@Vector(2, f32), .{ 6, undefined }) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), .{ undefined, 6 }) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), .{ undefined, 6 }) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(f32, undefined) -// @as(f32, undefined) -// @as(@Vector(2, f32), .{ 0, undefined }) -// @as(@Vector(2, f32), .{ undefined, 0 }) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), .{ 0, undefined }) -// @as(@Vector(2, f32), .{ 0, undefined }) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), .{ undefined, 0 }) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), .{ undefined, 0 }) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(f32, undefined) -// @as(f32, undefined) -// @as(@Vector(2, f32), .{ 9, undefined }) -// @as(@Vector(2, f32), .{ undefined, 9 }) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), .{ 9, undefined }) -// @as(@Vector(2, f32), .{ 9, undefined }) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), .{ undefined, 9 }) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), .{ undefined, 9 }) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(f32, undefined) -// @as(@Vector(2, f32), .{ -3, undefined }) -// @as(@Vector(2, f32), .{ undefined, -3 }) -// @as(@Vector(2, f32), undefined) -// @as(f32, undefined) -// @as(f32, undefined) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(f32, undefined) -// @as(f32, undefined) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(f32, undefined) -// @as(f32, undefined) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(@Vector(2, f32), undefined) -// @as(f32, undefined) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), [runtime value]) -// @as(@Vector(2, f32), undefined) -// @as(f64, undefined) -// @as(f64, undefined) -// @as(@Vector(2, f64), .{ 6, undefined }) -// @as(@Vector(2, f64), .{ undefined, 6 }) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), .{ 6, undefined }) -// @as(@Vector(2, f64), .{ 6, undefined }) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), .{ undefined, 6 }) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), .{ undefined, 6 }) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(f64, undefined) -// @as(f64, undefined) -// @as(@Vector(2, f64), .{ 0, undefined }) -// @as(@Vector(2, f64), .{ undefined, 0 }) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), .{ 0, undefined }) -// @as(@Vector(2, f64), .{ 0, undefined }) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), .{ undefined, 0 }) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), .{ undefined, 0 }) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(f64, undefined) -// @as(f64, undefined) -// @as(@Vector(2, f64), .{ 9, undefined }) -// @as(@Vector(2, f64), .{ undefined, 9 }) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), .{ 9, undefined }) -// @as(@Vector(2, f64), .{ 9, undefined }) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), .{ undefined, 9 }) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), .{ undefined, 9 }) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(f64, undefined) -// @as(@Vector(2, f64), .{ -3, undefined }) -// @as(@Vector(2, f64), .{ undefined, -3 }) -// @as(@Vector(2, f64), undefined) -// @as(f64, undefined) -// @as(f64, undefined) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(f64, undefined) -// @as(f64, undefined) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(f64, undefined) -// @as(f64, undefined) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(@Vector(2, f64), undefined) -// @as(f64, undefined) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), [runtime value]) -// @as(@Vector(2, f64), undefined) -// @as(f80, undefined) -// @as(f80, undefined) -// @as(@Vector(2, f80), .{ 6, undefined }) -// @as(@Vector(2, f80), .{ undefined, 6 }) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), .{ 6, undefined }) -// @as(@Vector(2, f80), .{ 6, undefined }) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), .{ undefined, 6 }) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), .{ undefined, 6 }) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(f80, undefined) -// @as(f80, undefined) -// @as(@Vector(2, f80), .{ 0, undefined }) -// @as(@Vector(2, f80), .{ undefined, 0 }) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), .{ 0, undefined }) -// @as(@Vector(2, f80), .{ 0, undefined }) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), .{ undefined, 0 }) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), .{ undefined, 0 }) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(f80, undefined) -// @as(f80, undefined) -// @as(@Vector(2, f80), .{ 9, undefined }) -// @as(@Vector(2, f80), .{ undefined, 9 }) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), .{ 9, undefined }) -// @as(@Vector(2, f80), .{ 9, undefined }) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), .{ undefined, 9 }) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), .{ undefined, 9 }) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(f80, undefined) -// @as(@Vector(2, f80), .{ -3, undefined }) -// @as(@Vector(2, f80), .{ undefined, -3 }) -// @as(@Vector(2, f80), undefined) -// @as(f80, undefined) -// @as(f80, undefined) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(f80, undefined) -// @as(f80, undefined) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(f80, undefined) -// @as(f80, undefined) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(@Vector(2, f80), undefined) -// @as(f80, undefined) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), [runtime value]) -// @as(@Vector(2, f80), undefined) +// @as(i500, undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), .{ 6, undefined }) +// @as(@Vector(2, i500), .{ undefined, 6 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ 6, undefined }) +// @as(@Vector(2, i500), .{ 6, undefined }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ undefined, 6 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ undefined, 6 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), .{ 6, undefined }) +// @as(@Vector(2, i500), .{ undefined, 6 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ 6, undefined }) +// @as(@Vector(2, i500), .{ 6, undefined }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ undefined, 6 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ undefined, 6 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), .{ 0, undefined }) +// @as(@Vector(2, i500), .{ undefined, 0 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ 0, undefined }) +// @as(@Vector(2, i500), .{ 0, undefined }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ undefined, 0 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ undefined, 0 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), .{ 0, undefined }) +// @as(@Vector(2, i500), .{ undefined, 0 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ 0, undefined }) +// @as(@Vector(2, i500), .{ 0, undefined }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ undefined, 0 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ undefined, 0 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), .{ 9, undefined }) +// @as(@Vector(2, i500), .{ undefined, 9 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ 9, undefined }) +// @as(@Vector(2, i500), .{ 9, undefined }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ undefined, 9 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ undefined, 9 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), .{ 9, undefined }) +// @as(@Vector(2, i500), .{ undefined, 9 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ 9, undefined }) +// @as(@Vector(2, i500), .{ 9, undefined }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ undefined, 9 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ undefined, 9 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), .{ 0, undefined }) +// @as(@Vector(2, i500), .{ undefined, 0 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ 0, undefined }) +// @as(@Vector(2, i500), .{ 0, undefined }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ undefined, 0 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), .{ undefined, 0 }) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, [runtime value]) +// @as(i500, [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), [runtime value]) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), undefined) +// @as(@Vector(2, i500), undefined) +// @as(i500, undefined) +// @as(@Vector(2, i500), undefined) +// @as(u500, undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), .{ 6, undefined }) +// @as(@Vector(2, u500), .{ undefined, 6 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ 6, undefined }) +// @as(@Vector(2, u500), .{ 6, undefined }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ undefined, 6 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ undefined, 6 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), .{ 6, undefined }) +// @as(@Vector(2, u500), .{ undefined, 6 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ 6, undefined }) +// @as(@Vector(2, u500), .{ 6, undefined }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ undefined, 6 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ undefined, 6 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), .{ 0, undefined }) +// @as(@Vector(2, u500), .{ undefined, 0 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ 0, undefined }) +// @as(@Vector(2, u500), .{ 0, undefined }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ undefined, 0 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ undefined, 0 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), .{ 0, undefined }) +// @as(@Vector(2, u500), .{ undefined, 0 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ 0, undefined }) +// @as(@Vector(2, u500), .{ 0, undefined }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ undefined, 0 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ undefined, 0 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), .{ 9, undefined }) +// @as(@Vector(2, u500), .{ undefined, 9 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ 9, undefined }) +// @as(@Vector(2, u500), .{ 9, undefined }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ undefined, 9 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ undefined, 9 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), .{ 9, undefined }) +// @as(@Vector(2, u500), .{ undefined, 9 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ 9, undefined }) +// @as(@Vector(2, u500), .{ 9, undefined }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ undefined, 9 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ undefined, 9 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), .{ 24, undefined }) +// @as(@Vector(2, u500), .{ undefined, 24 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ 24, undefined }) +// @as(@Vector(2, u500), .{ 24, undefined }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ undefined, 24 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ undefined, 24 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), .{ 0, undefined }) +// @as(@Vector(2, u500), .{ undefined, 0 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ 0, undefined }) +// @as(@Vector(2, u500), .{ 0, undefined }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ undefined, 0 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), .{ undefined, 0 }) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(u1, undefined) +// @as(@Vector(2, u1), .{ 1, undefined }) +// @as(@Vector(2, u1), .{ undefined, 1 }) +// @as(@Vector(2, u1), undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, [runtime value]) +// @as(u500, [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), [runtime value]) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), undefined) +// @as(u500, undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), undefined) +// @as(@Vector(2, u500), undefined) +// @as(u1, undefined) +// @as(@Vector(2, u1), [runtime value]) +// @as(@Vector(2, u1), [runtime value]) +// @as(@Vector(2, u1), undefined) +// @as(u500, undefined) +// @as(@Vector(2, u500), undefined) +// @as(i32, undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), .{ 6, undefined }) +// @as(@Vector(2, i32), .{ undefined, 6 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ 6, undefined }) +// @as(@Vector(2, i32), .{ 6, undefined }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ undefined, 6 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ undefined, 6 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), .{ 6, undefined }) +// @as(@Vector(2, i32), .{ undefined, 6 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ 6, undefined }) +// @as(@Vector(2, i32), .{ 6, undefined }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ undefined, 6 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ undefined, 6 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), .{ 0, undefined }) +// @as(@Vector(2, i32), .{ undefined, 0 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ 0, undefined }) +// @as(@Vector(2, i32), .{ 0, undefined }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ undefined, 0 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ undefined, 0 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), .{ 0, undefined }) +// @as(@Vector(2, i32), .{ undefined, 0 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ 0, undefined }) +// @as(@Vector(2, i32), .{ 0, undefined }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ undefined, 0 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ undefined, 0 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), .{ 9, undefined }) +// @as(@Vector(2, i32), .{ undefined, 9 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ 9, undefined }) +// @as(@Vector(2, i32), .{ 9, undefined }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ undefined, 9 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ undefined, 9 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), .{ 9, undefined }) +// @as(@Vector(2, i32), .{ undefined, 9 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ 9, undefined }) +// @as(@Vector(2, i32), .{ 9, undefined }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ undefined, 9 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ undefined, 9 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), .{ 0, undefined }) +// @as(@Vector(2, i32), .{ undefined, 0 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ 0, undefined }) +// @as(@Vector(2, i32), .{ 0, undefined }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ undefined, 0 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), .{ undefined, 0 }) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, [runtime value]) +// @as(i32, [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), [runtime value]) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), undefined) +// @as(@Vector(2, i32), undefined) +// @as(i32, undefined) +// @as(@Vector(2, i32), undefined) +// @as(u32, undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), .{ 6, undefined }) +// @as(@Vector(2, u32), .{ undefined, 6 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ 6, undefined }) +// @as(@Vector(2, u32), .{ 6, undefined }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ undefined, 6 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ undefined, 6 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), .{ 6, undefined }) +// @as(@Vector(2, u32), .{ undefined, 6 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ 6, undefined }) +// @as(@Vector(2, u32), .{ 6, undefined }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ undefined, 6 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ undefined, 6 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), .{ 0, undefined }) +// @as(@Vector(2, u32), .{ undefined, 0 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ 0, undefined }) +// @as(@Vector(2, u32), .{ 0, undefined }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ undefined, 0 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ undefined, 0 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), .{ 0, undefined }) +// @as(@Vector(2, u32), .{ undefined, 0 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ 0, undefined }) +// @as(@Vector(2, u32), .{ 0, undefined }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ undefined, 0 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ undefined, 0 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), .{ 9, undefined }) +// @as(@Vector(2, u32), .{ undefined, 9 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ 9, undefined }) +// @as(@Vector(2, u32), .{ 9, undefined }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ undefined, 9 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ undefined, 9 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), .{ 9, undefined }) +// @as(@Vector(2, u32), .{ undefined, 9 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ 9, undefined }) +// @as(@Vector(2, u32), .{ 9, undefined }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ undefined, 9 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ undefined, 9 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), .{ 24, undefined }) +// @as(@Vector(2, u32), .{ undefined, 24 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ 24, undefined }) +// @as(@Vector(2, u32), .{ 24, undefined }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ undefined, 24 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ undefined, 24 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), .{ 0, undefined }) +// @as(@Vector(2, u32), .{ undefined, 0 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ 0, undefined }) +// @as(@Vector(2, u32), .{ 0, undefined }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ undefined, 0 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), .{ undefined, 0 }) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(u1, undefined) +// @as(@Vector(2, u1), .{ 1, undefined }) +// @as(@Vector(2, u1), .{ undefined, 1 }) +// @as(@Vector(2, u1), undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, [runtime value]) +// @as(u32, [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), [runtime value]) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), undefined) +// @as(u32, undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), undefined) +// @as(@Vector(2, u32), undefined) +// @as(u1, undefined) +// @as(@Vector(2, u1), [runtime value]) +// @as(@Vector(2, u1), [runtime value]) +// @as(@Vector(2, u1), undefined) +// @as(u32, undefined) +// @as(@Vector(2, u32), undefined) +// @as(i8, undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), .{ 6, undefined }) +// @as(@Vector(2, i8), .{ undefined, 6 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ 6, undefined }) +// @as(@Vector(2, i8), .{ 6, undefined }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ undefined, 6 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ undefined, 6 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), .{ 6, undefined }) +// @as(@Vector(2, i8), .{ undefined, 6 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ 6, undefined }) +// @as(@Vector(2, i8), .{ 6, undefined }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ undefined, 6 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ undefined, 6 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), .{ 0, undefined }) +// @as(@Vector(2, i8), .{ undefined, 0 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ 0, undefined }) +// @as(@Vector(2, i8), .{ 0, undefined }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ undefined, 0 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ undefined, 0 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), .{ 0, undefined }) +// @as(@Vector(2, i8), .{ undefined, 0 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ 0, undefined }) +// @as(@Vector(2, i8), .{ 0, undefined }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ undefined, 0 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ undefined, 0 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), .{ 9, undefined }) +// @as(@Vector(2, i8), .{ undefined, 9 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ 9, undefined }) +// @as(@Vector(2, i8), .{ 9, undefined }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ undefined, 9 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ undefined, 9 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), .{ 9, undefined }) +// @as(@Vector(2, i8), .{ undefined, 9 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ 9, undefined }) +// @as(@Vector(2, i8), .{ 9, undefined }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ undefined, 9 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ undefined, 9 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), .{ 0, undefined }) +// @as(@Vector(2, i8), .{ undefined, 0 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ 0, undefined }) +// @as(@Vector(2, i8), .{ 0, undefined }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ undefined, 0 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), .{ undefined, 0 }) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, [runtime value]) +// @as(i8, [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), [runtime value]) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), undefined) +// @as(@Vector(2, i8), undefined) +// @as(i8, undefined) +// @as(@Vector(2, i8), undefined) // @as(f128, undefined) // @as(f128, undefined) // @as(@Vector(2, f128), .{ 6, undefined }) @@ -2585,3 +2145,443 @@ inline fn testFloatWithValue(comptime Float: type, x: Float) void { // @as(@Vector(2, f128), [runtime value]) // @as(@Vector(2, f128), [runtime value]) // @as(@Vector(2, f128), undefined) +// @as(f80, undefined) +// @as(f80, undefined) +// @as(@Vector(2, f80), .{ 6, undefined }) +// @as(@Vector(2, f80), .{ undefined, 6 }) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), .{ 6, undefined }) +// @as(@Vector(2, f80), .{ 6, undefined }) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), .{ undefined, 6 }) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), .{ undefined, 6 }) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(f80, undefined) +// @as(f80, undefined) +// @as(@Vector(2, f80), .{ 0, undefined }) +// @as(@Vector(2, f80), .{ undefined, 0 }) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), .{ 0, undefined }) +// @as(@Vector(2, f80), .{ 0, undefined }) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), .{ undefined, 0 }) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), .{ undefined, 0 }) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(f80, undefined) +// @as(f80, undefined) +// @as(@Vector(2, f80), .{ 9, undefined }) +// @as(@Vector(2, f80), .{ undefined, 9 }) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), .{ 9, undefined }) +// @as(@Vector(2, f80), .{ 9, undefined }) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), .{ undefined, 9 }) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), .{ undefined, 9 }) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(f80, undefined) +// @as(@Vector(2, f80), .{ -3, undefined }) +// @as(@Vector(2, f80), .{ undefined, -3 }) +// @as(@Vector(2, f80), undefined) +// @as(f80, undefined) +// @as(f80, undefined) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(f80, undefined) +// @as(f80, undefined) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(f80, undefined) +// @as(f80, undefined) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(@Vector(2, f80), undefined) +// @as(f80, undefined) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), [runtime value]) +// @as(@Vector(2, f80), undefined) +// @as(f64, undefined) +// @as(f64, undefined) +// @as(@Vector(2, f64), .{ 6, undefined }) +// @as(@Vector(2, f64), .{ undefined, 6 }) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), .{ 6, undefined }) +// @as(@Vector(2, f64), .{ 6, undefined }) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), .{ undefined, 6 }) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), .{ undefined, 6 }) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(f64, undefined) +// @as(f64, undefined) +// @as(@Vector(2, f64), .{ 0, undefined }) +// @as(@Vector(2, f64), .{ undefined, 0 }) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), .{ 0, undefined }) +// @as(@Vector(2, f64), .{ 0, undefined }) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), .{ undefined, 0 }) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), .{ undefined, 0 }) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(f64, undefined) +// @as(f64, undefined) +// @as(@Vector(2, f64), .{ 9, undefined }) +// @as(@Vector(2, f64), .{ undefined, 9 }) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), .{ 9, undefined }) +// @as(@Vector(2, f64), .{ 9, undefined }) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), .{ undefined, 9 }) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), .{ undefined, 9 }) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(f64, undefined) +// @as(@Vector(2, f64), .{ -3, undefined }) +// @as(@Vector(2, f64), .{ undefined, -3 }) +// @as(@Vector(2, f64), undefined) +// @as(f64, undefined) +// @as(f64, undefined) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(f64, undefined) +// @as(f64, undefined) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(f64, undefined) +// @as(f64, undefined) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(@Vector(2, f64), undefined) +// @as(f64, undefined) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), [runtime value]) +// @as(@Vector(2, f64), undefined) +// @as(f32, undefined) +// @as(f32, undefined) +// @as(@Vector(2, f32), .{ 6, undefined }) +// @as(@Vector(2, f32), .{ undefined, 6 }) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), .{ 6, undefined }) +// @as(@Vector(2, f32), .{ 6, undefined }) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), .{ undefined, 6 }) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), .{ undefined, 6 }) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(f32, undefined) +// @as(f32, undefined) +// @as(@Vector(2, f32), .{ 0, undefined }) +// @as(@Vector(2, f32), .{ undefined, 0 }) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), .{ 0, undefined }) +// @as(@Vector(2, f32), .{ 0, undefined }) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), .{ undefined, 0 }) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), .{ undefined, 0 }) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(f32, undefined) +// @as(f32, undefined) +// @as(@Vector(2, f32), .{ 9, undefined }) +// @as(@Vector(2, f32), .{ undefined, 9 }) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), .{ 9, undefined }) +// @as(@Vector(2, f32), .{ 9, undefined }) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), .{ undefined, 9 }) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), .{ undefined, 9 }) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(f32, undefined) +// @as(@Vector(2, f32), .{ -3, undefined }) +// @as(@Vector(2, f32), .{ undefined, -3 }) +// @as(@Vector(2, f32), undefined) +// @as(f32, undefined) +// @as(f32, undefined) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(f32, undefined) +// @as(f32, undefined) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(f32, undefined) +// @as(f32, undefined) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(@Vector(2, f32), undefined) +// @as(f32, undefined) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), [runtime value]) +// @as(@Vector(2, f32), undefined) +// @as(f16, undefined) +// @as(f16, undefined) +// @as(@Vector(2, f16), .{ 6, undefined }) +// @as(@Vector(2, f16), .{ undefined, 6 }) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), .{ 6, undefined }) +// @as(@Vector(2, f16), .{ 6, undefined }) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), .{ undefined, 6 }) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), .{ undefined, 6 }) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(f16, undefined) +// @as(f16, undefined) +// @as(@Vector(2, f16), .{ 0, undefined }) +// @as(@Vector(2, f16), .{ undefined, 0 }) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), .{ 0, undefined }) +// @as(@Vector(2, f16), .{ 0, undefined }) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), .{ undefined, 0 }) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), .{ undefined, 0 }) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(f16, undefined) +// @as(f16, undefined) +// @as(@Vector(2, f16), .{ 9, undefined }) +// @as(@Vector(2, f16), .{ undefined, 9 }) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), .{ 9, undefined }) +// @as(@Vector(2, f16), .{ 9, undefined }) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), .{ undefined, 9 }) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), .{ undefined, 9 }) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(f16, undefined) +// @as(@Vector(2, f16), .{ -3, undefined }) +// @as(@Vector(2, f16), .{ undefined, -3 }) +// @as(@Vector(2, f16), undefined) +// @as(f16, undefined) +// @as(f16, undefined) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(f16, undefined) +// @as(f16, undefined) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(f16, undefined) +// @as(f16, undefined) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(@Vector(2, f16), undefined) +// @as(f16, undefined) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), [runtime value]) +// @as(@Vector(2, f16), undefined) diff --git a/test/cases/compile_errors/undef_shifts_are_illegal.zig b/test/cases/compile_errors/undef_shifts_are_illegal.zig @@ -125,27 +125,19 @@ const std = @import("std"); // :53:17: error: use of undefined value here causes illegal behavior // :53:17: error: use of undefined value here causes illegal behavior // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '1' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '1' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior +// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior +// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior @@ -155,9 +147,9 @@ const std = @import("std"); // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '1' +// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '1' +// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior @@ -167,7 +159,9 @@ const std = @import("std"); // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior +// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior +// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior @@ -177,9 +171,9 @@ const std = @import("std"); // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '1' +// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '1' +// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior @@ -189,7 +183,9 @@ const std = @import("std"); // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior +// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior +// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior @@ -199,9 +195,9 @@ const std = @import("std"); // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '1' +// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '1' +// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior @@ -211,7 +207,9 @@ const std = @import("std"); // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior +// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior +// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior @@ -221,9 +219,9 @@ const std = @import("std"); // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '1' +// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '1' +// :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior @@ -233,27 +231,29 @@ const std = @import("std"); // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '0' // :53:17: error: use of undefined value here causes illegal behavior +// :53:17: note: when computing vector element at index '1' // :53:17: error: use of undefined value here causes illegal behavior +// :53:17: note: when computing vector element at index '1' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '0' +// :53:17: note: when computing vector element at index '1' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '0' +// :53:17: note: when computing vector element at index '1' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '0' +// :53:17: note: when computing vector element at index '1' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '0' +// :53:17: note: when computing vector element at index '1' // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '1' // :53:17: error: use of undefined value here causes illegal behavior // :53:17: note: when computing vector element at index '1' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '0' +// :53:17: note: when computing vector element at index '1' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '0' +// :53:17: note: when computing vector element at index '1' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '0' +// :53:17: note: when computing vector element at index '1' // :53:17: error: use of undefined value here causes illegal behavior -// :53:17: note: when computing vector element at index '0' +// :53:17: note: when computing vector element at index '1' // :53:22: error: use of undefined value here causes illegal behavior // :53:22: note: when computing vector element at index '0' // :53:22: error: use of undefined value here causes illegal behavior @@ -281,27 +281,19 @@ const std = @import("std"); // :56:27: error: use of undefined value here causes illegal behavior // :56:27: error: use of undefined value here causes illegal behavior // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '1' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '1' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior +// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior +// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior @@ -311,9 +303,9 @@ const std = @import("std"); // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '1' +// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '1' +// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior @@ -323,7 +315,9 @@ const std = @import("std"); // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior +// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior +// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior @@ -333,9 +327,9 @@ const std = @import("std"); // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '1' +// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '1' +// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior @@ -345,7 +339,9 @@ const std = @import("std"); // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior +// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior +// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior @@ -355,9 +351,9 @@ const std = @import("std"); // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '1' +// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '1' +// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior @@ -367,7 +363,9 @@ const std = @import("std"); // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior +// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior +// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior @@ -377,9 +375,9 @@ const std = @import("std"); // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '1' +// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '1' +// :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior @@ -389,27 +387,29 @@ const std = @import("std"); // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '0' // :56:27: error: use of undefined value here causes illegal behavior +// :56:27: note: when computing vector element at index '1' // :56:27: error: use of undefined value here causes illegal behavior +// :56:27: note: when computing vector element at index '1' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '0' +// :56:27: note: when computing vector element at index '1' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '0' +// :56:27: note: when computing vector element at index '1' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '0' +// :56:27: note: when computing vector element at index '1' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '0' +// :56:27: note: when computing vector element at index '1' // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '1' // :56:27: error: use of undefined value here causes illegal behavior // :56:27: note: when computing vector element at index '1' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '0' +// :56:27: note: when computing vector element at index '1' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '0' +// :56:27: note: when computing vector element at index '1' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '0' +// :56:27: note: when computing vector element at index '1' // :56:27: error: use of undefined value here causes illegal behavior -// :56:27: note: when computing vector element at index '0' +// :56:27: note: when computing vector element at index '1' // :56:30: error: use of undefined value here causes illegal behavior // :56:30: note: when computing vector element at index '0' // :56:30: error: use of undefined value here causes illegal behavior @@ -437,27 +437,19 @@ const std = @import("std"); // :59:34: error: use of undefined value here causes illegal behavior // :59:34: error: use of undefined value here causes illegal behavior // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '1' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '1' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior +// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior +// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior @@ -467,9 +459,9 @@ const std = @import("std"); // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '1' +// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '1' +// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior @@ -479,7 +471,9 @@ const std = @import("std"); // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior +// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior +// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior @@ -489,9 +483,9 @@ const std = @import("std"); // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '1' +// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '1' +// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior @@ -501,7 +495,9 @@ const std = @import("std"); // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior +// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior +// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior @@ -511,9 +507,9 @@ const std = @import("std"); // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '1' +// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '1' +// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior @@ -523,7 +519,9 @@ const std = @import("std"); // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior +// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior +// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior @@ -533,9 +531,9 @@ const std = @import("std"); // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '1' +// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '1' +// :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior @@ -545,27 +543,29 @@ const std = @import("std"); // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '0' // :59:34: error: use of undefined value here causes illegal behavior +// :59:34: note: when computing vector element at index '1' // :59:34: error: use of undefined value here causes illegal behavior +// :59:34: note: when computing vector element at index '1' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '0' +// :59:34: note: when computing vector element at index '1' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '0' +// :59:34: note: when computing vector element at index '1' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '0' +// :59:34: note: when computing vector element at index '1' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '0' +// :59:34: note: when computing vector element at index '1' // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '1' // :59:34: error: use of undefined value here causes illegal behavior // :59:34: note: when computing vector element at index '1' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '0' +// :59:34: note: when computing vector element at index '1' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '0' +// :59:34: note: when computing vector element at index '1' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '0' +// :59:34: note: when computing vector element at index '1' // :59:34: error: use of undefined value here causes illegal behavior -// :59:34: note: when computing vector element at index '0' +// :59:34: note: when computing vector element at index '1' // :59:37: error: use of undefined value here causes illegal behavior // :59:37: note: when computing vector element at index '0' // :59:37: error: use of undefined value here causes illegal behavior @@ -593,27 +593,19 @@ const std = @import("std"); // :62:17: error: use of undefined value here causes illegal behavior // :62:17: error: use of undefined value here causes illegal behavior // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '1' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '1' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior +// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior +// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior @@ -623,9 +615,9 @@ const std = @import("std"); // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '1' +// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '1' +// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior @@ -635,7 +627,9 @@ const std = @import("std"); // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior +// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior +// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior @@ -645,9 +639,9 @@ const std = @import("std"); // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '1' +// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '1' +// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior @@ -657,7 +651,9 @@ const std = @import("std"); // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior +// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior +// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior @@ -667,9 +663,9 @@ const std = @import("std"); // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '1' +// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '1' +// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior @@ -679,7 +675,9 @@ const std = @import("std"); // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior +// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior +// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior @@ -689,9 +687,9 @@ const std = @import("std"); // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '1' +// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '1' +// :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior @@ -701,27 +699,29 @@ const std = @import("std"); // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '0' // :62:17: error: use of undefined value here causes illegal behavior +// :62:17: note: when computing vector element at index '1' // :62:17: error: use of undefined value here causes illegal behavior +// :62:17: note: when computing vector element at index '1' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '0' +// :62:17: note: when computing vector element at index '1' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '0' +// :62:17: note: when computing vector element at index '1' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '0' +// :62:17: note: when computing vector element at index '1' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '0' +// :62:17: note: when computing vector element at index '1' // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '1' // :62:17: error: use of undefined value here causes illegal behavior // :62:17: note: when computing vector element at index '1' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '0' +// :62:17: note: when computing vector element at index '1' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '0' +// :62:17: note: when computing vector element at index '1' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '0' +// :62:17: note: when computing vector element at index '1' // :62:17: error: use of undefined value here causes illegal behavior -// :62:17: note: when computing vector element at index '0' +// :62:17: note: when computing vector element at index '1' // :62:22: error: use of undefined value here causes illegal behavior // :62:22: note: when computing vector element at index '0' // :62:22: error: use of undefined value here causes illegal behavior @@ -749,27 +749,19 @@ const std = @import("std"); // :65:27: error: use of undefined value here causes illegal behavior // :65:27: error: use of undefined value here causes illegal behavior // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '1' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '1' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior +// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior +// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior @@ -779,9 +771,9 @@ const std = @import("std"); // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '1' +// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '1' +// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior @@ -791,7 +783,9 @@ const std = @import("std"); // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior +// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior +// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior @@ -801,9 +795,9 @@ const std = @import("std"); // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '1' +// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '1' +// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior @@ -813,7 +807,9 @@ const std = @import("std"); // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior +// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior +// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior @@ -823,9 +819,9 @@ const std = @import("std"); // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '1' +// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '1' +// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior @@ -835,7 +831,9 @@ const std = @import("std"); // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior +// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior +// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior @@ -845,9 +843,9 @@ const std = @import("std"); // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '1' +// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '1' +// :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior @@ -857,27 +855,29 @@ const std = @import("std"); // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '0' // :65:27: error: use of undefined value here causes illegal behavior +// :65:27: note: when computing vector element at index '1' // :65:27: error: use of undefined value here causes illegal behavior +// :65:27: note: when computing vector element at index '1' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '0' +// :65:27: note: when computing vector element at index '1' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '0' +// :65:27: note: when computing vector element at index '1' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '0' +// :65:27: note: when computing vector element at index '1' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '0' +// :65:27: note: when computing vector element at index '1' // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '1' // :65:27: error: use of undefined value here causes illegal behavior // :65:27: note: when computing vector element at index '1' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '0' +// :65:27: note: when computing vector element at index '1' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '0' +// :65:27: note: when computing vector element at index '1' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '0' +// :65:27: note: when computing vector element at index '1' // :65:27: error: use of undefined value here causes illegal behavior -// :65:27: note: when computing vector element at index '0' +// :65:27: note: when computing vector element at index '1' // :65:30: error: use of undefined value here causes illegal behavior // :65:30: note: when computing vector element at index '0' // :65:30: error: use of undefined value here causes illegal behavior @@ -909,21 +909,13 @@ const std = @import("std"); // :70:17: error: use of undefined value here causes illegal behavior // :70:17: error: use of undefined value here causes illegal behavior // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior // :70:17: error: use of undefined value here causes illegal behavior // :70:17: error: use of undefined value here causes illegal behavior @@ -931,21 +923,13 @@ const std = @import("std"); // :70:17: error: use of undefined value here causes illegal behavior // :70:17: error: use of undefined value here causes illegal behavior // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior // :70:17: error: use of undefined value here causes illegal behavior // :70:17: error: use of undefined value here causes illegal behavior @@ -953,13 +937,11 @@ const std = @import("std"); // :70:17: error: use of undefined value here causes illegal behavior // :70:17: error: use of undefined value here causes illegal behavior // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '1' +// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '1' +// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior // :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior @@ -969,19 +951,25 @@ const std = @import("std"); // :70:17: error: use of undefined value here causes illegal behavior // :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '1' +// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '1' +// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '1' +// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '1' +// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior // :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior @@ -991,11 +979,17 @@ const std = @import("std"); // :70:17: error: use of undefined value here causes illegal behavior // :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '0' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior // :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior @@ -1005,19 +999,25 @@ const std = @import("std"); // :70:17: error: use of undefined value here causes illegal behavior // :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '0' +// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '0' +// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '0' +// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '0' +// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior +// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior // :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior @@ -1027,13 +1027,13 @@ const std = @import("std"); // :70:17: error: use of undefined value here causes illegal behavior // :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '0' +// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '0' +// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '0' +// :70:17: note: when computing vector element at index '1' // :70:17: error: use of undefined value here causes illegal behavior -// :70:17: note: when computing vector element at index '0' +// :70:17: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: error: use of undefined value here causes illegal behavior // :73:27: error: use of undefined value here causes illegal behavior @@ -1041,21 +1041,13 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: error: use of undefined value here causes illegal behavior // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: error: use of undefined value here causes illegal behavior // :73:27: error: use of undefined value here causes illegal behavior @@ -1063,21 +1055,13 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: error: use of undefined value here causes illegal behavior // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: error: use of undefined value here causes illegal behavior // :73:27: error: use of undefined value here causes illegal behavior @@ -1085,13 +1069,11 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: error: use of undefined value here causes illegal behavior // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior @@ -1101,19 +1083,25 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '1' +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior @@ -1123,11 +1111,17 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '0' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior @@ -1137,19 +1131,25 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior @@ -1159,13 +1159,13 @@ const std = @import("std"); // :73:27: error: use of undefined value here causes illegal behavior // :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :73:27: error: use of undefined value here causes illegal behavior -// :73:27: note: when computing vector element at index '0' +// :73:27: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior // :76:34: error: use of undefined value here causes illegal behavior // :76:34: error: use of undefined value here causes illegal behavior @@ -1173,21 +1173,13 @@ const std = @import("std"); // :76:34: error: use of undefined value here causes illegal behavior // :76:34: error: use of undefined value here causes illegal behavior // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior // :76:34: error: use of undefined value here causes illegal behavior // :76:34: error: use of undefined value here causes illegal behavior @@ -1195,21 +1187,13 @@ const std = @import("std"); // :76:34: error: use of undefined value here causes illegal behavior // :76:34: error: use of undefined value here causes illegal behavior // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior // :76:34: error: use of undefined value here causes illegal behavior // :76:34: error: use of undefined value here causes illegal behavior @@ -1217,13 +1201,11 @@ const std = @import("std"); // :76:34: error: use of undefined value here causes illegal behavior // :76:34: error: use of undefined value here causes illegal behavior // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '1' +// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '1' +// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior // :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior @@ -1233,19 +1215,25 @@ const std = @import("std"); // :76:34: error: use of undefined value here causes illegal behavior // :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '1' +// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '1' +// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '1' +// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '1' +// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior // :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior @@ -1255,11 +1243,17 @@ const std = @import("std"); // :76:34: error: use of undefined value here causes illegal behavior // :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '0' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior // :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior @@ -1269,19 +1263,25 @@ const std = @import("std"); // :76:34: error: use of undefined value here causes illegal behavior // :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '0' +// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '0' +// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '0' +// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '0' +// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior +// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior // :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior @@ -1291,13 +1291,13 @@ const std = @import("std"); // :76:34: error: use of undefined value here causes illegal behavior // :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '0' +// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '0' +// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '0' +// :76:34: note: when computing vector element at index '1' // :76:34: error: use of undefined value here causes illegal behavior -// :76:34: note: when computing vector element at index '0' +// :76:34: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior // :79:17: error: use of undefined value here causes illegal behavior // :79:17: error: use of undefined value here causes illegal behavior @@ -1305,21 +1305,13 @@ const std = @import("std"); // :79:17: error: use of undefined value here causes illegal behavior // :79:17: error: use of undefined value here causes illegal behavior // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior // :79:17: error: use of undefined value here causes illegal behavior // :79:17: error: use of undefined value here causes illegal behavior @@ -1327,21 +1319,13 @@ const std = @import("std"); // :79:17: error: use of undefined value here causes illegal behavior // :79:17: error: use of undefined value here causes illegal behavior // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior // :79:17: error: use of undefined value here causes illegal behavior // :79:17: error: use of undefined value here causes illegal behavior @@ -1349,13 +1333,11 @@ const std = @import("std"); // :79:17: error: use of undefined value here causes illegal behavior // :79:17: error: use of undefined value here causes illegal behavior // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '1' +// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '1' +// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior // :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior @@ -1365,19 +1347,25 @@ const std = @import("std"); // :79:17: error: use of undefined value here causes illegal behavior // :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '1' +// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '1' +// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '1' +// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '1' +// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior // :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior @@ -1387,11 +1375,17 @@ const std = @import("std"); // :79:17: error: use of undefined value here causes illegal behavior // :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '0' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior // :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior @@ -1401,19 +1395,25 @@ const std = @import("std"); // :79:17: error: use of undefined value here causes illegal behavior // :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '0' +// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '0' +// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '0' +// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '0' +// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior +// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior // :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior @@ -1423,13 +1423,13 @@ const std = @import("std"); // :79:17: error: use of undefined value here causes illegal behavior // :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '0' +// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '0' +// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '0' +// :79:17: note: when computing vector element at index '1' // :79:17: error: use of undefined value here causes illegal behavior -// :79:17: note: when computing vector element at index '0' +// :79:17: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior // :82:27: error: use of undefined value here causes illegal behavior // :82:27: error: use of undefined value here causes illegal behavior @@ -1437,21 +1437,13 @@ const std = @import("std"); // :82:27: error: use of undefined value here causes illegal behavior // :82:27: error: use of undefined value here causes illegal behavior // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior // :82:27: error: use of undefined value here causes illegal behavior // :82:27: error: use of undefined value here causes illegal behavior @@ -1459,21 +1451,13 @@ const std = @import("std"); // :82:27: error: use of undefined value here causes illegal behavior // :82:27: error: use of undefined value here causes illegal behavior // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior // :82:27: error: use of undefined value here causes illegal behavior // :82:27: error: use of undefined value here causes illegal behavior @@ -1481,13 +1465,11 @@ const std = @import("std"); // :82:27: error: use of undefined value here causes illegal behavior // :82:27: error: use of undefined value here causes illegal behavior // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '1' +// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '1' +// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior // :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior @@ -1497,19 +1479,25 @@ const std = @import("std"); // :82:27: error: use of undefined value here causes illegal behavior // :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '1' +// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '1' +// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '1' +// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '1' +// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior // :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior @@ -1519,11 +1507,17 @@ const std = @import("std"); // :82:27: error: use of undefined value here causes illegal behavior // :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '0' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior // :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior @@ -1533,19 +1527,25 @@ const std = @import("std"); // :82:27: error: use of undefined value here causes illegal behavior // :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '0' +// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '0' +// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '0' +// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '0' +// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior +// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior // :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior @@ -1555,44 +1555,37 @@ const std = @import("std"); // :82:27: error: use of undefined value here causes illegal behavior // :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '0' +// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '0' +// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '0' +// :82:27: note: when computing vector element at index '1' // :82:27: error: use of undefined value here causes illegal behavior -// :82:27: note: when computing vector element at index '0' +// :82:27: note: when computing vector element at index '1' // :87:17: error: use of undefined value here causes illegal behavior // :87:17: error: use of undefined value here causes illegal behavior -// :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior -// :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior -// :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior -// :87:17: note: when computing vector element at index '1' // :87:17: error: use of undefined value here causes illegal behavior -// :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior -// :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior -// :87:17: note: when computing vector element at index '1' -// :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior +// :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior @@ -1600,7 +1593,7 @@ const std = @import("std"); // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior -// :87:17: note: when computing vector element at index '1' +// :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior @@ -1608,6 +1601,7 @@ const std = @import("std"); // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior +// :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior @@ -1615,7 +1609,7 @@ const std = @import("std"); // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior -// :87:17: note: when computing vector element at index '1' +// :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior @@ -1623,6 +1617,7 @@ const std = @import("std"); // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior +// :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior @@ -1630,7 +1625,7 @@ const std = @import("std"); // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior -// :87:17: note: when computing vector element at index '1' +// :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior @@ -1638,6 +1633,7 @@ const std = @import("std"); // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior +// :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '0' // :87:17: error: use of undefined value here causes illegal behavior @@ -1647,108 +1643,105 @@ const std = @import("std"); // :87:17: error: use of undefined value here causes illegal behavior // :87:17: note: when computing vector element at index '1' // :87:17: error: use of undefined value here causes illegal behavior -// :87:17: note: when computing vector element at index '0' +// :87:17: note: when computing vector element at index '1' // :87:17: error: use of undefined value here causes illegal behavior -// :87:17: note: when computing vector element at index '0' +// :87:17: note: when computing vector element at index '1' // :87:17: error: use of undefined value here causes illegal behavior -// :87:17: note: when computing vector element at index '0' +// :87:17: note: when computing vector element at index '1' +// :87:17: error: use of undefined value here causes illegal behavior +// :87:17: note: when computing vector element at index '1' +// :87:17: error: use of undefined value here causes illegal behavior +// :87:17: note: when computing vector element at index '1' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: error: use of undefined value here causes illegal behavior -// :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior -// :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior -// :87:22: note: when computing vector element at index '1' // :87:22: error: use of undefined value here causes illegal behavior -// :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior -// :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior +// :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior -// :87:22: note: when computing vector element at index '1' +// :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior +// :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior -// :87:22: note: when computing vector element at index '1' +// :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior +// :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior -// :87:22: note: when computing vector element at index '1' +// :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior +// :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior -// :87:22: note: when computing vector element at index '1' +// :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: note: when computing vector element at index '0' // :87:22: error: use of undefined value here causes illegal behavior +// :87:22: note: when computing vector element at index '1' // :87:22: error: use of undefined value here causes illegal behavior -// :87:22: note: when computing vector element at index '0' +// :87:22: note: when computing vector element at index '1' // :87:22: error: use of undefined value here causes illegal behavior -// :87:22: note: when computing vector element at index '0' +// :87:22: note: when computing vector element at index '1' // :87:22: error: use of undefined value here causes illegal behavior // :87:22: note: when computing vector element at index '1' // :87:22: error: use of undefined value here causes illegal behavior -// :87:22: note: when computing vector element at index '0' +// :87:22: note: when computing vector element at index '1' // :87:22: error: use of undefined value here causes illegal behavior -// :87:22: note: when computing vector element at index '0' +// :87:22: note: when computing vector element at index '1' // :90:27: error: use of undefined value here causes illegal behavior // :90:27: error: use of undefined value here causes illegal behavior -// :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior -// :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior -// :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior -// :90:27: note: when computing vector element at index '1' // :90:27: error: use of undefined value here causes illegal behavior -// :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior -// :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior -// :90:27: note: when computing vector element at index '1' -// :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior +// :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior @@ -1756,7 +1749,7 @@ const std = @import("std"); // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior -// :90:27: note: when computing vector element at index '1' +// :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior @@ -1764,6 +1757,7 @@ const std = @import("std"); // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior +// :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior @@ -1771,7 +1765,7 @@ const std = @import("std"); // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior -// :90:27: note: when computing vector element at index '1' +// :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior @@ -1779,6 +1773,7 @@ const std = @import("std"); // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior +// :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior @@ -1786,7 +1781,7 @@ const std = @import("std"); // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior -// :90:27: note: when computing vector element at index '1' +// :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior @@ -1794,6 +1789,7 @@ const std = @import("std"); // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior +// :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '0' // :90:27: error: use of undefined value here causes illegal behavior @@ -1803,108 +1799,105 @@ const std = @import("std"); // :90:27: error: use of undefined value here causes illegal behavior // :90:27: note: when computing vector element at index '1' // :90:27: error: use of undefined value here causes illegal behavior -// :90:27: note: when computing vector element at index '0' +// :90:27: note: when computing vector element at index '1' // :90:27: error: use of undefined value here causes illegal behavior -// :90:27: note: when computing vector element at index '0' +// :90:27: note: when computing vector element at index '1' // :90:27: error: use of undefined value here causes illegal behavior -// :90:27: note: when computing vector element at index '0' +// :90:27: note: when computing vector element at index '1' +// :90:27: error: use of undefined value here causes illegal behavior +// :90:27: note: when computing vector element at index '1' +// :90:27: error: use of undefined value here causes illegal behavior +// :90:27: note: when computing vector element at index '1' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: error: use of undefined value here causes illegal behavior -// :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior -// :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior -// :90:30: note: when computing vector element at index '1' // :90:30: error: use of undefined value here causes illegal behavior -// :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior -// :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior +// :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior -// :90:30: note: when computing vector element at index '1' +// :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior +// :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior -// :90:30: note: when computing vector element at index '1' +// :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior +// :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior -// :90:30: note: when computing vector element at index '1' +// :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior +// :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior -// :90:30: note: when computing vector element at index '1' +// :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: note: when computing vector element at index '0' // :90:30: error: use of undefined value here causes illegal behavior +// :90:30: note: when computing vector element at index '1' // :90:30: error: use of undefined value here causes illegal behavior -// :90:30: note: when computing vector element at index '0' +// :90:30: note: when computing vector element at index '1' // :90:30: error: use of undefined value here causes illegal behavior -// :90:30: note: when computing vector element at index '0' +// :90:30: note: when computing vector element at index '1' // :90:30: error: use of undefined value here causes illegal behavior // :90:30: note: when computing vector element at index '1' // :90:30: error: use of undefined value here causes illegal behavior -// :90:30: note: when computing vector element at index '0' +// :90:30: note: when computing vector element at index '1' // :90:30: error: use of undefined value here causes illegal behavior -// :90:30: note: when computing vector element at index '0' +// :90:30: note: when computing vector element at index '1' // :93:34: error: use of undefined value here causes illegal behavior // :93:34: error: use of undefined value here causes illegal behavior -// :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior -// :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior -// :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior -// :93:34: note: when computing vector element at index '1' // :93:34: error: use of undefined value here causes illegal behavior -// :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior -// :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior -// :93:34: note: when computing vector element at index '1' -// :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior +// :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior @@ -1912,7 +1905,7 @@ const std = @import("std"); // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior -// :93:34: note: when computing vector element at index '1' +// :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior @@ -1920,6 +1913,7 @@ const std = @import("std"); // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior +// :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior @@ -1927,7 +1921,7 @@ const std = @import("std"); // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior -// :93:34: note: when computing vector element at index '1' +// :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior @@ -1935,6 +1929,7 @@ const std = @import("std"); // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior +// :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior @@ -1942,7 +1937,7 @@ const std = @import("std"); // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior -// :93:34: note: when computing vector element at index '1' +// :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior @@ -1950,6 +1945,7 @@ const std = @import("std"); // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior +// :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '0' // :93:34: error: use of undefined value here causes illegal behavior @@ -1959,108 +1955,105 @@ const std = @import("std"); // :93:34: error: use of undefined value here causes illegal behavior // :93:34: note: when computing vector element at index '1' // :93:34: error: use of undefined value here causes illegal behavior -// :93:34: note: when computing vector element at index '0' +// :93:34: note: when computing vector element at index '1' // :93:34: error: use of undefined value here causes illegal behavior -// :93:34: note: when computing vector element at index '0' +// :93:34: note: when computing vector element at index '1' // :93:34: error: use of undefined value here causes illegal behavior -// :93:34: note: when computing vector element at index '0' +// :93:34: note: when computing vector element at index '1' +// :93:34: error: use of undefined value here causes illegal behavior +// :93:34: note: when computing vector element at index '1' +// :93:34: error: use of undefined value here causes illegal behavior +// :93:34: note: when computing vector element at index '1' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: error: use of undefined value here causes illegal behavior -// :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior -// :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior -// :93:37: note: when computing vector element at index '1' // :93:37: error: use of undefined value here causes illegal behavior -// :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior -// :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior +// :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior -// :93:37: note: when computing vector element at index '1' +// :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior +// :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior -// :93:37: note: when computing vector element at index '1' +// :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior +// :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior -// :93:37: note: when computing vector element at index '1' +// :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior +// :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior -// :93:37: note: when computing vector element at index '1' +// :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: note: when computing vector element at index '0' // :93:37: error: use of undefined value here causes illegal behavior +// :93:37: note: when computing vector element at index '1' // :93:37: error: use of undefined value here causes illegal behavior -// :93:37: note: when computing vector element at index '0' +// :93:37: note: when computing vector element at index '1' // :93:37: error: use of undefined value here causes illegal behavior -// :93:37: note: when computing vector element at index '0' +// :93:37: note: when computing vector element at index '1' // :93:37: error: use of undefined value here causes illegal behavior // :93:37: note: when computing vector element at index '1' // :93:37: error: use of undefined value here causes illegal behavior -// :93:37: note: when computing vector element at index '0' +// :93:37: note: when computing vector element at index '1' // :93:37: error: use of undefined value here causes illegal behavior -// :93:37: note: when computing vector element at index '0' +// :93:37: note: when computing vector element at index '1' // :96:17: error: use of undefined value here causes illegal behavior // :96:17: error: use of undefined value here causes illegal behavior -// :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior -// :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior -// :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior -// :96:17: note: when computing vector element at index '1' // :96:17: error: use of undefined value here causes illegal behavior -// :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior -// :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior -// :96:17: note: when computing vector element at index '1' -// :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior +// :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior @@ -2068,7 +2061,7 @@ const std = @import("std"); // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior -// :96:17: note: when computing vector element at index '1' +// :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior @@ -2076,6 +2069,7 @@ const std = @import("std"); // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior +// :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior @@ -2083,7 +2077,7 @@ const std = @import("std"); // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior -// :96:17: note: when computing vector element at index '1' +// :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior @@ -2091,6 +2085,7 @@ const std = @import("std"); // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior +// :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior @@ -2098,7 +2093,7 @@ const std = @import("std"); // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior -// :96:17: note: when computing vector element at index '1' +// :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior @@ -2106,6 +2101,7 @@ const std = @import("std"); // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior +// :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '0' // :96:17: error: use of undefined value here causes illegal behavior @@ -2115,67 +2111,65 @@ const std = @import("std"); // :96:17: error: use of undefined value here causes illegal behavior // :96:17: note: when computing vector element at index '1' // :96:17: error: use of undefined value here causes illegal behavior -// :96:17: note: when computing vector element at index '0' +// :96:17: note: when computing vector element at index '1' // :96:17: error: use of undefined value here causes illegal behavior -// :96:17: note: when computing vector element at index '0' +// :96:17: note: when computing vector element at index '1' // :96:17: error: use of undefined value here causes illegal behavior -// :96:17: note: when computing vector element at index '0' -// :96:22: error: use of undefined value here causes illegal behavior -// :96:22: error: use of undefined value here causes illegal behavior -// :96:22: note: when computing vector element at index '0' +// :96:17: note: when computing vector element at index '1' +// :96:17: error: use of undefined value here causes illegal behavior +// :96:17: note: when computing vector element at index '1' +// :96:17: error: use of undefined value here causes illegal behavior +// :96:17: note: when computing vector element at index '1' // :96:22: error: use of undefined value here causes illegal behavior -// :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior -// :96:22: note: when computing vector element at index '1' // :96:22: error: use of undefined value here causes illegal behavior -// :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior -// :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior // :96:22: error: use of undefined value here causes illegal behavior -// :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior // :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior -// :96:22: note: when computing vector element at index '1' -// :96:22: error: use of undefined value here causes illegal behavior // :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior // :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior +// :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior // :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior // :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior -// :96:22: note: when computing vector element at index '1' +// :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior // :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior // :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior +// :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior // :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior // :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior -// :96:22: note: when computing vector element at index '1' +// :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior // :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior // :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior +// :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior // :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior // :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior -// :96:22: note: when computing vector element at index '1' +// :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior // :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior // :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior +// :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior // :96:22: note: when computing vector element at index '0' // :96:22: error: use of undefined value here causes illegal behavior @@ -2183,40 +2177,39 @@ const std = @import("std"); // :96:22: error: use of undefined value here causes illegal behavior // :96:22: note: when computing vector element at index '1' // :96:22: error: use of undefined value here causes illegal behavior -// :96:22: note: when computing vector element at index '0' +// :96:22: note: when computing vector element at index '1' // :96:22: error: use of undefined value here causes illegal behavior -// :96:22: note: when computing vector element at index '0' +// :96:22: note: when computing vector element at index '1' +// :96:22: error: use of undefined value here causes illegal behavior +// :96:22: note: when computing vector element at index '1' +// :96:22: error: use of undefined value here causes illegal behavior +// :96:22: note: when computing vector element at index '1' +// :96:22: error: use of undefined value here causes illegal behavior +// :96:22: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' -// :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior @@ -2224,7 +2217,7 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior @@ -2232,6 +2225,7 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior @@ -2239,7 +2233,7 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior @@ -2247,6 +2241,7 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior @@ -2254,7 +2249,7 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '1' +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior @@ -2262,6 +2257,7 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '0' // :99:27: error: use of undefined value here causes illegal behavior @@ -2271,77 +2267,81 @@ const std = @import("std"); // :99:27: error: use of undefined value here causes illegal behavior // :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' +// :99:27: note: when computing vector element at index '1' // :99:27: error: use of undefined value here causes illegal behavior -// :99:27: note: when computing vector element at index '0' +// :99:27: note: when computing vector element at index '1' +// :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' +// :99:27: error: use of undefined value here causes illegal behavior +// :99:27: note: when computing vector element at index '1' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: error: use of undefined value here causes illegal behavior -// :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior -// :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior -// :99:30: note: when computing vector element at index '1' // :99:30: error: use of undefined value here causes illegal behavior -// :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior -// :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior +// :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior -// :99:30: note: when computing vector element at index '1' +// :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior +// :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior -// :99:30: note: when computing vector element at index '1' +// :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior +// :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior -// :99:30: note: when computing vector element at index '1' +// :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior +// :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior -// :99:30: note: when computing vector element at index '1' +// :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: note: when computing vector element at index '0' // :99:30: error: use of undefined value here causes illegal behavior +// :99:30: note: when computing vector element at index '1' // :99:30: error: use of undefined value here causes illegal behavior -// :99:30: note: when computing vector element at index '0' +// :99:30: note: when computing vector element at index '1' // :99:30: error: use of undefined value here causes illegal behavior -// :99:30: note: when computing vector element at index '0' +// :99:30: note: when computing vector element at index '1' // :99:30: error: use of undefined value here causes illegal behavior // :99:30: note: when computing vector element at index '1' // :99:30: error: use of undefined value here causes illegal behavior -// :99:30: note: when computing vector element at index '0' +// :99:30: note: when computing vector element at index '1' // :99:30: error: use of undefined value here causes illegal behavior -// :99:30: note: when computing vector element at index '0' +// :99:30: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior // :104:22: error: use of undefined value here causes illegal behavior // :104:22: error: use of undefined value here causes illegal behavior @@ -2349,21 +2349,13 @@ const std = @import("std"); // :104:22: error: use of undefined value here causes illegal behavior // :104:22: error: use of undefined value here causes illegal behavior // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior // :104:22: error: use of undefined value here causes illegal behavior // :104:22: error: use of undefined value here causes illegal behavior @@ -2371,21 +2363,13 @@ const std = @import("std"); // :104:22: error: use of undefined value here causes illegal behavior // :104:22: error: use of undefined value here causes illegal behavior // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior // :104:22: error: use of undefined value here causes illegal behavior // :104:22: error: use of undefined value here causes illegal behavior @@ -2393,13 +2377,11 @@ const std = @import("std"); // :104:22: error: use of undefined value here causes illegal behavior // :104:22: error: use of undefined value here causes illegal behavior // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '1' +// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '1' +// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior // :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior @@ -2409,19 +2391,25 @@ const std = @import("std"); // :104:22: error: use of undefined value here causes illegal behavior // :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '1' +// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '1' +// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '1' +// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '1' +// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior // :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior @@ -2431,11 +2419,17 @@ const std = @import("std"); // :104:22: error: use of undefined value here causes illegal behavior // :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '0' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior // :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior @@ -2445,19 +2439,25 @@ const std = @import("std"); // :104:22: error: use of undefined value here causes illegal behavior // :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '0' +// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '0' +// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '0' +// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '0' +// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior +// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior // :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior @@ -2467,13 +2467,13 @@ const std = @import("std"); // :104:22: error: use of undefined value here causes illegal behavior // :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '0' +// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '0' +// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '0' +// :104:22: note: when computing vector element at index '1' // :104:22: error: use of undefined value here causes illegal behavior -// :104:22: note: when computing vector element at index '0' +// :104:22: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior // :107:30: error: use of undefined value here causes illegal behavior // :107:30: error: use of undefined value here causes illegal behavior @@ -2481,21 +2481,13 @@ const std = @import("std"); // :107:30: error: use of undefined value here causes illegal behavior // :107:30: error: use of undefined value here causes illegal behavior // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior // :107:30: error: use of undefined value here causes illegal behavior // :107:30: error: use of undefined value here causes illegal behavior @@ -2503,21 +2495,13 @@ const std = @import("std"); // :107:30: error: use of undefined value here causes illegal behavior // :107:30: error: use of undefined value here causes illegal behavior // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior // :107:30: error: use of undefined value here causes illegal behavior // :107:30: error: use of undefined value here causes illegal behavior @@ -2525,13 +2509,11 @@ const std = @import("std"); // :107:30: error: use of undefined value here causes illegal behavior // :107:30: error: use of undefined value here causes illegal behavior // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '1' +// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '1' +// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior // :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior @@ -2541,19 +2523,25 @@ const std = @import("std"); // :107:30: error: use of undefined value here causes illegal behavior // :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '1' +// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '1' +// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '1' +// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '1' +// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior // :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior @@ -2563,11 +2551,17 @@ const std = @import("std"); // :107:30: error: use of undefined value here causes illegal behavior // :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '0' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior // :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior @@ -2577,19 +2571,25 @@ const std = @import("std"); // :107:30: error: use of undefined value here causes illegal behavior // :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '0' +// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '0' +// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '0' +// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '0' +// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior +// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior // :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior @@ -2599,13 +2599,13 @@ const std = @import("std"); // :107:30: error: use of undefined value here causes illegal behavior // :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '0' +// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '0' +// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '0' +// :107:30: note: when computing vector element at index '1' // :107:30: error: use of undefined value here causes illegal behavior -// :107:30: note: when computing vector element at index '0' +// :107:30: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior // :110:37: error: use of undefined value here causes illegal behavior // :110:37: error: use of undefined value here causes illegal behavior @@ -2613,21 +2613,13 @@ const std = @import("std"); // :110:37: error: use of undefined value here causes illegal behavior // :110:37: error: use of undefined value here causes illegal behavior // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior // :110:37: error: use of undefined value here causes illegal behavior // :110:37: error: use of undefined value here causes illegal behavior @@ -2635,21 +2627,13 @@ const std = @import("std"); // :110:37: error: use of undefined value here causes illegal behavior // :110:37: error: use of undefined value here causes illegal behavior // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior // :110:37: error: use of undefined value here causes illegal behavior // :110:37: error: use of undefined value here causes illegal behavior @@ -2657,13 +2641,11 @@ const std = @import("std"); // :110:37: error: use of undefined value here causes illegal behavior // :110:37: error: use of undefined value here causes illegal behavior // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '1' +// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '1' +// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior // :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior @@ -2673,19 +2655,25 @@ const std = @import("std"); // :110:37: error: use of undefined value here causes illegal behavior // :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '1' +// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '1' +// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '1' +// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '1' +// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior // :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior @@ -2695,11 +2683,17 @@ const std = @import("std"); // :110:37: error: use of undefined value here causes illegal behavior // :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '0' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior // :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior @@ -2709,19 +2703,25 @@ const std = @import("std"); // :110:37: error: use of undefined value here causes illegal behavior // :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '0' +// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '0' +// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '0' +// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '0' +// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior +// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior // :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior @@ -2731,13 +2731,13 @@ const std = @import("std"); // :110:37: error: use of undefined value here causes illegal behavior // :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '0' +// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '0' +// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '0' +// :110:37: note: when computing vector element at index '1' // :110:37: error: use of undefined value here causes illegal behavior -// :110:37: note: when computing vector element at index '0' +// :110:37: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior // :113:22: error: use of undefined value here causes illegal behavior // :113:22: error: use of undefined value here causes illegal behavior @@ -2745,21 +2745,13 @@ const std = @import("std"); // :113:22: error: use of undefined value here causes illegal behavior // :113:22: error: use of undefined value here causes illegal behavior // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior // :113:22: error: use of undefined value here causes illegal behavior // :113:22: error: use of undefined value here causes illegal behavior @@ -2767,21 +2759,13 @@ const std = @import("std"); // :113:22: error: use of undefined value here causes illegal behavior // :113:22: error: use of undefined value here causes illegal behavior // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior // :113:22: error: use of undefined value here causes illegal behavior // :113:22: error: use of undefined value here causes illegal behavior @@ -2789,13 +2773,11 @@ const std = @import("std"); // :113:22: error: use of undefined value here causes illegal behavior // :113:22: error: use of undefined value here causes illegal behavior // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '1' +// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '1' +// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior // :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior @@ -2805,19 +2787,25 @@ const std = @import("std"); // :113:22: error: use of undefined value here causes illegal behavior // :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '1' +// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '1' +// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '1' +// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '1' +// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior // :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior @@ -2827,11 +2815,17 @@ const std = @import("std"); // :113:22: error: use of undefined value here causes illegal behavior // :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '0' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior // :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior @@ -2841,19 +2835,25 @@ const std = @import("std"); // :113:22: error: use of undefined value here causes illegal behavior // :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '0' +// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '0' +// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '0' +// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '0' +// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior +// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior // :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior @@ -2863,13 +2863,13 @@ const std = @import("std"); // :113:22: error: use of undefined value here causes illegal behavior // :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '0' +// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '0' +// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '0' +// :113:22: note: when computing vector element at index '1' // :113:22: error: use of undefined value here causes illegal behavior -// :113:22: note: when computing vector element at index '0' +// :113:22: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior // :116:30: error: use of undefined value here causes illegal behavior // :116:30: error: use of undefined value here causes illegal behavior @@ -2877,21 +2877,13 @@ const std = @import("std"); // :116:30: error: use of undefined value here causes illegal behavior // :116:30: error: use of undefined value here causes illegal behavior // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior // :116:30: error: use of undefined value here causes illegal behavior // :116:30: error: use of undefined value here causes illegal behavior @@ -2899,21 +2891,13 @@ const std = @import("std"); // :116:30: error: use of undefined value here causes illegal behavior // :116:30: error: use of undefined value here causes illegal behavior // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior // :116:30: error: use of undefined value here causes illegal behavior // :116:30: error: use of undefined value here causes illegal behavior @@ -2921,13 +2905,11 @@ const std = @import("std"); // :116:30: error: use of undefined value here causes illegal behavior // :116:30: error: use of undefined value here causes illegal behavior // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '1' +// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '1' +// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior // :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior @@ -2937,19 +2919,25 @@ const std = @import("std"); // :116:30: error: use of undefined value here causes illegal behavior // :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '1' +// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '1' +// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '1' +// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '1' +// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior // :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior @@ -2959,11 +2947,17 @@ const std = @import("std"); // :116:30: error: use of undefined value here causes illegal behavior // :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '0' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior // :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior @@ -2973,19 +2967,25 @@ const std = @import("std"); // :116:30: error: use of undefined value here causes illegal behavior // :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '0' +// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '0' +// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '0' +// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '0' +// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior +// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior // :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior @@ -2995,10 +2995,10 @@ const std = @import("std"); // :116:30: error: use of undefined value here causes illegal behavior // :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '0' +// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '0' +// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '0' +// :116:30: note: when computing vector element at index '1' // :116:30: error: use of undefined value here causes illegal behavior -// :116:30: note: when computing vector element at index '0' +// :116:30: note: when computing vector element at index '1' diff --git a/test/cases/compile_errors/union_auto-enum_value_already_taken.zig b/test/cases/compile_errors/union_auto-enum_value_already_taken.zig @@ -12,5 +12,5 @@ export fn entry() void { // error // -// :6:9: error: enum tag value 60 already taken -// :4:9: note: other occurrence here +// :6:9: error: enum tag value '60' for field 'E' already taken +// :4:9: note: previous occurrence in field 'C' diff --git a/test/cases/compile_errors/union_backed_by_enum_backed_by_comptime_int.zig b/test/cases/compile_errors/union_backed_by_enum_backed_by_comptime_int.zig @@ -0,0 +1,9 @@ +const U = union(enum(comptime_int)) { a: u32 }; +comptime { + const u: U = .{ .a = 123 }; + _ = u; +} + +// error +// +// :1:22: error: expected integer tag type, found 'comptime_int' diff --git a/test/cases/compile_errors/union_depends_on_pointer_alignment.zig b/test/cases/compile_errors/union_depends_on_pointer_alignment.zig @@ -1,11 +0,0 @@ -const U = union { - next: ?*align(1) U align(128), -}; - -export fn entry() usize { - return @alignOf(U); -} - -// error -// -// :1:11: error: union layout depends on being pointer aligned diff --git a/test/cases/compile_errors/union_enum_field_missing.zig b/test/cases/compile_errors/union_enum_field_missing.zig @@ -15,6 +15,5 @@ export fn entry() usize { // error // -// :7:11: error: enum field(s) missing in union -// :4:5: note: field 'c' missing, declared here -// :1:11: note: enum declared here +// :7:11: error: enum field 'c' missing from union +// :4:5: note: enum field here diff --git a/test/cases/compile_errors/union_field_ordered_differently_than_enum.zig b/test/cases/compile_errors/union_field_ordered_differently_than_enum.zig @@ -21,7 +21,6 @@ export fn entry() usize { // error // -// :4:5: error: union field 'b' ordered differently than corresponding enum field -// :1:23: note: enum field here -// :14:5: error: union field 'b' ordered differently than corresponding enum field -// :10:5: note: enum field here +// :3:15: error: union field order does not match tag enum field order +// :5:5: note: union field 'a' is index 1 +// :1:20: note: enum field 'a' is index 0 diff --git a/test/cases/compile_errors/union_noreturn_field_initialized.zig b/test/cases/compile_errors/union_noreturn_field_initialized.zig @@ -15,8 +15,8 @@ pub export fn entry2() void { const U = union(enum) { a: noreturn, }; - var u: U = undefined; - u = .a; + const u: U = .a; + _ = u; } pub export fn entry3() void { const U = union(enum) { @@ -30,12 +30,12 @@ pub export fn entry3() void { // error // -// :11:14: error: cannot initialize 'noreturn' field of union +// :11:14: error: cannot initialize union field with uninstantiable type 'noreturn' // :4:9: note: field 'b' declared here // :2:15: note: union declared here -// :19:10: error: cannot initialize 'noreturn' field of union +// :18:19: error: cannot initialize union field with uninstantiable type 'noreturn' // :16:9: note: field 'a' declared here // :15:15: note: union declared here -// :28:13: error: runtime coercion from enum '@typeInfo(tmp.entry3.U).@"union".tag_type.?' to union 'tmp.entry3.U' which has a 'noreturn' field -// :23:9: note: 'noreturn' field here +// :28:13: error: runtime coercion from enum '@typeInfo(tmp.entry3.U).@"union".tag_type.?' to union 'tmp.entry3.U' which has non-void fields +// :23:9: note: field 'a' has uninstantiable type 'noreturn' // :22:15: note: union declared here diff --git a/test/cases/compile_errors/union_with_specified_enum_omits_field.zig b/test/cases/compile_errors/union_with_specified_enum_omits_field.zig @@ -13,6 +13,5 @@ export fn entry() usize { // error // -// :6:17: error: enum field(s) missing in union -// :4:5: note: field 'C' missing, declared here -// :1:16: note: enum declared here +// :6:17: error: enum field 'C' missing from union +// :4:5: note: enum field here diff --git a/test/cases/compile_errors/union_with_too_small_explicit_signed_tag_type.zig b/test/cases/compile_errors/union_with_too_small_explicit_signed_tag_type.zig @@ -10,5 +10,4 @@ export fn entry() void { // error // -// :1:22: error: specified integer tag type cannot represent every field -// :1:22: note: type 'i2' cannot fit values in range 0...3 +// :4:5: error: enum tag value '2' too large for type 'i2' diff --git a/test/cases/compile_errors/union_with_too_small_explicit_unsigned_tag_type.zig b/test/cases/compile_errors/union_with_too_small_explicit_unsigned_tag_type.zig @@ -11,5 +11,4 @@ export fn entry() void { // error // -// :1:22: error: specified integer tag type cannot represent every field -// :1:22: note: type 'u2' cannot fit values in range 0...4 +// :6:5: error: enum tag value '4' too large for type 'u2' diff --git a/test/cases/compile_errors/untagged_union_integer_conversion.zig b/test/cases/compile_errors/untagged_union_integer_conversion.zig @@ -1,4 +1,4 @@ -const UntaggedUnion = union {}; +const UntaggedUnion = union { a: void }; comptime { @intFromEnum(@as(UntaggedUnion, undefined)); } diff --git a/test/cases/compile_errors/variadic_arg_validation.zig b/test/cases/compile_errors/variadic_arg_validation.zig @@ -25,4 +25,4 @@ pub export fn entry3() void { // :14:24: error: cannot pass 'u48' to variadic function // :14:24: note: only integers with 0 or power of two bits are extern compatible // :18:24: error: cannot pass 'void' to variadic function -// :18:24: note: 'void' is a zero bit type; for C 'void' use 'anyopaque' +// :18:24: note: 'void' is a zero bit type diff --git a/test/cases/compile_errors/zero_width_nonexhaustive_enum.zig b/test/cases/compile_errors/zero_width_nonexhaustive_enum.zig @@ -1,17 +1,20 @@ comptime { - _ = enum(i0) { a, _ }; + const E = enum(i0) { a, _ }; + _ = @as(E, undefined); } comptime { - _ = enum(u0) { a, _ }; + const E = enum(u0) { a, _ }; + _ = @as(E, undefined); } comptime { - _ = enum(u0) { a, b, _ }; + const E = enum(u0) { a, b, _ }; + _ = @as(E, undefined); } // error // -// :2:9: error: non-exhaustive enum specifies every value -// :6:9: error: non-exhaustive enum specifies every value -// :10:23: error: enumeration value '1' too large for type 'u0' +// :2:15: error: non-exhaustive enum specifies every value +// :7:15: error: non-exhaustive enum specifies every value +// :12:29: error: enum tag value '1' too large for type 'u0' diff --git a/test/incremental/change_enum_tag_type b/test/incremental/change_enum_tag_type @@ -44,7 +44,7 @@ comptime { } const std = @import("std"); const io = std.Io.Threaded.global_single_threaded.io(); -#expect_error=main.zig:7:5: error: enumeration value '4' too large for type 'u2' +#expect_error=main.zig:7:5: error: enum tag value '4' too large for type 'u2' #update=increase tag size #file=main.zig const Tag = u3; diff --git a/test/incremental/type_dependency_loop b/test/incremental/type_dependency_loop @@ -0,0 +1,55 @@ +#target=x86_64-linux-selfhosted +#target=x86_64-windows-selfhosted +#target=x86_64-linux-cbe +#target=x86_64-windows-cbe +#target=wasm32-wasi-selfhosted +#update=initial version +#file=main.zig +pub const A = struct { b: B }; +pub const B = struct { a: A }; +pub fn main() void { + _ = @as(B, undefined); +} +#expect_error=:error: dependency loop with length 2 +#expect_error=main.zig:2:27: note: type 'main.B' depends on type 'main.A' for field declared here +#expect_error=main.zig:1:27: note: type 'main.A' depends on type 'main.B' for field declared here +#expect_error=:note: eliminate any one of these dependencies to break the loop + +#update=remove reference to dependency loop +#file=main.zig +pub const A = struct { b: B }; +pub const B = struct { a: A }; +pub fn main() void { + _ = B; +} +#expect_stdout="" + +#update=change dependency loop without fixing it +#file=main.zig +pub const A = struct { b: B }; +pub const B = struct { a: *align(@alignOf(A)) A }; +pub fn main() void { + _ = B; +} +#expect_stdout="" + +#update=reference dependency loop again +#file=main.zig +pub const A = struct { b: B }; +pub const B = struct { a: *align(@alignOf(A)) A }; +pub fn main() void { + _ = @as(B, undefined); +} +#expect_error=:error: dependency loop with length 2 +#expect_error=main.zig:2:43: note: type 'main.B' depends on type 'main.A' for alignment query here +#expect_error=main.zig:1:27: note: type 'main.A' depends on type 'main.B' for field declared here +#expect_error=:note: eliminate any one of these dependencies to break the loop + +#update=fix dependency loop +#file=main.zig +pub const A = struct { b: B }; +pub const B = struct { a: *A }; +pub fn main() void { + _ = @as(B, undefined); +} +#expect_stdout="" diff --git a/tools/incr-check.zig b/tools/incr-check.zig @@ -311,12 +311,12 @@ const Eval = struct { .error_bundle => { const result_error_bundle = try std.zig.Server.allocErrorBundle(arena, body); if (stderr.bufferedLen() > 0) { - const stderr_data = try mr.toOwnedSlice(1); if (eval.allow_stderr) { - std.log.info("error_bundle stderr:\n{s}", .{stderr_data}); + std.log.info("error_bundle stderr:\n{s}", .{stderr.buffered()}); } else { - eval.fatal("error_bundle unexpected stderr:\n{s}", .{stderr_data}); + eval.fatal("error_bundle unexpected stderr:\n{s}", .{stderr.buffered()}); } + stderr.tossBuffered(); } if (result_error_bundle.errorMessageCount() != 0) { try eval.checkErrorOutcome(update, result_error_bundle); @@ -327,18 +327,18 @@ const Eval = struct { .emit_digest => { var r: std.Io.Reader = .fixed(body); _ = r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch unreachable; + if (stderr.bufferedLen() > 0) { - const stderr_data = try mr.toOwnedSlice(1); if (eval.allow_stderr) { - std.log.info("emit_digest stderr:\n{s}", .{stderr_data}); + std.log.info("emit_digest stderr:\n{s}", .{stderr.buffered()}); } else { - eval.fatal("emit_digest unexpected stderr:\n{s}", .{stderr_data}); + eval.fatal("emit_digest unexpected stderr:\n{s}", .{stderr.buffered()}); } + stderr.tossBuffered(); } - if (eval.target.backend == .sema) { try eval.checkSuccessOutcome(update, null, prog_node); - // This message indicates the end of the update. + continue; } const digest = r.takeArray(Cache.bin_digest_len) catch unreachable; @@ -352,7 +352,6 @@ const Eval = struct { const bin_path = try Dir.path.join(arena, &.{ result_dir, bin_name }); try eval.checkSuccessOutcome(update, bin_path, prog_node); - // This message indicates the end of the update. }, else => { // Ignore other messages. @@ -370,7 +369,7 @@ const Eval = struct { } waitChild(eval.child, eval); - eval.fatal("compiler failed to send error_bundle or emit_bin_path", .{}); + eval.fatal("compiler failed to send terminating error_bundle", .{}); } fn checkErrorOutcome(eval: *Eval, update: Case.Update, error_bundle: std.zig.ErrorBundle) !void { @@ -417,29 +416,32 @@ const Eval = struct { is_note: bool, err_idx: std.zig.ErrorBundle.MessageIndex, ) Allocator.Error!void { + const io = eval.io; const err = eb.getErrorMessage(err_idx); - if (err.src_loc == .none) @panic("TODO error message with no source location"); if (err.count != 1) @panic("TODO error message with count>1"); const msg = eb.nullTerminatedString(err.msg); - const src = eb.getSourceLocation(err.src_loc); - const raw_filename = eb.nullTerminatedString(src.src_path); - - const io = eval.io; - - // We need to replace backslashes for consistency between platforms. - const filename = name: { - if (std.mem.indexOfScalar(u8, raw_filename, '\\') == null) break :name raw_filename; - const copied = try eval.arena.dupe(u8, raw_filename); - std.mem.replaceScalar(u8, copied, '\\', '/'); - break :name copied; + const matches = matches: { + if (expected.is_note != is_note) break :matches false; + if (!std.mem.eql(u8, expected.msg, msg)) break :matches false; + if (err.src_loc == .none) { + break :matches expected.src == null; + } + const expected_src = expected.src orelse break :matches false; + const src = eb.getSourceLocation(err.src_loc); + const raw_filename = eb.nullTerminatedString(src.src_path); + // We need to replace backslashes for consistency between platforms. + const filename = name: { + if (std.mem.indexOfScalar(u8, raw_filename, '\\') == null) break :name raw_filename; + const copied = try eval.arena.dupe(u8, raw_filename); + std.mem.replaceScalar(u8, copied, '\\', '/'); + break :name copied; + }; + if (!std.mem.eql(u8, expected_src.filename, filename)) break :matches false; + if (expected_src.line != src.line + 1) break :matches false; + if (expected_src.column != src.column + 1) break :matches false; + break :matches true; }; - - if (expected.is_note != is_note or - !std.mem.eql(u8, expected.filename, filename) or - expected.line != src.line + 1 or - expected.column != src.column + 1 or - !std.mem.eql(u8, expected.msg, msg)) - { + if (!matches) { eb.renderToStderr(io, .{}, .auto) catch {}; eval.fatal("compile error did not match expected error", .{}); } @@ -714,10 +716,12 @@ const Case = struct { const ExpectedError = struct { is_note: bool, - filename: []const u8, - line: u32, - column: u32, msg: []const u8, + src: ?struct { + filename: []const u8, + line: u32, + column: u32, + }, }; fn parse(arena: Allocator, io: Io, bytes: []const u8) !Case { @@ -930,16 +934,16 @@ fn parseExpectedError(str: []const u8, l: usize) Case.ExpectedError { var it = std.mem.splitScalar(u8, str, ':'); const filename = it.first(); - const line_str = it.next() orelse fatal("line {d}: incomplete error specification", .{l}); - const column_str = it.next() orelse fatal("line {d}: incomplete error specification", .{l}); + const line_str, const column_str = if (filename.len > 0) .{ + it.next() orelse fatal("line {d}: incomplete error specification", .{l}), + it.next() orelse fatal("line {d}: incomplete error specification", .{l}), + } else .{ undefined, undefined }; const error_or_note_str = std.mem.trim( u8, it.next() orelse fatal("line {d}: incomplete error specification", .{l}), " ", ); - const message = std.mem.trim(u8, it.rest(), " "); - if (filename.len == 0) fatal("line {d}: empty filename", .{l}); - if (message.len == 0) fatal("line {d}: empty error message", .{l}); + const is_note = if (std.mem.eql(u8, error_or_note_str, "error")) false else if (std.mem.eql(u8, error_or_note_str, "note")) @@ -947,18 +951,19 @@ fn parseExpectedError(str: []const u8, l: usize) Case.ExpectedError { else fatal("line {d}: expeted 'error' or 'note', found '{s}'", .{ l, error_or_note_str }); - const line = std.fmt.parseInt(u32, line_str, 10) catch - fatal("line {d}: invalid line number '{s}'", .{ l, line_str }); - - const column = std.fmt.parseInt(u32, column_str, 10) catch - fatal("line {d}: invalid column number '{s}'", .{ l, column_str }); + const message = std.mem.trim(u8, it.rest(), " "); + if (message.len == 0) fatal("line {d}: empty error message", .{l}); return .{ .is_note = is_note, - .filename = filename, - .line = line, - .column = column, .msg = message, + .src = if (filename.len == 0) null else .{ + .filename = filename, + .line = std.fmt.parseInt(u32, line_str, 10) catch + fatal("line {d}: invalid line number '{s}'", .{ l, line_str }), + .column = std.fmt.parseInt(u32, column_str, 10) catch + fatal("line {d}: invalid column number '{s}'", .{ l, column_str }), + }, }; }